install.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. 'use strict';
  2. const assert = require('assert');
  3. const _ = require('lodash');
  4. const dargs = require('dargs');
  5. const async = require('async');
  6. const chalk = require('chalk');
  7. /**
  8. * @mixin
  9. * @alias actions/install
  10. */
  11. const install = module.exports;
  12. /**
  13. * Combine package manager cmd line arguments and run the `install` command.
  14. *
  15. * During the `install` step, every command will be scheduled to run once, on the
  16. * run loop. This means you can use `Promise.then` to log information, but don't
  17. * return it or mix it with `this.async` as it'll dead lock the process.
  18. *
  19. * @param {String} installer Which package manager to use
  20. * @param {String|Array} [paths] Packages to install. Use an empty string for `npm install`
  21. * @param {Object} [options] Options to pass to `dargs` as arguments
  22. * @param {Object} [spawnOptions] Options to pass `child_process.spawn`. ref
  23. * https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
  24. * @return {Promise} Resolved on installation success, rejected otherwise
  25. */
  26. install.runInstall = function (installer, paths, options, spawnOptions) {
  27. return new Promise((resolve, reject) => {
  28. options = options || {};
  29. spawnOptions = spawnOptions || {};
  30. paths = Array.isArray(paths) ? paths : (paths && paths.split(' ')) || [];
  31. let args = ['install'].concat(paths).concat(dargs(options));
  32. // Yarn uses the `add` command to specifically add a package to a project
  33. if (installer === 'yarn' && paths.length > 0) {
  34. args[0] = 'add';
  35. }
  36. // Only for npm, use a minimum cache of one day
  37. if (installer === 'npm') {
  38. args = args.concat(['--cache-min', 24 * 60 * 60]);
  39. }
  40. // Return early if we're skipping installation
  41. if (this.options.skipInstall) {
  42. this.log('Skipping install command: ' + chalk.yellow(installer + ' ' + args.join(' ')));
  43. return resolve();
  44. }
  45. this.env.runLoop.add('install', done => {
  46. this.emit(`${installer}Install`, paths);
  47. this.spawnCommand(installer, args, spawnOptions)
  48. .on('error', err => {
  49. this.log(chalk.red('Could not finish installation. \n') +
  50. 'Please install ' + installer + ' with ' +
  51. chalk.yellow('npm install -g ' + installer) + ' and try again. \n' +
  52. 'If ' + installer + ' is already installed, try running the following command manually: ' + chalk.yellow(installer + ' ' + args.join(' '))
  53. );
  54. reject(err);
  55. done();
  56. })
  57. .on('exit', () => {
  58. this.emit(`${installer}Install:end`, paths);
  59. resolve();
  60. done();
  61. });
  62. }, {
  63. once: installer + ' ' + args.join(' '),
  64. run: false
  65. });
  66. });
  67. };
  68. /**
  69. * Runs `npm` and `bower`, in sequence, in the generated directory and prints a
  70. * message to let the user know.
  71. *
  72. * @example
  73. * this.installDependencies({
  74. * bower: true,
  75. * npm: true
  76. * }).then(() => console.log('Everything is ready!'));
  77. *
  78. * @example
  79. * this.installDependencies({
  80. * yarn: {force: true},
  81. * npm: false
  82. * }).then(() => console.log('Everything is ready!'));
  83. *
  84. * @param {Object} [options]
  85. * @param {Boolean|Object} [options.npm=true] - whether to run `npm install` or can be options to pass to `dargs` as arguments
  86. * @param {Boolean|Object} [options.bower=true] - whether to run `bower install` or can be options to pass to `dargs` as arguments
  87. * @param {Boolean|Object} [options.yarn=false] - whether to run `yarn install` or can be options to pass to `dargs` as arguments
  88. * @param {Boolean} [options.skipMessage=false] - whether to log the used commands
  89. * @return {Promise} Resolved once done, rejected if errors
  90. */
  91. install.installDependencies = function (options) {
  92. options = options || {};
  93. const commands = [];
  94. const msg = {
  95. commands: [],
  96. template: _.template('\n\nI\'m all done. ' +
  97. '<%= skipInstall ? "Just run" : "Running" %> <%= commands %> ' +
  98. '<%= skipInstall ? "" : "for you " %>to install the required dependencies.' +
  99. '<% if (!skipInstall) { %> If this fails, try running the command yourself.<% } %>\n\n')
  100. };
  101. const getOptions = options => {
  102. return typeof options === 'object' ? options : null;
  103. };
  104. if (options.npm !== false) {
  105. msg.commands.push('npm install');
  106. commands.push(cb => {
  107. this.npmInstall(null, getOptions(options.npm)).then(
  108. val => cb(null, val),
  109. cb
  110. );
  111. });
  112. }
  113. if (options.yarn) {
  114. msg.commands.push('yarn install');
  115. commands.push(cb => {
  116. this.yarnInstall(null, getOptions(options.yarn)).then(
  117. val => cb(null, val),
  118. cb
  119. );
  120. });
  121. }
  122. if (options.bower !== false) {
  123. msg.commands.push('bower install');
  124. commands.push(cb => {
  125. this.bowerInstall(null, getOptions(options.bower)).then(
  126. val => cb(null, val),
  127. cb
  128. );
  129. });
  130. }
  131. assert(msg.commands.length, 'installDependencies needs at least one of `npm`, `bower` or `yarn` to run.');
  132. if (!options.skipMessage) {
  133. const tplValues = _.extend({
  134. skipInstall: false
  135. }, this.options, {
  136. commands: chalk.yellow.bold(msg.commands.join(' && '))
  137. });
  138. this.log(msg.template(tplValues));
  139. }
  140. return new Promise((resolve, reject) => {
  141. async.parallel(commands, (err, results) => {
  142. if (err) {
  143. return reject(err);
  144. }
  145. resolve(results);
  146. });
  147. });
  148. };
  149. /**
  150. * Receives a list of `components` and an `options` object to install through bower.
  151. *
  152. * The installation will automatically run during the run loop `install` phase.
  153. *
  154. * @param {String|Array} [cmpnt] Components to install
  155. * @param {Object} [options] Options to pass to `dargs` as arguments
  156. * @param {Object} [spawnOptions] Options to pass `child_process.spawn`.
  157. * @return {Promise} Resolved if install successful, rejected otherwise
  158. */
  159. install.bowerInstall = function (cmpnt, options, spawnOptions) {
  160. return this.runInstall('bower', cmpnt, options, spawnOptions);
  161. };
  162. /**
  163. * Receives a list of `packages` and an `options` object to install through npm.
  164. *
  165. * The installation will automatically run during the run loop `install` phase.
  166. *
  167. * @param {String|Array} [pkgs] Packages to install
  168. * @param {Object} [options] Options to pass to `dargs` as arguments
  169. * @param {Object} [spawnOptions] Options to pass `child_process.spawn`.
  170. * @return {Promise} Resolved if install successful, rejected otherwise
  171. */
  172. install.npmInstall = function (pkgs, options, spawnOptions) {
  173. return this.runInstall('npm', pkgs, options, spawnOptions);
  174. };
  175. /**
  176. * Receives a list of `packages` and an `options` object to install through yarn.
  177. *
  178. * The installation will automatically run during the run loop `install` phase.
  179. *
  180. * @param {String|Array} [pkgs] Packages to install
  181. * @param {Object} [options] Options to pass to `dargs` as arguments
  182. * @param {Object} [spawnOptions] Options to pass `child_process.spawn`.
  183. * @return {Promise} Resolved if install successful, rejected otherwise
  184. */
  185. install.yarnInstall = function (pkgs, options, spawnOptions) {
  186. return this.runInstall('yarn', pkgs, options, spawnOptions);
  187. };