Skip to content

Commit 8ffeb71

Browse files
committed
chore: refactor commands
This is the first phase of refactoring the internal structure of the npm commands to set us up for future changes. This iteration changes the function signature of `exec` for all the commands to be a async (no more callbacks), and also groups all the commands into their own subdirectory. It also removes the Proxy `npm.commands` object, in favor of an `npm.cmd` and `npm.exec` function that breaks up the two things that proxy was doing. Namely, getting to the attributes of a given command (`npm.cmd` now does this), and actually running the command `npm.exec` does this. PR-URL: #3959 Credit: @wraithgar Close: #3959 Reviewed-by: @lukekarrys
1 parent 85d5919 commit 8ffeb71

File tree

192 files changed

+11724
-14685
lines changed

Some content is hidden

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

192 files changed

+11724
-14685
lines changed

lib/workspaces/arborist-cmd.js renamed to lib/arborist-cmd.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// a list of workspace names and passes it on to new Arborist() to
33
// be able to run a filtered Arborist.reify() at some point.
44

5-
const BaseCommand = require('../base-command.js')
5+
const BaseCommand = require('./base-command.js')
66
class ArboristCmd extends BaseCommand {
77
get isArboristCmd () {
88
return true
@@ -17,12 +17,9 @@ class ArboristCmd extends BaseCommand {
1717
]
1818
}
1919

20-
execWorkspaces (args, filters, cb) {
21-
this.setWorkspaces(filters, true)
22-
.then(() => {
23-
this.exec(args, cb)
24-
})
25-
.catch(er => cb(er))
20+
async execWorkspaces (args, filters) {
21+
await this.setWorkspaces(filters)
22+
return this.exec(args)
2623
}
2724
}
2825

lib/base-command.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class BaseCommand {
6565
})
6666
}
6767

