ci.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. const util = require('util')
  2. const Arborist = require('@npmcli/arborist')
  3. const rimraf = util.promisify(require('rimraf'))
  4. const reifyFinish = require('./utils/reify-finish.js')
  5. const runScript = require('@npmcli/run-script')
  6. const fs = require('fs')
  7. const readdir = util.promisify(fs.readdir)
  8. const log = require('npmlog')
  9. const removeNodeModules = async where => {
  10. const rimrafOpts = { glob: false }
  11. process.emit('time', 'npm-ci:rm')
  12. const path = `${where}/node_modules`
  13. // get the list of entries so we can skip the glob for performance
  14. const entries = await readdir(path, null).catch(er => [])
  15. await Promise.all(entries.map(f => rimraf(`${path}/${f}`, rimrafOpts)))
  16. process.emit('timeEnd', 'npm-ci:rm')
  17. }
  18. const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js')
  19. class CI extends ArboristWorkspaceCmd {
  20. /* istanbul ignore next - see test/lib/load-all-commands.js */
  21. static get description () {
  22. return 'Install a project with a clean slate'
  23. }
  24. /* istanbul ignore next - see test/lib/load-all-commands.js */
  25. static get name () {
  26. return 'ci'
  27. }
  28. /* istanbul ignore next - see test/lib/load-all-commands.js */
  29. static get params () {
  30. return [
  31. 'audit',
  32. 'ignore-scripts',
  33. 'script-shell',
  34. ]
  35. }
  36. exec (args, cb) {
  37. this.ci().then(() => cb()).catch(cb)
  38. }
  39. async ci () {
  40. if (this.npm.config.get('global')) {
  41. const err = new Error('`npm ci` does not work for global packages')
  42. err.code = 'ECIGLOBAL'
  43. throw err
  44. }
  45. const where = this.npm.prefix
  46. const opts = {
  47. ...this.npm.flatOptions,
  48. path: where,
  49. log: this.npm.log,
  50. save: false, // npm ci should never modify the lockfile or package.json
  51. workspaces: this.workspaceNames,
  52. }
  53. const arb = new Arborist(opts)
  54. await Promise.all([
  55. arb.loadVirtual().catch(er => {
  56. log.verbose('loadVirtual', er.stack)
  57. const msg =
  58. 'The `npm ci` command can only install with an existing package-lock.json or\n' +
  59. 'npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or\n' +
  60. 'later to generate a package-lock.json file, then try again.'
  61. throw new Error(msg)
  62. }),
  63. removeNodeModules(where),
  64. ])
  65. await arb.reify(opts)
  66. const ignoreScripts = this.npm.config.get('ignore-scripts')
  67. // run the same set of scripts that `npm install` runs.
  68. if (!ignoreScripts) {
  69. const scripts = [
  70. 'preinstall',
  71. 'install',
  72. 'postinstall',
  73. 'prepublish', // XXX should we remove this finally??
  74. 'preprepare',
  75. 'prepare',
  76. 'postprepare',
  77. ]
  78. const scriptShell = this.npm.config.get('script-shell') || undefined
  79. for (const event of scripts) {
  80. await runScript({
  81. path: where,
  82. args: [],
  83. scriptShell,
  84. stdio: 'inherit',
  85. stdioString: true,
  86. banner: log.level !== 'silent',
  87. event,
  88. })
  89. }
  90. }
  91. await reifyFinish(this.npm, arb)
  92. }
  93. }
  94. module.exports = CI