xref: /third_party/node/deps/npm/lib/base-command.js (revision 1cb0ef41)
11cb0ef41Sopenharmony_ci// Base class for npm commands
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst { relative } = require('path')
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst { definitions } = require('@npmcli/config/lib/definitions')
61cb0ef41Sopenharmony_ciconst getWorkspaces = require('./workspaces/get-workspaces.js')
71cb0ef41Sopenharmony_ciconst { aliases: cmdAliases } = require('./utils/cmd-list')
81cb0ef41Sopenharmony_ciconst log = require('./utils/log-shim.js')
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ciclass BaseCommand {
111cb0ef41Sopenharmony_ci  static workspaces = false
121cb0ef41Sopenharmony_ci  static ignoreImplicitWorkspace = true
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci  // these are all overridden by individual commands
151cb0ef41Sopenharmony_ci  static name = null
161cb0ef41Sopenharmony_ci  static description = null
171cb0ef41Sopenharmony_ci  static params = null
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci  // this is a static so that we can read from it without instantiating a command
201cb0ef41Sopenharmony_ci  // which would require loading the config
211cb0ef41Sopenharmony_ci  static get describeUsage () {
221cb0ef41Sopenharmony_ci    const seenExclusive = new Set()
231cb0ef41Sopenharmony_ci    const wrapWidth = 80
241cb0ef41Sopenharmony_ci    const { description, usage = [''], name, params } = this
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci    const fullUsage = [
271cb0ef41Sopenharmony_ci      `${description}`,
281cb0ef41Sopenharmony_ci      '',
291cb0ef41Sopenharmony_ci      'Usage:',
301cb0ef41Sopenharmony_ci      ...usage.map(u => `npm ${name} ${u}`.trim()),
311cb0ef41Sopenharmony_ci    ]
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci    if (params) {
341cb0ef41Sopenharmony_ci      let results = ''
351cb0ef41Sopenharmony_ci      let line = ''
361cb0ef41Sopenharmony_ci      for (const param of params) {
371cb0ef41Sopenharmony_ci        /* istanbul ignore next */
381cb0ef41Sopenharmony_ci        if (seenExclusive.has(param)) {
391cb0ef41Sopenharmony_ci          continue
401cb0ef41Sopenharmony_ci        }
411cb0ef41Sopenharmony_ci        const { exclusive } = definitions[param]
421cb0ef41Sopenharmony_ci        let paramUsage = `${definitions[param].usage}`
431cb0ef41Sopenharmony_ci        if (exclusive) {
441cb0ef41Sopenharmony_ci          const exclusiveParams = [paramUsage]
451cb0ef41Sopenharmony_ci          seenExclusive.add(param)
461cb0ef41Sopenharmony_ci          for (const e of exclusive) {
471cb0ef41Sopenharmony_ci            seenExclusive.add(e)
481cb0ef41Sopenharmony_ci            exclusiveParams.push(definitions[e].usage)
491cb0ef41Sopenharmony_ci          }
501cb0ef41Sopenharmony_ci          paramUsage = `${exclusiveParams.join('|')}`
511cb0ef41Sopenharmony_ci        }
521cb0ef41Sopenharmony_ci        paramUsage = `[${paramUsage}]`
531cb0ef41Sopenharmony_ci        if (line.length + paramUsage.length > wrapWidth) {
541cb0ef41Sopenharmony_ci          results = [results, line].filter(Boolean).join('\n')
551cb0ef41Sopenharmony_ci          line = ''
561cb0ef41Sopenharmony_ci        }
571cb0ef41Sopenharmony_ci        line = [line, paramUsage].filter(Boolean).join(' ')
581cb0ef41Sopenharmony_ci      }
591cb0ef41Sopenharmony_ci      fullUsage.push('')
601cb0ef41Sopenharmony_ci      fullUsage.push('Options:')
611cb0ef41Sopenharmony_ci      fullUsage.push([results, line].filter(Boolean).join('\n'))
621cb0ef41Sopenharmony_ci    }
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci    const aliases = Object.entries(cmdAliases).reduce((p, [k, v]) => {
651cb0ef41Sopenharmony_ci      return p.concat(v === name ? k : [])
661cb0ef41Sopenharmony_ci    }, [])
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci    if (aliases.length) {
691cb0ef41Sopenharmony_ci      const plural = aliases.length === 1 ? '' : 'es'
701cb0ef41Sopenharmony_ci      fullUsage.push('')
711cb0ef41Sopenharmony_ci      fullUsage.push(`alias${plural}: ${aliases.join(', ')}`)
721cb0ef41Sopenharmony_ci    }
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci    fullUsage.push('')
751cb0ef41Sopenharmony_ci    fullUsage.push(`Run "npm help ${name}" for more info`)
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci    return fullUsage.join('\n')
781cb0ef41Sopenharmony_ci  }
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci  constructor (npm) {
811cb0ef41Sopenharmony_ci    this.npm = npm
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci    const { config } = this.npm
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci    if (!this.constructor.skipConfigValidation) {
861cb0ef41Sopenharmony_ci      config.validate()
871cb0ef41Sopenharmony_ci    }
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci    if (config.get('workspaces') === false && config.get('workspace').length) {
901cb0ef41Sopenharmony_ci      throw new Error('Can not use --no-workspaces and --workspace at the same time')
911cb0ef41Sopenharmony_ci    }
921cb0ef41Sopenharmony_ci  }
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  get name () {
951cb0ef41Sopenharmony_ci    return this.constructor.name
961cb0ef41Sopenharmony_ci  }
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  get description () {
991cb0ef41Sopenharmony_ci    return this.constructor.description
1001cb0ef41Sopenharmony_ci  }
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci  get params () {
1031cb0ef41Sopenharmony_ci    return this.constructor.params
1041cb0ef41Sopenharmony_ci  }
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci  get usage () {
1071cb0ef41Sopenharmony_ci    return this.constructor.describeUsage
1081cb0ef41Sopenharmony_ci  }
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci  usageError (prefix = '') {
1111cb0ef41Sopenharmony_ci    if (prefix) {
1121cb0ef41Sopenharmony_ci      prefix += '\n\n'
1131cb0ef41Sopenharmony_ci    }
1141cb0ef41Sopenharmony_ci    return Object.assign(new Error(`\n${prefix}${this.usage}`), {
1151cb0ef41Sopenharmony_ci      code: 'EUSAGE',
1161cb0ef41Sopenharmony_ci    })
1171cb0ef41Sopenharmony_ci  }
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  async cmdExec (args) {
1201cb0ef41Sopenharmony_ci    const { config } = this.npm
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci    if (config.get('usage')) {
1231cb0ef41Sopenharmony_ci      return this.npm.output(this.usage)
1241cb0ef41Sopenharmony_ci    }
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci    const hasWsConfig = config.get('workspaces') || config.get('workspace').length
1271cb0ef41Sopenharmony_ci    // if cwd is a workspace, the default is set to [that workspace]
1281cb0ef41Sopenharmony_ci    const implicitWs = config.get('workspace', 'default').length
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci    // (-ws || -w foo) && (cwd is not a workspace || command is not ignoring implicit workspaces)
1311cb0ef41Sopenharmony_ci    if (hasWsConfig && (!implicitWs || !this.constructor.ignoreImplicitWorkspace)) {
1321cb0ef41Sopenharmony_ci      if (this.npm.global) {
1331cb0ef41Sopenharmony_ci        throw new Error('Workspaces not supported for global packages')
1341cb0ef41Sopenharmony_ci      }
1351cb0ef41Sopenharmony_ci      if (!this.constructor.workspaces) {
1361cb0ef41Sopenharmony_ci        throw Object.assign(new Error('This command does not support workspaces.'), {
1371cb0ef41Sopenharmony_ci          code: 'ENOWORKSPACES',
1381cb0ef41Sopenharmony_ci        })
1391cb0ef41Sopenharmony_ci      }
1401cb0ef41Sopenharmony_ci      return this.execWorkspaces(args)
1411cb0ef41Sopenharmony_ci    }
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci    return this.exec(args)
1441cb0ef41Sopenharmony_ci  }
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci  // Compare the number of entries with what was expected
1471cb0ef41Sopenharmony_ci  checkExpected (entries) {
1481cb0ef41Sopenharmony_ci    if (!this.npm.config.isDefault('expect-results')) {
1491cb0ef41Sopenharmony_ci      const expected = this.npm.config.get('expect-results')
1501cb0ef41Sopenharmony_ci      if (!!entries !== !!expected) {
1511cb0ef41Sopenharmony_ci        log.warn(this.name, `Expected ${expected ? '' : 'no '}results, got ${entries}`)
1521cb0ef41Sopenharmony_ci        process.exitCode = 1
1531cb0ef41Sopenharmony_ci      }
1541cb0ef41Sopenharmony_ci    } else if (!this.npm.config.isDefault('expect-result-count')) {
1551cb0ef41Sopenharmony_ci      const expected = this.npm.config.get('expect-result-count')
1561cb0ef41Sopenharmony_ci      if (expected !== entries) {
1571cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1581cb0ef41Sopenharmony_ci        log.warn(this.name, `Expected ${expected} result${expected === 1 ? '' : 's'}, got ${entries}`)
1591cb0ef41Sopenharmony_ci        process.exitCode = 1
1601cb0ef41Sopenharmony_ci      }
1611cb0ef41Sopenharmony_ci    }
1621cb0ef41Sopenharmony_ci  }
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci  async setWorkspaces () {
1651cb0ef41Sopenharmony_ci    const includeWorkspaceRoot = this.isArboristCmd
1661cb0ef41Sopenharmony_ci      ? false
1671cb0ef41Sopenharmony_ci      : this.npm.config.get('include-workspace-root')
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci    const prefixInsideCwd = relative(this.npm.localPrefix, process.cwd()).startsWith('..')
1701cb0ef41Sopenharmony_ci    const relativeFrom = prefixInsideCwd ? this.npm.localPrefix : process.cwd()
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci    const filters = this.npm.config.get('workspace')
1731cb0ef41Sopenharmony_ci    const ws = await getWorkspaces(filters, {
1741cb0ef41Sopenharmony_ci      path: this.npm.localPrefix,
1751cb0ef41Sopenharmony_ci      includeWorkspaceRoot,
1761cb0ef41Sopenharmony_ci      relativeFrom,
1771cb0ef41Sopenharmony_ci    })
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci    this.workspaces = ws
1801cb0ef41Sopenharmony_ci    this.workspaceNames = [...ws.keys()]
1811cb0ef41Sopenharmony_ci    this.workspacePaths = [...ws.values()]
1821cb0ef41Sopenharmony_ci  }
1831cb0ef41Sopenharmony_ci}
1841cb0ef41Sopenharmony_cimodule.exports = BaseCommand
185