123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // Base class for npm commands
- const { relative } = require('path')
- const { definitions } = require('@npmcli/config/lib/definitions')
- const getWorkspaces = require('./workspaces/get-workspaces.js')
- const { aliases: cmdAliases } = require('./utils/cmd-list')
- class BaseCommand {
- static workspaces = false
- static ignoreImplicitWorkspace = true
- // these are all overridden by individual commands
- static name = null
- static description = null
- static params = null
- // this is a static so that we can read from it without instantiating a command
- // which would require loading the config
- static get describeUsage () {
- const seenExclusive = new Set()
- const wrapWidth = 80
- const { description, usage = [''], name, params } = this
- const fullUsage = [
- `${description}`,
- '',
- 'Usage:',
- ...usage.map(u => `npm ${name} ${u}`.trim()),
- ]
- if (params) {
- let results = ''
- let line = ''
- for (const param of params) {
- /* istanbul ignore next */
- if (seenExclusive.has(param)) {
- continue
- }
- const { exclusive } = definitions[param]
- let paramUsage = `${definitions[param].usage}`
- if (exclusive) {
- const exclusiveParams = [paramUsage]
- seenExclusive.add(param)
- for (const e of exclusive) {
- seenExclusive.add(e)
- exclusiveParams.push(definitions[e].usage)
- }
- paramUsage = `${exclusiveParams.join('|')}`
- }
- paramUsage = `[${paramUsage}]`
- if (line.length + paramUsage.length > wrapWidth) {
- results = [results, line].filter(Boolean).join('\n')
- line = ''
- }
- line = [line, paramUsage].filter(Boolean).join(' ')
- }
- fullUsage.push('')
- fullUsage.push('Options:')
- fullUsage.push([results, line].filter(Boolean).join('\n'))
- }
- const aliases = Object.entries(cmdAliases).reduce((p, [k, v]) => {
- return p.concat(v === name ? k : [])
- }, [])
- if (aliases.length) {
- const plural = aliases.length === 1 ? '' : 'es'
- fullUsage.push('')
- fullUsage.push(`alias${plural}: ${aliases.join(', ')}`)
- }
- fullUsage.push('')
- fullUsage.push(`Run "npm help ${name}" for more info`)
- return fullUsage.join('\n')
- }
- constructor (npm) {
- this.npm = npm
- const { config } = this.npm
- if (!this.constructor.skipConfigValidation) {
- config.validate()
- }
- if (config.get('workspaces') === false && config.get('workspace').length) {
- throw new Error('Can not use --no-workspaces and --workspace at the same time')
- }
- }
- get name () {
- return this.constructor.name
- }
- get description () {
- return this.constructor.description
- }
- get params () {
- return this.constructor.params
- }
- get usage () {
- return this.constructor.describeUsage
- }
- usageError (prefix = '') {
- if (prefix) {
- prefix += '\n\n'
- }
- return Object.assign(new Error(`\n${prefix}${this.usage}`), {
- code: 'EUSAGE',
- })
- }
- async cmdExec (args) {
- const { config } = this.npm
- if (config.get('usage')) {
- return this.npm.output(this.usage)
- }
- const hasWsConfig = config.get('workspaces') || config.get('workspace').length
- // if cwd is a workspace, the default is set to [that workspace]
- const implicitWs = config.get('workspace', 'default').length
- // (-ws || -w foo) && (cwd is not a workspace || command is not ignoring implicit workspaces)
- if (hasWsConfig && (!implicitWs || !this.constructor.ignoreImplicitWorkspace)) {
- if (this.npm.global) {
- throw new Error('Workspaces not supported for global packages')
- }
- if (!this.constructor.workspaces) {
- throw Object.assign(new Error('This command does not support workspaces.'), {
- code: 'ENOWORKSPACES',
- })
- }
- return this.execWorkspaces(args)
- }
- return this.exec(args)
- }
- async setWorkspaces () {
- const includeWorkspaceRoot = this.isArboristCmd
- ? false
- : this.npm.config.get('include-workspace-root')
- const prefixInsideCwd = relative(this.npm.localPrefix, process.cwd()).startsWith('..')
- const relativeFrom = prefixInsideCwd ? this.npm.localPrefix : process.cwd()
- const filters = this.npm.config.get('workspace')
- const ws = await getWorkspaces(filters, {
- path: this.npm.localPrefix,
- includeWorkspaceRoot,
- relativeFrom,
- })
- this.workspaces = ws
- this.workspaceNames = [...ws.keys()]
- this.workspacePaths = [...ws.values()]
- }
- }
- module.exports = BaseCommand
|