codekingpro/portable-devtools
114k
1const Npm = require('../npm')
2const { distance } = require('fastest-levenshtein')
3const { commands } = require('./cmd-list.js')
4
5const runScripts = ['stop', 'start', 'test', 'restart']
6
7const isClose = (scmd, cmd) => distance(scmd, cmd) < scmd.length * 0.4
8
9const didYouMean = (pkg, scmd) => {
10 const { scripts = {}, bin = {} } = pkg || {}
11
12 const best = [
13 ...commands
14 .filter(cmd => isClose(scmd, cmd) && scmd !== cmd)
15 .map(str => [str, Npm.cmd(str).description]),
16 ...Object.keys(scripts)
17 // We would already be suggesting this in `npm x` so omit them here
18 .filter(cmd => isClose(scmd, cmd) && !runScripts.includes(cmd))
19 .map(str => [`run ${str}`, `run the "${str}" package script`]),
20 ...Object.keys(bin)
21 .filter(cmd => isClose(scmd, cmd))
22 .map(str => [`exec ${str}`, `run the "${str}" command from either this or a remote npm package`]),
23 ]
24
25 if (best.length === 0) {
26 return ''
27 }
28
29 return `\n\nDid you mean ${best.length === 1 ? 'this' : 'one of these'}?\n` +
30 best.slice(0, 3).map(([msg, comment]) => ` npm ${msg} # ${comment}`).join('\n')
31}
32
33module.exports = didYouMean
34 