11cb0ef41Sopenharmony_ciconst reifyFinish = require('../utils/reify-finish.js') 21cb0ef41Sopenharmony_ciconst runScript = require('@npmcli/run-script') 31cb0ef41Sopenharmony_ciconst fs = require('fs/promises') 41cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js') 51cb0ef41Sopenharmony_ciconst validateLockfile = require('../utils/validate-lockfile.js') 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ciconst ArboristWorkspaceCmd = require('../arborist-cmd.js') 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciclass CI extends ArboristWorkspaceCmd { 101cb0ef41Sopenharmony_ci static description = 'Clean install a project' 111cb0ef41Sopenharmony_ci static name = 'ci' 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ci // These are in the order they will show up in when running "-h" 141cb0ef41Sopenharmony_ci static params = [ 151cb0ef41Sopenharmony_ci 'install-strategy', 161cb0ef41Sopenharmony_ci 'legacy-bundling', 171cb0ef41Sopenharmony_ci 'global-style', 181cb0ef41Sopenharmony_ci 'omit', 191cb0ef41Sopenharmony_ci 'include', 201cb0ef41Sopenharmony_ci 'strict-peer-deps', 211cb0ef41Sopenharmony_ci 'foreground-scripts', 221cb0ef41Sopenharmony_ci 'ignore-scripts', 231cb0ef41Sopenharmony_ci 'audit', 241cb0ef41Sopenharmony_ci 'bin-links', 251cb0ef41Sopenharmony_ci 'fund', 261cb0ef41Sopenharmony_ci 'dry-run', 271cb0ef41Sopenharmony_ci ...super.params, 281cb0ef41Sopenharmony_ci ] 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci async exec () { 311cb0ef41Sopenharmony_ci if (this.npm.global) { 321cb0ef41Sopenharmony_ci throw Object.assign(new Error('`npm ci` does not work for global packages'), { 331cb0ef41Sopenharmony_ci code: 'ECIGLOBAL', 341cb0ef41Sopenharmony_ci }) 351cb0ef41Sopenharmony_ci } 361cb0ef41Sopenharmony_ci 371cb0ef41Sopenharmony_ci const where = this.npm.prefix 381cb0ef41Sopenharmony_ci const Arborist = require('@npmcli/arborist') 391cb0ef41Sopenharmony_ci const opts = { 401cb0ef41Sopenharmony_ci ...this.npm.flatOptions, 411cb0ef41Sopenharmony_ci packageLock: true, // npm ci should never skip lock files 421cb0ef41Sopenharmony_ci path: where, 431cb0ef41Sopenharmony_ci save: false, // npm ci should never modify the lockfile or package.json 441cb0ef41Sopenharmony_ci workspaces: this.workspaceNames, 451cb0ef41Sopenharmony_ci } 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ci const arb = new Arborist(opts) 481cb0ef41Sopenharmony_ci await arb.loadVirtual().catch(er => { 491cb0ef41Sopenharmony_ci log.verbose('loadVirtual', er.stack) 501cb0ef41Sopenharmony_ci const msg = 511cb0ef41Sopenharmony_ci 'The `npm ci` command can only install with an existing package-lock.json or\n' + 521cb0ef41Sopenharmony_ci 'npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or\n' + 531cb0ef41Sopenharmony_ci 'later to generate a package-lock.json file, then try again.' 541cb0ef41Sopenharmony_ci throw this.usageError(msg) 551cb0ef41Sopenharmony_ci }) 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci // retrieves inventory of packages from loaded virtual tree (lock file) 581cb0ef41Sopenharmony_ci const virtualInventory = new Map(arb.virtualTree.inventory) 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_ci // build ideal tree step needs to come right after retrieving the virtual 611cb0ef41Sopenharmony_ci // inventory since it's going to erase the previous ref to virtualTree 621cb0ef41Sopenharmony_ci await arb.buildIdealTree() 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci // verifies that the packages from the ideal tree will match 651cb0ef41Sopenharmony_ci // the same versions that are present in the virtual tree (lock file) 661cb0ef41Sopenharmony_ci // throws a validation error in case of mismatches 671cb0ef41Sopenharmony_ci const errors = validateLockfile(virtualInventory, arb.idealTree.inventory) 681cb0ef41Sopenharmony_ci if (errors.length) { 691cb0ef41Sopenharmony_ci throw this.usageError( 701cb0ef41Sopenharmony_ci '`npm ci` can only install packages when your package.json and ' + 711cb0ef41Sopenharmony_ci 'package-lock.json or npm-shrinkwrap.json are in sync. Please ' + 721cb0ef41Sopenharmony_ci 'update your lock file with `npm install` ' + 731cb0ef41Sopenharmony_ci 'before continuing.\n\n' + 741cb0ef41Sopenharmony_ci errors.join('\n') 751cb0ef41Sopenharmony_ci ) 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci // Only remove node_modules after we've successfully loaded the virtual 791cb0ef41Sopenharmony_ci // tree and validated the lockfile 801cb0ef41Sopenharmony_ci await this.npm.time('npm-ci:rm', async () => { 811cb0ef41Sopenharmony_ci const path = `${where}/node_modules` 821cb0ef41Sopenharmony_ci // get the list of entries so we can skip the glob for performance 831cb0ef41Sopenharmony_ci const entries = await fs.readdir(path, null).catch(er => []) 841cb0ef41Sopenharmony_ci return Promise.all(entries.map(f => fs.rm(`${path}/${f}`, { force: true, recursive: true }))) 851cb0ef41Sopenharmony_ci }) 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci await arb.reify(opts) 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ci const ignoreScripts = this.npm.config.get('ignore-scripts') 901cb0ef41Sopenharmony_ci // run the same set of scripts that `npm install` runs. 911cb0ef41Sopenharmony_ci if (!ignoreScripts) { 921cb0ef41Sopenharmony_ci const scripts = [ 931cb0ef41Sopenharmony_ci 'preinstall', 941cb0ef41Sopenharmony_ci 'install', 951cb0ef41Sopenharmony_ci 'postinstall', 961cb0ef41Sopenharmony_ci 'prepublish', // XXX should we remove this finally?? 971cb0ef41Sopenharmony_ci 'preprepare', 981cb0ef41Sopenharmony_ci 'prepare', 991cb0ef41Sopenharmony_ci 'postprepare', 1001cb0ef41Sopenharmony_ci ] 1011cb0ef41Sopenharmony_ci const scriptShell = this.npm.config.get('script-shell') || undefined 1021cb0ef41Sopenharmony_ci for (const event of scripts) { 1031cb0ef41Sopenharmony_ci await runScript({ 1041cb0ef41Sopenharmony_ci path: where, 1051cb0ef41Sopenharmony_ci args: [], 1061cb0ef41Sopenharmony_ci scriptShell, 1071cb0ef41Sopenharmony_ci stdio: 'inherit', 1081cb0ef41Sopenharmony_ci banner: !this.npm.silent, 1091cb0ef41Sopenharmony_ci event, 1101cb0ef41Sopenharmony_ci }) 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci } 1131cb0ef41Sopenharmony_ci await reifyFinish(this.npm, arb) 1141cb0ef41Sopenharmony_ci } 1151cb0ef41Sopenharmony_ci} 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_cimodule.exports = CI 118