68-
execWorkspaces (args, filters, cb) {
68+
async execWorkspaces (args, filters) {
6969
throw Object.assign(
7070
new Error('This command does not support workspaces.'),
7171
{ code: 'ENOWORKSPACES' }

lib/cli.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ module.exports = async (process) => {
1818

1919
checkForUnsupportedNode()
2020

21-
const npm = require('../lib/npm.js')
21+
const Npm = require('../lib/npm.js')
22+
const npm = new Npm()
2223
const exitHandler = require('../lib/utils/exit-handler.js')
2324
exitHandler.setNpm(npm)
2425

@@ -38,6 +39,7 @@ module.exports = async (process) => {
3839

3940
const updateNotifier = require('../lib/utils/update-notifier.js')
4041

42+
let cmd
4143
// now actually fire up npm and run the command.
4244
// this is how to use npm programmatically:
4345
try {
@@ -55,24 +57,23 @@ module.exports = async (process) => {
5557

5658
updateNotifier(npm)
5759

58-
const cmd = npm.argv.shift()
60+
cmd = npm.argv.shift()
5961
if (!cmd) {
60-
npm.output(npm.usage)
62+
npm.output(await npm.usage)
6163
process.exitCode = 1
6264
return exitHandler()
6365
}
6466

65-
const impl = npm.commands[cmd]
66-
if (!impl) {
67+
await npm.exec(cmd, npm.argv)
68+
exitHandler()
69+
} catch (err) {
70+
if (err.code === 'EUNKNOWNCOMMAND') {
6771
const didYouMean = require('./utils/did-you-mean.js')
6872
const suggestions = await didYouMean(npm, npm.localPrefix, cmd)
6973
npm.output(`Unknown command: "${cmd}"${suggestions}\n\nTo see a list of supported npm commands, run:\n npm help`)
7074
process.exitCode = 1
7175
return exitHandler()
7276
}
73-
74-
impl(npm.argv, exitHandler)
75-
} catch (err) {
7677
return exitHandler(err)
7778
}
7879
}

lib/access.js renamed to lib/commands/access.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ const path = require('path')
33
const libaccess = require('libnpmaccess')
44
const readPackageJson = require('read-package-json-fast')
55

6-
const otplease = require('./utils/otplease.js')
7-
const getIdentity = require('./utils/get-identity.js')
8-
const BaseCommand = require('./base-command.js')
6+
const otplease = require('../utils/otplease.js')
7+
const getIdentity = require('../utils/get-identity.js')
8+
const BaseCommand = require('../base-command.js')
99

1010
const subcommands = [
1111
'public',
@@ -76,11 +76,7 @@ class Access extends BaseCommand {
7676
}
7777
}
7878

79-
exec (args, cb) {
80-
this.access(args).then(() => cb()).catch(cb)
81-
}
82-
83-
async access ([cmd, ...args]) {
79+
async exec ([cmd, ...args]) {
8480
if (!cmd)
8581
throw this.usageError('Subcommand is required.')
8682

lib/adduser.js renamed to lib/commands/adduser.js

+7-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
const log = require('npmlog')
2-
const replaceInfo = require('./utils/replace-info.js')
3-
const BaseCommand = require('./base-command.js')
2+
const replaceInfo = require('../utils/replace-info.js')
3+
const BaseCommand = require('../base-command.js')
44
const authTypes = {
5-
legacy: require('./auth/legacy.js'),
6-
oauth: require('./auth/oauth.js'),
7-
saml: require('./auth/saml.js'),
8-
sso: require('./auth/sso.js'),
5+
legacy: require('../auth/legacy.js'),
6+
oauth: require('../auth/oauth.js'),
7+
saml: require('../auth/saml.js'),
8+
sso: require('../auth/sso.js'),
99
}
1010

1111
class AddUser extends BaseCommand {
@@ -24,11 +24,7 @@ class AddUser extends BaseCommand {
2424
]
2525
}
2626

27-
exec (args, cb) {
28-
this.adduser(args).then(() => cb()).catch(cb)
29-
}
30-
31-
async adduser (args) {
27+
async exec (args) {
3228
const { scope } = this.npm.flatOptions
3329
const registry = this.getRegistry(this.npm.flatOptions)
3430
const auth = this.getAuthType(this.npm.flatOptions)

lib/audit.js renamed to lib/commands/audit.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const Arborist = require('@npmcli/arborist')
22
const auditReport = require('npm-audit-report')
3-
const reifyFinish = require('./utils/reify-finish.js')
4-
const auditError = require('./utils/audit-error.js')
5-
const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js')
3+
const reifyFinish = require('../utils/reify-finish.js')
4+
const auditError = require('../utils/audit-error.js')
5+
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
66

77
class Audit extends ArboristWorkspaceCmd {
88
/* istanbul ignore next - see test/lib/load-all-commands.js */
@@ -47,11 +47,7 @@ class Audit extends ArboristWorkspaceCmd {
4747
}
4848
}
4949

50-
exec (args, cb) {
51-
this.audit(args).then(() => cb()).catch(cb)
52-
}
53-
54-
async audit (args) {
50+
async exec (args) {
5551
const reporter = this.npm.config.get('json') ? 'json' : 'detail'
5652
const opts = {
5753
...this.npm.flatOptions,

lib/bin.js renamed to lib/commands/bin.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const envPath = require('./utils/path.js')
2-
const BaseCommand = require('./base-command.js')
1+
const envPath = require('../utils/path.js')
2+
const BaseCommand = require('../base-command.js')
33

44
class Bin extends BaseCommand {
55
static get description () {
@@ -14,11 +14,7 @@ class Bin extends BaseCommand {
1414
return ['global']
1515
}
1616

17-
exec (args, cb) {
18-
this.bin(args).then(() => cb()).catch(cb)
19-
}
20-
21-
async bin (args) {
17+
async exec (args) {
2218
const b = this.npm.bin
2319
this.npm.output(b)
2420
if (this.npm.config.get('global') && !envPath.includes(b))

lib/birthday.js renamed to lib/commands/birthday.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
const BaseCommand = require('./base-command.js')
1+
const BaseCommand = require('../base-command.js')
22

33
class Birthday extends BaseCommand {
4-
exec (args, cb) {
4+
async exec () {
55
this.npm.config.set('package', ['@npmcli/npm-birthday'])
66
this.npm.config.set('yes', true)
7-
return this.npm.commands.exec(['npm-birthday'], cb)
7+
const exec = await this.npm.cmd('exec')
8+
return exec.exec(['npm-birthday'])
89
}
910
}
1011

lib/bugs.js renamed to lib/commands/bugs.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const log = require('npmlog')
22
const pacote = require('pacote')
3-
const openUrl = require('./utils/open-url.js')
4-
const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js')
5-
const BaseCommand = require('./base-command.js')
3+
const openUrl = require('../utils/open-url.js')
4+
const hostedFromMani = require('../utils/hosted-git-info-from-manifest.js')
5+
const BaseCommand = require('../base-command.js')
66

77
class Bugs extends BaseCommand {
88
static get description () {
@@ -22,11 +22,7 @@ class Bugs extends BaseCommand {
2222
return ['browser', 'registry']
2323
}
2424

25-
exec (args, cb) {
26-
this.bugs(args).then(() => cb()).catch(cb)
27-
}
28-
29-
async bugs (args) {
25+
async exec (args) {
3026
if (!args || !args.length)
3127
args = ['.']
3228

lib/cache.js renamed to lib/commands/cache.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const pacote = require('pacote')
55
const path = require('path')
66
const rimraf = promisify(require('rimraf'))
77
const semver = require('semver')
8-
const BaseCommand = require('./base-command.js')
8+
const BaseCommand = require('../base-command.js')
99
const npa = require('npm-package-arg')
1010
const jsonParse = require('json-parse-even-better-errors')
1111
const localeCompare = require('@isaacs/string-locale-compare')('en')
@@ -104,11 +104,7 @@ class Cache extends BaseCommand {
104104
}
105105
}
106106

107-
exec (args, cb) {
108-
this.cache(args).then(() => cb()).catch(cb)
109-
}
110-
111-
async cache (args) {
107+
async exec (args) {
112108
const cmd = args.shift()
113109
switch (cmd) {
114110
case 'rm': case 'clear': case 'clean':

lib/ci.js renamed to lib/commands/ci.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const util = require('util')
22
const Arborist = require('@npmcli/arborist')
33
const rimraf = util.promisify(require('rimraf'))
4-
const reifyFinish = require('./utils/reify-finish.js')
4+
const reifyFinish = require('../utils/reify-finish.js')
55
const runScript = require('@npmcli/run-script')
66
const fs = require('fs')
77
const readdir = util.promisify(fs.readdir)
@@ -17,7 +17,7 @@ const removeNodeModules = async where => {
1717
await Promise.all(entries.map(f => rimraf(`${path}/${f}`, rimrafOpts)))
1818
process.emit('timeEnd', 'npm-ci:rm')
1919
}
20-
const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js')
20+
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
2121

2222
class CI extends ArboristWorkspaceCmd {
2323
/* istanbul ignore next - see test/lib/load-all-commands.js */
@@ -39,11 +39,7 @@ class CI extends ArboristWorkspaceCmd {
3939
]
4040
}
4141

42-
exec (args, cb) {
43-
this.ci().then(() => cb()).catch(cb)
44-
}
45-
46-
async ci () {
42+
async exec () {
4743
if (this.npm.config.get('global')) {
4844
const err = new Error('`npm ci` does not work for global packages')
4945
err.code = 'ECIGLOBAL'

lib/completion.js renamed to lib/commands/completion.js

+20-22
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@
2929
// as an array.
3030
//
3131

32-
const { definitions, shorthands } = require('./utils/config/index.js')
33-
const deref = require('./utils/deref-command.js')
34-
const { aliases, cmdList, plumbing } = require('./utils/cmd-list.js')
32+
const { definitions, shorthands } = require('../utils/config/index.js')
33+
const deref = require('../utils/deref-command.js')
34+
const { aliases, cmdList, plumbing } = require('../utils/cmd-list.js')
3535
const aliasNames = Object.keys(aliases)
3636
const fullList = cmdList.concat(aliasNames).filter(c => !plumbing.includes(c))
3737
const nopt = require('nopt')
3838
const configNames = Object.keys(definitions)
3939
const shorthandNames = Object.keys(shorthands)
4040
const allConfs = configNames.concat(shorthandNames)
41-
const isWindowsShell = require('./utils/is-windows-shell.js')
42-
const fileExists = require('./utils/file-exists.js')
41+
const isWindowsShell = require('../utils/is-windows-shell.js')
42+
const fileExists = require('../utils/file-exists.js')
4343

4444
const { promisify } = require('util')
45-
const BaseCommand = require('./base-command.js')
45+
const BaseCommand = require('../base-command.js')
4646

4747
class Completion extends BaseCommand {
4848
/* istanbul ignore next - see test/lib/load-all-commands.js */
@@ -75,11 +75,7 @@ class Completion extends BaseCommand {
7575
return out
7676
}
7777

78-
exec (args, cb) {
79-
this.compl(args).then(() => cb()).catch(cb)
80-
}
81-
82-
async compl (args) {
78+
async exec (args) {
8379
if (isWindowsShell) {
8480
const msg = 'npm completion supported only in MINGW / Git bash on Windows'
8581
throw Object.assign(new Error(msg), {
@@ -163,8 +159,8 @@ class Completion extends BaseCommand {
163159
// at this point, if words[1] is some kind of npm command,
164160
// then complete on it.
165161
// otherwise, do nothing
166-
const impl = this.npm.commands[cmd]
167-
if (impl && impl.completion) {
162+
const impl = this.npm.cmd(cmd)
163+
if (impl.completion) {
168164
const comps = await impl.completion(opts)
169165
return this.wrap(opts, comps)
170166
}
@@ -195,19 +191,11 @@ const dumpScript = async () => {
195191
const fs = require('fs')
196192
const readFile = promisify(fs.readFile)
197193
const { resolve } = require('path')
198-
const p = resolve(__dirname, 'utils/completion.sh')
194+
const p = resolve(__dirname, '..', 'utils', 'completion.sh')
199195

200196
const d = (await readFile(p, 'utf8')).replace(/^#!.*?\n/, '')
201197
await new Promise((res, rej) => {
202198
let done = false
203-
process.stdout.write(d, () => {
204-
if (done)
205-
return
206-
207-
done = true
208-
res()
209-
})
210-
211199
process.stdout.on('error', er => {
212200
if (done)
213201
return
@@ -224,11 +212,21 @@ const dumpScript = async () => {
224212
// Really, one should not be tossing away EPIPE errors, or any
225213
// errors, so casually. But, without this, `. <(npm completion)`
226214
// can never ever work on OS X.
215+
// TODO Ignoring coverage, see 'non EPIPE errors cause failures' test.
216+
/* istanbul ignore next */
227217
if (er.errno === 'EPIPE')
228218
res()
229219
else
230220
rej(er)
231221
})
222+
223+
process.stdout.write(d, () => {
224+
if (done)
225+
return
226+
227+
done = true
228+
res()
229+
})
232230
})
233231
}
234232

0 commit comments

Comments
 (0)