Skip to content

Commit

Permalink
Refactor install methods to handle promises - ref #1006
Browse files Browse the repository at this point in the history
  • Loading branch information
SBoudrias committed Mar 5, 2017
1 parent 1be88e6 commit cfd2a8e
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 131 deletions.
154 changes: 78 additions & 76 deletions lib/actions/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,66 +15,61 @@ const install = module.exports;
* Combine package manager cmd line arguments and run the `install` command.
*
* During the `install` step, every command will be scheduled to run once, on the
* run loop. (So don't combine the callback with `this.async()`)
* run loop. This means you can use `Promise.then` to log information, but don't
* return it or mix it with `this.async` as it'll dead lock the process.
*
* @param {String} installer Which package manager to use
* @param {String|Array} [paths] Packages to install. Use an empty string for `npm install`
* @param {Object} [options] Options to pass to `dargs` as arguments
* @param {Function} [cb]
* @param {Object} [spawnOptions] Options to pass `child_process.spawn`. ref
* https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
* @return {Promise} Resolved on installation success, rejected otherwise
*/

install.runInstall = function (installer, paths, options, cb, spawnOptions) {
if (!cb && _.isFunction(options)) {
cb = options;
options = {};
}

options = options || {};
spawnOptions = spawnOptions || {};
cb = cb || (() => {});
paths = Array.isArray(paths) ? paths : (paths && paths.split(' ')) || [];

let args = ['install'].concat(paths).concat(dargs(options));

// Yarn uses the `add` command to specifically add a package to a project
if (installer === 'yarn' && paths.length > 0) {
args[0] = 'add';
}

// Only for npm, use a minimum cache of one day
if (installer === 'npm') {
args = args.concat(['--cache-min', 24 * 60 * 60]);
}

// Return early if we're skipping installation
if (this.options.skipInstall) {
cb();
return this;
}

this.env.runLoop.add('install', done => {
this.emit(`${installer}Install`, paths);
this.spawnCommand(installer, args, spawnOptions)
.on('error', err => {
console.log(chalk.red('Could not finish installation. \n') +
'Please install ' + installer + ' with ' +
chalk.yellow('npm install -g ' + installer) + ' and try again.'
);
cb(err);
})
.on('exit', err => {
this.emit(`${installer}Install:end`, paths);
cb(err);
done();
});
}, {
once: installer + ' ' + args.join(' '),
run: false
install.runInstall = function (installer, paths, options, spawnOptions) {
return new Promise((resolve, reject) => {
options = options || {};
spawnOptions = spawnOptions || {};
paths = Array.isArray(paths) ? paths : (paths && paths.split(' ')) || [];

let args = ['install'].concat(paths).concat(dargs(options));

// Yarn uses the `add` command to specifically add a package to a project
if (installer === 'yarn' && paths.length > 0) {
args[0] = 'add';
}

// Only for npm, use a minimum cache of one day
if (installer === 'npm') {
args = args.concat(['--cache-min', 24 * 60 * 60]);
}

// Return early if we're skipping installation
if (this.options.skipInstall) {
return resolve();
}

this.env.runLoop.add('install', done => {
this.emit(`${installer}Install`, paths);
this.spawnCommand(installer, args, spawnOptions)
.on('error', err => {
console.log(chalk.red('Could not finish installation. \n') +
'Please install ' + installer + ' with ' +
chalk.yellow('npm install -g ' + installer) + ' and try again.'
);
reject(err);
done();
})
.on('exit', () => {
this.emit(`${installer}Install:end`, paths);
resolve();
done();
});
}, {
once: installer + ' ' + args.join(' '),
run: false
});
});

return this;
};

/**
Expand All @@ -84,18 +79,15 @@ install.runInstall = function (installer, paths, options, cb, spawnOptions) {
* @example
* this.installDependencies({
* bower: true,
* npm: true,
* callback: function () {
* console.log('Everything is ready!');
* }
* });
* npm: true
* }).then(() => console.log('Everything is ready!'));
*
* @param {Object} [options]
* @param {Boolean} [options.npm=true] - whether to run `npm install`
* @param {Boolean} [options.bower=true] - whether to run `bower install`
* @param {Boolean} [options.yarn=false] - whether to run `yarn install`
* @param {Boolean} [options.skipMessage=false] - whether to log the used commands
* @param {Function} [options.callback] - call once all commands have run
* @return {Promise} Resolved once done, rejected if errors
*/
install.installDependencies = function (options) {
options = options || {};
Expand All @@ -108,30 +100,33 @@ install.installDependencies = function (options) {
'<% if (!skipInstall) { %> If this fails, try running the command yourself.<% } %>\n\n')
};

if (_.isFunction(options)) {
options = {
callback: options
};
}

if (options.npm !== false) {
msg.commands.push('npm install');
commands.push(cb => {
this.npmInstall(null, null, cb);
this.npmInstall(null, null).then(
val => cb(null, val),
cb
);
});
}

if (options.yarn === true) {
msg.commands.push('yarn install');
commands.push(cb => {
this.yarnInstall(null, null, cb);
this.yarnInstall(null, null).then(
val => cb(null, val),
cb
);
});
}

if (options.bower !== false) {
msg.commands.push('bower install');
commands.push(cb => {
this.bowerInstall(null, null, cb);
this.bowerInstall(null, null).then(
val => cb(null, val),
cb
);
});
}

Expand All @@ -146,7 +141,14 @@ install.installDependencies = function (options) {
this.log(msg.template(tplValues));
}

async.parallel(commands, options.callback || _.noop);
return new Promise((resolve, reject) => {
async.parallel(commands, (err, results) => {
if (err) {
return reject(err);
}
resolve(results);
});
});
};

/**
Expand All @@ -156,11 +158,11 @@ install.installDependencies = function (options) {
*
* @param {String|Array} [cmpnt] Components to install
* @param {Object} [options] Options to pass to `dargs` as arguments
* @param {Function} [cb]
* @param {Object} [spawnOptions] Options to pass `child_process.spawn`.
* @return {Promise} Resolved if install successful, rejected otherwise
*/
install.bowerInstall = function (cmpnt, options, cb, spawnOptions) {
return this.runInstall('bower', cmpnt, options, cb, spawnOptions);
install.bowerInstall = function (cmpnt, options, spawnOptions) {
return this.runInstall('bower', cmpnt, options, spawnOptions);
};

/**
Expand All @@ -170,11 +172,11 @@ install.bowerInstall = function (cmpnt, options, cb, spawnOptions) {
*
* @param {String|Array} [pkgs] Packages to install
* @param {Object} [options] Options to pass to `dargs` as arguments
* @param {Function} [cb]
* @param {Object} [spawnOptions] Options to pass `child_process.spawn`.
* @return {Promise} Resolved if install successful, rejected otherwise
*/
install.npmInstall = function (pkgs, options, cb, spawnOptions) {
return this.runInstall('npm', pkgs, options, cb, spawnOptions);
install.npmInstall = function (pkgs, options, spawnOptions) {
return this.runInstall('npm', pkgs, options, spawnOptions);
};

/**
Expand All @@ -184,9 +186,9 @@ install.npmInstall = function (pkgs, options, cb, spawnOptions) {
*
* @param {String|Array} [pkgs] Packages to install
* @param {Object} [options] Options to pass to `dargs` as arguments
* @param {Function} [cb]
* @param {Object} [spawnOptions] Options to pass `child_process.spawn`.
* @return {Promise} Resolved if install successful, rejected otherwise
*/
install.yarnInstall = function (pkgs, options, cb, spawnOptions) {
return this.runInstall('yarn', pkgs, options, cb, spawnOptions);
install.yarnInstall = function (pkgs, options, spawnOptions) {
return this.runInstall('yarn', pkgs, options, spawnOptions);
};
Loading

0 comments on commit cfd2a8e

Please sign in to comment.