Skip to content

Commit e252532

Browse files
authoredMar 29, 2023
fix: do less work looking up commands (#6283)
1 parent 9d2be4e commit e252532

File tree

10 files changed

+568
-551
lines changed

10 files changed

+568
-551
lines changed
 

‎lib/commands/completion.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ const nopt = require('nopt')
3434
const { resolve } = require('path')
3535

3636
const { definitions, shorthands } = require('../utils/config/index.js')
37-
const { aliases, commands, plumbing } = require('../utils/cmd-list.js')
38-
const aliasNames = Object.keys(aliases)
39-
const fullList = commands.concat(aliasNames).filter(c => !plumbing.includes(c))
37+
const { commands, aliases } = require('../utils/cmd-list.js')
4038
const configNames = Object.keys(definitions)
4139
const shorthandNames = Object.keys(shorthands)
4240
const allConfs = configNames.concat(shorthandNames)
@@ -263,7 +261,8 @@ const isFlag = word => {
263261
// complete against the npm commands
264262
// if they all resolve to the same thing, just return the thing it already is
265263
const cmdCompl = (opts, npm) => {
266-
const matches = fullList.filter(c => c.startsWith(opts.partialWord))
264+
const allCommands = commands.concat(Object.keys(aliases))
265+
const matches = allCommands.filter(c => c.startsWith(opts.partialWord))
267266
if (!matches.length) {
268267
return matches
269268
}
@@ -273,7 +272,7 @@ const cmdCompl = (opts, npm) => {
273272
return [...derefs]
274273
}
275274

276-
return fullList
275+
return allCommands
277276
}
278277

279278
module.exports = Completion

‎lib/npm.js

+18-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const Config = require('@npmcli/config')
55
const chalk = require('chalk')
66
const which = require('which')
77
const fs = require('fs/promises')
8+
const abbrev = require('abbrev')
89

910
// Patch the global fs module here at the app level
1011
require('graceful-fs').gracefulify(require('fs'))
@@ -18,7 +19,7 @@ const log = require('./utils/log-shim')
1819
const replaceInfo = require('./utils/replace-info.js')
1920
const updateNotifier = require('./utils/update-notifier.js')
2021
const pkg = require('../package.json')
21-
const cmdList = require('./utils/cmd-list.js')
22+
const { commands, aliases } = require('./utils/cmd-list.js')
2223

2324
class Npm extends EventEmitter {
2425
static get version () {
@@ -84,18 +85,30 @@ class Npm extends EventEmitter {
8485
if (!c) {
8586
return
8687
}
88+
89+
// Translate camelCase to snake-case (i.e. installTest to install-test)
8790
if (c.match(/[A-Z]/)) {
8891
c = c.replace(/([A-Z])/g, m => '-' + m.toLowerCase())
8992
}
90-
if (cmdList.plumbing.indexOf(c) !== -1) {
93+
94+
// if they asked for something exactly we are done
95+
if (commands.includes(c)) {
9196
return c
9297
}
98+
99+
// if they asked for a direct alias
100+
if (aliases[c]) {
101+
return aliases[c]
102+
}
103+
104+
const abbrevs = abbrev(commands.concat(Object.keys(aliases)))
105+
93106
// first deref the abbrev, if there is one
94107
// then resolve any aliases
95108
// so `npm install-cl` will resolve to `install-clean` then to `ci`
96-
let a = cmdList.abbrevs[c]
97-
while (cmdList.aliases[a]) {
98-
a = cmdList.aliases[a]
109+
let a = abbrevs[c]
110+
while (aliases[a]) {
111+
a = aliases[a]
99112
}
100113
return a
101114
}

‎lib/utils/cmd-list.js

+71-79
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,5 @@
1-
const abbrev = require('abbrev')
2-
const localeCompare = require('@isaacs/string-locale-compare')('en')
3-
4-
// plumbing should not have any aliases
5-
const aliases = {
6-
7-
// aliases
8-
author: 'owner',
9-
home: 'docs',
10-
issues: 'bugs',
11-
info: 'view',
12-
show: 'view',
13-
find: 'search',
14-
add: 'install',
15-
unlink: 'uninstall',
16-
remove: 'uninstall',
17-
rm: 'uninstall',
18-
r: 'uninstall',
19-
20-
// short names for common things
21-
un: 'uninstall',
22-
rb: 'rebuild',
23-
list: 'ls',
24-
ln: 'link',
25-
create: 'init',
26-
i: 'install',
27-
it: 'install-test',
28-
cit: 'install-ci-test',
29-
up: 'update',
30-
c: 'config',
31-
s: 'search',
32-
se: 'search',
33-
tst: 'test',
34-
t: 'test',
35-
ddp: 'dedupe',
36-
v: 'view',
37-
run: 'run-script',
38-
'clean-install': 'ci',
39-
'clean-install-test': 'install-ci-test',
40-
x: 'exec',
41-
why: 'explain',
42-
la: 'll',
43-
verison: 'version',
44-
ic: 'ci',
45-
46-
// typos
47-
innit: 'init',
48-
// manually abbrev so that install-test doesn't make insta stop working
49-
in: 'install',
50-
ins: 'install',
51-
inst: 'install',
52-
insta: 'install',
53-
instal: 'install',
54-
isnt: 'install',
55-
isnta: 'install',
56-
isntal: 'install',
57-
isntall: 'install',
58-
'install-clean': 'ci',
59-
'isntall-clean': 'ci',
60-
hlep: 'help',
61-
'dist-tags': 'dist-tag',
62-
upgrade: 'update',
63-
udpate: 'update',
64-
rum: 'run-script',
65-
sit: 'install-ci-test',
66-
urn: 'run-script',
67-
ogr: 'org',
68-
'add-user': 'adduser',
69-
}
70-
71-
// these are filenames in .
1+
// These correspond to filenames in lib/commands
2+
// Please keep this list sorted alphabetically
723
const commands = [
734
'access',
745
'adduser',
@@ -92,14 +23,15 @@ const commands = [
9223
'fund',
9324
'get',
9425
'help',
26+
'help-search',
9527
'hook',
9628
'init',
9729
'install',
9830
'install-ci-test',
9931
'install-test',
10032
'link',
10133
'll',
102-
'login', // This is an alias for `adduser` but it can be confusing
34+
'login',
10335
'logout',
10436
'ls',
10537
'org',
@@ -135,16 +67,76 @@ const commands = [
13567
'version',
13668
'view',
13769
'whoami',
138-
].sort(localeCompare)
70+
]
71+
72+
// These must resolve to an entry in commands
73+
const aliases = {
74+
75+
// aliases
76+
author: 'owner',
77+
home: 'docs',
78+
issues: 'bugs',
79+
info: 'view',
80+
show: 'view',
81+
find: 'search',
82+
add: 'install',
83+
unlink: 'uninstall',
84+
remove: 'uninstall',
85+
rm: 'uninstall',
86+
r: 'uninstall',
87+
88+
// short names for common things
89+
un: 'uninstall',
90+
rb: 'rebuild',
91+
list: 'ls',
92+
ln: 'link',
93+
create: 'init',
94+
i: 'install',
95+
it: 'install-test',
96+
cit: 'install-ci-test',
97+
up: 'update',
98+
c: 'config',
99+
s: 'search',
100+
se: 'search',
101+
tst: 'test',
102+
t: 'test',
103+
ddp: 'dedupe',
104+
v: 'view',
105+
run: 'run-script',
106+
'clean-install': 'ci',
107+
'clean-install-test': 'install-ci-test',
108+
x: 'exec',
109+
why: 'explain',
110+
la: 'll',
111+
verison: 'version',
112+
ic: 'ci',
139113

140-
const plumbing = ['help-search']
141-
const allCommands = [...commands, ...plumbing].sort(localeCompare)
142-
const abbrevs = abbrev(commands.concat(Object.keys(aliases)))
114+
// typos
115+
innit: 'init',
116+
// manually abbrev so that install-test doesn't make insta stop working
117+
in: 'install',
118+
ins: 'install',
119+
inst: 'install',
120+
insta: 'install',
121+
instal: 'install',
122+
isnt: 'install',
123+
isnta: 'install',
124+
isntal: 'install',
125+
isntall: 'install',
126+
'install-clean': 'ci',
127+
'isntall-clean': 'ci',
128+
hlep: 'help',
129+
'dist-tags': 'dist-tag',
130+
upgrade: 'update',
131+
udpate: 'update',
132+
rum: 'run-script',
133+
sit: 'install-ci-test',
134+
urn: 'run-script',
135+
ogr: 'org',
136+
'add-user': 'adduser',
137+
}
143138

144139
module.exports = {
145-
abbrevs,
146140
aliases,
147141
commands,
148-
plumbing,
149-
allCommands,
150142
}

‎smoke-tests/tap-snapshots/test/index.js.test.cjs

+7-6
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ All commands:
2424
access, adduser, audit, bugs, cache, ci, completion,
2525
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
2626
edit, exec, explain, explore, find-dupes, fund, get, help,
27-
hook, init, install, install-ci-test, install-test, link,
28-
ll, login, logout, ls, org, outdated, owner, pack, ping,
29-
pkg, prefix, profile, prune, publish, query, rebuild, repo,
30-
restart, root, run-script, search, set, shrinkwrap, star,
31-
stars, start, stop, team, test, token, uninstall, unpublish,
32-
unstar, update, version, view, whoami
27+
help-search, hook, init, install, install-ci-test,
28+
install-test, link, ll, login, logout, ls, org, outdated,
29+
owner, pack, ping, pkg, prefix, profile, prune, publish,
30+
query, rebuild, repo, restart, root, run-script, search,
31+
set, shrinkwrap, star, stars, start, stop, team, test,
32+
token, uninstall, unpublish, unstar, update, version, view,
33+
whoami
3334
3435
Specify configs in the ini-formatted file:
3536
{NPM}/{TESTDIR}/project/.npmrc

‎tap-snapshots/test/lib/commands/completion.js.test.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Array [
6666
fund
6767
get
6868
help
69+
help-search
6970
hook
7071
init
7172
install

‎tap-snapshots/test/lib/docs.js.test.cjs

+1-420
Large diffs are not rendered by default.

‎tap-snapshots/test/lib/npm.js.test.cjs

+450
Large diffs are not rendered by default.

‎test/lib/docs.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ t.test('usage', async t => {
6666
// are all in sync. eg, this will error if a command is removed but not its docs file
6767
t.strictSame(
6868
fsCommands.sort(localeCompare),
69-
cmdList.allCommands,
69+
cmdList.commands,
7070
'command list and fs are the same'
7171
)
7272
t.strictSame(
7373
allDocs.filter(f => !bareCommands.includes(f)).sort(localeCompare),
74-
cmdList.allCommands,
74+
cmdList.commands,
7575
'command list and docs files are the same'
7676
)
7777

‎test/lib/load-all-commands.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
const t = require('tap')
66
const util = require('util')
77
const { load: loadMockNpm } = require('../fixtures/mock-npm.js')
8-
const { allCommands } = require('../../lib/utils/cmd-list.js')
8+
const { commands } = require('../../lib/utils/cmd-list.js')
99

1010
const isAsyncFn = (v) => typeof v === 'function' && /^\[AsyncFunction:/.test(util.inspect(v))
1111

1212
t.test('load each command', async t => {
13-
for (const cmd of allCommands) {
13+
for (const cmd of commands) {
1414
t.test(cmd, async t => {
1515
const { npm, outputs, cmd: impl } = await loadMockNpm(t, {
1616
command: cmd,

‎test/lib/npm.js

+12-32
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ t.test('aliases and typos', async t => {
561561
await t.resolves(npm.cmd('it'), { name: 'install-test' })
562562
await t.resolves(npm.cmd('installTe'), { name: 'install-test' })
563563
await t.resolves(npm.cmd('access'), { name: 'access' })
564+
await t.resolves(npm.cmd('auth'), { name: 'owner' })
564565
})
565566

566567
t.test('explicit workspace rejection', async t => {
@@ -706,40 +707,19 @@ t.test('usage', async t => {
706707
})
707708

708709
t.test('set process.stdout.columns', async t => {
709-
const { npm } = await loadMockNpm(t)
710+
const { npm } = await loadMockNpm(t, {
711+
config: { viewer: 'man' },
712+
})
713+
t.cleanSnapshot = str =>
714+
str.replace(npm.config.get('userconfig'), '{USERCONFIG}').replace(npm.npmRoot, '{NPMROOT}')
710715

711-
const colUsage = async (cols) => {
712-
const usages = []
713-
for (const col of cols) {
714-
mockGlobals(t, { 'process.stdout.columns': col })
716+
const widths = [0, 1, 10, 24, 40, 41, 75, 76, 90, 100]
717+
for (const width of widths) {
718+
t.test(`column width ${width}`, async t => {
719+
mockGlobals(t, { 'process.stdout.columns': width })
715720
const usage = await npm.usage
716-
usages.push(usage)
717-
}
718-
return usages
721+
t.matchSnapshot(usage)
722+
})
719723
}
720-
721-
t.test('max size', async t => {
722-
const usages = await colUsage([0, 76, 90, 100])
723-
t.equal(usages.filter(Boolean).length, 4)
724-
t.equal(new Set([...usages]).size, 1)
725-
})
726-
727-
t.test('across max boundary', async t => {
728-
const usages = await colUsage([75, 76])
729-
t.equal(usages.filter(Boolean).length, 2)
730-
t.equal(new Set([...usages]).size, 2)
731-
})
732-
733-
t.test('min size', async t => {
734-
const usages = await colUsage([1, 10, 24, 40])
735-
t.equal(usages.filter(Boolean).length, 4)
736-
t.equal(new Set([...usages]).size, 1)
737-
})
738-
739-
t.test('different cols within min/max', async t => {
740-
const usages = await colUsage([40, 41])
741-
t.equal(usages.filter(Boolean).length, 2)
742-
t.equal(new Set([...usages]).size, 2)
743-
})
744724
})
745725
})

0 commit comments

Comments
 (0)
Please sign in to comment.