Skip to content

Commit 4b5ccfc

Browse files
authored
fix: make usage and completion static functions (#6477)
1 parent 4f39e8c commit 4b5ccfc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+452
-441
lines changed

lib/base-command.js

+53-57
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,65 @@
22

33
const { relative } = require('path')
44

5-
const ConfigDefinitions = require('./utils/config/definitions.js')
5+
const definitions = require('./utils/config/definitions.js')
66
const getWorkspaces = require('./workspaces/get-workspaces.js')
7-
8-
const cmdAliases = require('./utils/cmd-list').aliases
7+
const { aliases: cmdAliases } = require('./utils/cmd-list')
98

109
class BaseCommand {
1110
static workspaces = false
1211
static ignoreImplicitWorkspace = true
1312

13+
// these are all overridden by individual commands
14+
static name = null
15+
static description = null
16+
static params = null
17+
18+
// this is a static so that we can read from it without instantiating a command
19+
// which would require loading the config
20+
static get describeUsage () {
21+
const wrapWidth = 80
22+
const { description, usage = [''], name, params } = this
23+
24+
const fullUsage = [
25+
`${description}`,
26+
'',
27+
'Usage:',
28+
...usage.map(u => `npm ${name} ${u}`.trim()),
29+
]
30+
31+
if (params) {
32+
let results = ''
33+
let line = ''
34+
for (const param of params) {
35+
const paramUsage = `[${definitions[param].usage}]`
36+
if (line.length + paramUsage.length > wrapWidth) {
37+
results = [results, line].filter(Boolean).join('\n')
38+
line = ''
39+
}
40+
line = [line, paramUsage].filter(Boolean).join(' ')
41+
}
42+
fullUsage.push('')
43+
fullUsage.push('Options:')
44+
fullUsage.push([results, line].filter(Boolean).join('\n'))
45+
}
46+
47+
const aliases = Object.entries(cmdAliases).reduce((p, [k, v]) => {
48+
return p.concat(v === name ? k : [])
49+
}, [])
50+
51+
if (aliases.length) {
52+
const plural = aliases.length === 1 ? '' : 'es'
53+
fullUsage.push('')
54+
fullUsage.push(`alias${plural}: ${aliases.join(', ')}`)
55+
}
56+
57+
fullUsage.push('')
58+
fullUsage.push(`Run "npm help ${name}" for more info`)
59+
60+
return fullUsage.join('\n')
61+
}
62+
1463
constructor (npm) {
15-
this.wrapWidth = 80
1664
this.npm = npm
1765

1866
const { config } = this.npm
@@ -39,59 +87,7 @@ class BaseCommand {
3987
}
4088

4189
get usage () {
42-
const usage = [
43-
`${this.description}`,
44-
'',
45-
'Usage:',
46-
]
47-
48-
if (!this.constructor.usage) {
49-
usage.push(`npm ${this.name}`)
50-
} else {
51-
usage.push(...this.constructor.usage.map(u => `npm ${this.name} ${u}`))
52-
}
53-
54-
if (this.params) {
55-
usage.push('')
56-
usage.push('Options:')
57-
usage.push(this.wrappedParams)
58-
}
59-
60-
const aliases = Object.keys(cmdAliases).reduce((p, c) => {
61-
if (cmdAliases[c] === this.name) {
62-
p.push(c)
63-
}
64-
return p
65-
}, [])
66-
67-
if (aliases.length === 1) {
68-
usage.push('')
69-
usage.push(`alias: ${aliases.join(', ')}`)
70-
} else if (aliases.length > 1) {
71-
usage.push('')
72-
usage.push(`aliases: ${aliases.join(', ')}`)
73-
}
74-
75-
usage.push('')
76-
usage.push(`Run "npm help ${this.name}" for more info`)
77-
78-
return usage.join('\n')
79-
}
80-
81-
get wrappedParams () {
82-
let results = ''
83-
let line = ''
84-
85-
for (const param of this.params) {
86-
const usage = `[${ConfigDefinitions[param].usage}]`
87-
if (line.length && line.length + usage.length > this.wrapWidth) {
88-
results = [results, line].filter(Boolean).join('\n')
89-
line = ''
90-
}
91-
line = [line, usage].filter(Boolean).join(' ')
92-
}
93-
results = [results, line].filter(Boolean).join('\n')
94-
return results
90+
return this.constructor.describeUsage
9591
}
9692

9793
usageError (prefix = '') {

lib/cli-entry.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ module.exports = async (process, validateEngines) => {
5353

5454
cmd = npm.argv.shift()
5555
if (!cmd) {
56-
npm.output(await npm.usage)
56+
npm.output(npm.usage)
5757
process.exitCode = 1
5858
return exitHandler()
5959
}
@@ -63,7 +63,7 @@ module.exports = async (process, validateEngines) => {
6363
} catch (err) {
6464
if (err.code === 'EUNKNOWNCOMMAND') {
6565
const didYouMean = require('./utils/did-you-mean.js')
66-
const suggestions = await didYouMean(npm, npm.localPrefix, cmd)
66+
const suggestions = await didYouMean(npm.localPrefix, cmd)
6767
npm.output(`Unknown command: "${cmd}"${suggestions}\n`)
6868
npm.output('To see a list of supported npm commands, run:\n npm help')
6969
process.exitCode = 1

lib/commands/access.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Access extends BaseCommand {
4545
'revoke <scope:team> [<package>]',
4646
]
4747

48-
async completion (opts) {
48+
static async completion (opts) {
4949
const argv = opts.conf.argv.remain
5050
if (argv.length === 2) {
5151
return commands

lib/commands/audit.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ class Audit extends ArboristWorkspaceCmd {
412412

413413
static usage = ['[fix|signatures]']
414414

415-
async completion (opts) {
415+
static async completion (opts) {
416416
const argv = opts.conf.argv.remain
417417

418418
if (argv.length === 2) {

lib/commands/cache.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class Cache extends BaseCommand {
7373
'verify',
7474
]
7575

76-
async completion (opts) {
76+
static async completion (opts) {
7777
const argv = opts.conf.argv.remain
7878
if (argv.length === 2) {
7979
return ['add', 'clean', 'verify', 'ls']

lib/commands/completion.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ const fs = require('fs/promises')
3333
const nopt = require('nopt')
3434
const { resolve } = require('path')
3535

36+
const Npm = require('../npm.js')
3637
const { definitions, shorthands } = require('../utils/config/index.js')
37-
const { commands, aliases } = require('../utils/cmd-list.js')
38+
const { commands, aliases, deref } = require('../utils/cmd-list.js')
3839
const configNames = Object.keys(definitions)
3940
const shorthandNames = Object.keys(shorthands)
4041
const allConfs = configNames.concat(shorthandNames)
@@ -48,7 +49,7 @@ class Completion extends BaseCommand {
4849
static name = 'completion'
4950

5051
// completion for the completion command
51-
async completion (opts) {
52+
static async completion (opts) {
5253
if (opts.w > 2) {
5354
return
5455
}
@@ -156,10 +157,14 @@ class Completion extends BaseCommand {
156157
// at this point, if words[1] is some kind of npm command,
157158
// then complete on it.
158159
// otherwise, do nothing
159-
const impl = await this.npm.cmd(cmd)
160-
if (impl.completion) {
161-
const comps = await impl.completion(opts)
162-
return this.wrap(opts, comps)
160+
try {
161+
const { completion } = Npm.cmd(cmd)
162+
if (completion) {
163+
const comps = await completion(opts, this.npm)
164+
return this.wrap(opts, comps)
165+
}
166+
} catch {
167+
// it wasnt a valid command, so do nothing
163168
}
164169
}
165170

@@ -267,7 +272,7 @@ const cmdCompl = (opts, npm) => {
267272
return matches
268273
}
269274

270-
const derefs = new Set([...matches.map(c => npm.deref(c))])
275+
const derefs = new Set([...matches.map(c => deref(c))])
271276
if (derefs.size === 1) {
272277
return [...derefs]
273278
}

lib/commands/config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class Config extends BaseCommand {
7474

7575
static skipConfigValidation = true
7676

77-
async completion (opts) {
77+
static async completion (opts) {
7878
const argv = opts.conf.argv.remain
7979
if (argv[1] !== 'config') {
8080
argv.unshift('config')

lib/commands/deprecate.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ class Deprecate extends BaseCommand {
1717

1818
static ignoreImplicitWorkspace = false
1919

20-
async completion (opts) {
20+
static async completion (opts, npm) {
2121
if (opts.conf.argv.remain.length > 1) {
2222
return []
2323
}
2424

25-
const username = await getIdentity(this.npm, this.npm.flatOptions)
26-
const packages = await libaccess.getPackages(username, this.npm.flatOptions)
25+
const username = await getIdentity(npm, npm.flatOptions)
26+
const packages = await libaccess.getPackages(username, npm.flatOptions)
2727
return Object.keys(packages)
2828
.filter((name) =>
2929
packages[name] === 'write' &&

lib/commands/dist-tag.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class DistTag extends BaseCommand {
1919
static workspaces = true
2020
static ignoreImplicitWorkspace = false
2121

22-
async completion (opts) {
22+
static async completion (opts) {
2323
const argv = opts.conf.argv.remain
2424
if (argv.length === 2) {
2525
return ['add', 'rm', 'ls']

lib/commands/edit.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class Edit extends BaseCommand {
3838

3939
// TODO
4040
/* istanbul ignore next */
41-
async completion (opts) {
42-
return completion(this.npm, opts)
41+
static async completion (opts, npm) {
42+
return completion(npm, opts)
4343
}
4444

4545
async exec (args) {

lib/commands/explain.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ class Explain extends ArboristWorkspaceCmd {
1818

1919
// TODO
2020
/* istanbul ignore next */
21-
async completion (opts) {
21+
static async completion (opts, npm) {
2222
const completion = require('../utils/completion/installed-deep.js')
23-
return completion(this.npm, opts)
23+
return completion(npm, opts)
2424
}
2525

2626
async exec (args) {

lib/commands/explore.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class Explore extends BaseCommand {
1717

1818
// TODO
1919
/* istanbul ignore next */
20-
async completion (opts) {
21-
return completion(this.npm, opts)
20+
static async completion (opts, npm) {
21+
return completion(npm, opts)
2222
}
2323

2424
async exec (args) {

lib/commands/fund.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ class Fund extends ArboristWorkspaceCmd {
3636

3737
// TODO
3838
/* istanbul ignore next */
39-
async completion (opts) {
39+
static async completion (opts, npm) {
4040
const completion = require('../utils/completion/installed-deep.js')
41-
return completion(this.npm, opts)
41+
return completion(npm, opts)
4242
}
4343

4444
async exec (args) {

lib/commands/get.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const Npm = require('../npm.js')
12
const BaseCommand = require('../base-command.js')
23

34
class Get extends BaseCommand {
@@ -9,9 +10,9 @@ class Get extends BaseCommand {
910

1011
// TODO
1112
/* istanbul ignore next */
12-
async completion (opts) {
13-
const config = await this.npm.cmd('config')
14-
return config.completion(opts)
13+
static async completion (opts) {
14+
const Config = Npm.cmd('config')
15+
return Config.completion(opts)
1516
}
1617

1718
async exec (args) {

lib/commands/help.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const path = require('path')
33
const openUrl = require('../utils/open-url.js')
44
const { glob } = require('glob')
55
const localeCompare = require('@isaacs/string-locale-compare')('en')
6+
const { deref } = require('../utils/cmd-list.js')
67

78
const globify = pattern => pattern.split('\\').join('/')
89
const BaseCommand = require('../base-command.js')
@@ -26,11 +27,11 @@ class Help extends BaseCommand {
2627
static usage = ['<term> [<terms..>]']
2728
static params = ['viewer']
2829

29-
async completion (opts) {
30+
static async completion (opts, npm) {
3031
if (opts.conf.argv.remain.length > 2) {
3132
return []
3233
}
33-
const g = path.resolve(this.npm.npmRoot, 'man/man[0-9]/*.[0-9]')
34+
const g = path.resolve(npm.npmRoot, 'man/man[0-9]/*.[0-9]')
3435
let files = await glob(globify(g))
3536
// preserve glob@8 behavior
3637
files = files.sort((a, b) => a.localeCompare(b, 'en'))
@@ -49,7 +50,7 @@ class Help extends BaseCommand {
4950
const manSearch = /^\d+$/.test(args[0]) ? `man${args.shift()}` : 'man*'
5051

5152
if (!args.length) {
52-
return this.npm.output(await this.npm.usage)
53+
return this.npm.output(this.npm.usage)
5354
}
5455

5556
// npm help foo bar baz: search topics
@@ -58,7 +59,7 @@ class Help extends BaseCommand {
5859
}
5960

6061
// `npm help package.json`
61-
const arg = (this.npm.deref(args[0]) || args[0]).replace('.json', '-json')
62+
const arg = (deref(args[0]) || args[0]).replace('.json', '-json')
6263

6364
// find either section.n or npm-section.n
6465
const f = globify(path.resolve(this.npm.npmRoot, `man/${manSearch}/?(npm-)${arg}.[0-9]*`))

lib/commands/install.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Install extends ArboristWorkspaceCmd {
3838

3939
static usage = ['[<package-spec> ...]']
4040

41-
async completion (opts) {
41+
static async completion (opts) {
4242
const { partialWord } = opts
4343
// install can complete to a folder with a package.json, or any package.
4444
// if it has a slash, then it's gotta be a folder

lib/commands/link.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class Link extends ArboristWorkspaceCmd {
3535
...super.params,
3636
]
3737

38-
async completion (opts) {
39-
const dir = this.npm.globalDir
38+
static async completion (opts, npm) {
39+
const dir = npm.globalDir
4040
const files = await readdir(dir)
4141
return files.filter(f => !/^[._-]/.test(f))
4242
}

lib/commands/ls.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ class LS extends ArboristWorkspaceCmd {
4040

4141
// TODO
4242
/* istanbul ignore next */
43-
async completion (opts) {
43+
static async completion (opts, npm) {
4444
const completion = require('../utils/completion/installed-deep.js')
45-
return completion(this.npm, opts)
45+
return completion(npm, opts)
4646
}
4747

4848
async exec (args) {

0 commit comments

Comments
 (0)