• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const Npm = require('../npm')
2const { distance } = require('fastest-levenshtein')
3const pkgJson = require('@npmcli/package-json')
4const { commands } = require('./cmd-list.js')
5
6const didYouMean = async (path, scmd) => {
7  const close = commands.filter(cmd => distance(scmd, cmd) < scmd.length * 0.4 && scmd !== cmd)
8  let best = []
9  for (const str of close) {
10    const cmd = Npm.cmd(str)
11    best.push(`    npm ${str} # ${cmd.description}`)
12  }
13  // We would already be suggesting this in `npm x` so omit them here
14  const runScripts = ['stop', 'start', 'test', 'restart']
15  try {
16    const { content: { scripts, bin } } = await pkgJson.normalize(path)
17    best = best.concat(
18      Object.keys(scripts || {})
19        .filter(cmd => distance(scmd, cmd) < scmd.length * 0.4 && !runScripts.includes(cmd))
20        .map(str => `    npm run ${str} # run the "${str}" package script`),
21      Object.keys(bin || {})
22        .filter(cmd => distance(scmd, cmd) < scmd.length * 0.4)
23        /* eslint-disable-next-line max-len */
24        .map(str => `    npm exec ${str} # run the "${str}" command from either this or a remote npm package`)
25    )
26  } catch (_) {
27    // gracefully ignore not being in a folder w/ a package.json
28  }
29
30  if (best.length === 0) {
31    return ''
32  }
33
34  const suggestion =
35    best.length === 1
36      ? `\n\nDid you mean this?\n${best[0]}`
37      : `\n\nDid you mean one of these?\n${best.slice(0, 3).join('\n')}`
38  return suggestion
39}
40module.exports = didYouMean
41