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