lib/models/installation-checker.js

'use strict';

const logger = require('heimdalljs-logger')('ember-cli:installation-checker');
const fs = require('fs-extra');
const path = require('path');
const SilentError = require('silent-error');

class InstallationChecker {
  constructor(options) {
    this.project = options.project;
  }

  /**
  * Check if npm and bower installation directories are present,
  * and raise an error message with instructions on how to proceed.
  *
  * If some of these package managers aren't being used in the project
  * we just ignore them. Their usage is considered by checking the
  * presence of your manifest files: package.json for npm and bower.json for bower.
  *
  * @method checkInstallations
  */
  checkInstallations() {
    let commands = [];

    if (this.usingNpm() && this.npmDependenciesNotPresent()) {
      logger.info('npm dependencies not installed');
      const command = this.usingYarn() ? '`yarn`' : '`npm install`';
      commands.push(command);
    }

    if (this.usingBower() && this.bowerDependenciesNotPresent()) {
      logger.info('bower dependencies not installed');
      commands.push('`bower install`');
    }
    if (commands.length) {
      let commandText = commands.join(' and ');
      throw new SilentError(`No dependencies installed. Run ${commandText} to install missing dependencies.`);
    }
  }

  hasBowerDeps() {
    return hasDependencies(readJSON(path.join(this.project.root, 'bower.json')));
  }

  usingBower() {
    return fs.existsSync(path.join(this.project.root, 'bower.json')) && this.hasBowerDeps();
  }

  bowerDependenciesNotPresent() {
    return !fs.existsSync(this.project.bowerDirectory);
  }

  hasNpmDeps() {
    return hasDependencies(readJSON(path.join(this.project.root, 'package.json')));
  }

  usingNpm() {
    return this.fileExistsInRoot('package.json') && this.hasNpmDeps();
  }

  usingYarn() {
    return this.fileExistsInRoot('yarn.lock') && this.hasNpmDeps();
  }

  fileExistsInRoot(fileName) {
    return fs.existsSync(path.join(this.project.root, fileName));
  }

  npmDependenciesNotPresent() {
    return !this.project.hasDependencies();
  }
}

module.exports = InstallationChecker;

function hasDependencies(pkg) {
  return (pkg.dependencies && pkg.dependencies.length) ||
         (pkg.devDependencies && pkg.devDependencies.length);
}

function readJSON(path) {
  try {
    return fs.readJsonSync(path);
  } catch (e) {
    throw new SilentError(`InstallationChecker: Unable to parse: ${path}`);
  }
}