diff options
Diffstat (limited to 'deps/npm/lib/help.js')
-rw-r--r-- | deps/npm/lib/help.js | 357 |
1 files changed, 195 insertions, 162 deletions
diff --git a/deps/npm/lib/help.js b/deps/npm/lib/help.js index 6f215c76c1..d7897326f3 100644 --- a/deps/npm/lib/help.js +++ b/deps/npm/lib/help.js @@ -1,191 +1,224 @@ - -module.exports = help - -help.completion = async (opts) => { - if (opts.conf.argv.remain.length > 2) - return [] - const g = path.resolve(__dirname, '../man/man[0-9]/*.[0-9]') - const files = await new Promise((resolve, reject) => { - glob(g, function (er, files) { - if (er) - return reject(er) - resolve(files) - }) - }) - - return Object.keys(files.reduce(function (acc, file) { - file = path.basename(file).replace(/\.[0-9]+$/, '') - file = file.replace(/^npm-/, '') - acc[file] = true - return acc - }, { help: true })) -} - const npmUsage = require('./utils/npm-usage.js') const { spawn } = require('child_process') const path = require('path') -const npm = require('./npm.js') const log = require('npmlog') -const openUrl = require('./utils/open-url') +const openUrl = require('./utils/open-url.js') const glob = require('glob') const output = require('./utils/output.js') const usage = require('./utils/usage.js') -help.usage = usage('help', 'npm help <term> [<terms..>]') - -function help (args, cb) { - const argv = npm.config.parsedArgv.cooked - - let argnum = 0 - if (args.length === 2 && ~~args[0]) - argnum = ~~args.shift() - - // npm help foo bar baz: search topics - if (args.length > 1 && args[0]) - return npm.commands['help-search'](args, cb) +class Help { + constructor (npm) { + this.npm = npm + } - const affordances = { - 'find-dupes': 'dedupe', + /* istanbul ignore next - see test/lib/load-all-commands.js */ + get usage () { + return usage('help', 'npm help <term> [<terms..>]') } - let section = affordances[args[0]] || npm.deref(args[0]) || args[0] - // npm help <noargs>: show basic usage - if (!section) { - npmUsage(argv[0] === 'help') - return cb() + async completion (opts) { + if (opts.conf.argv.remain.length > 2) + return [] + const g = path.resolve(__dirname, '../man/man[0-9]/*.[0-9]') + const files = await new Promise((resolve, reject) => { + glob(g, function (er, files) { + if (er) + return reject(er) + resolve(files) + }) + }) + + return Object.keys(files.reduce(function (acc, file) { + file = path.basename(file).replace(/\.[0-9]+$/, '') + file = file.replace(/^npm-/, '') + acc[file] = true + return acc + }, { help: true })) } - // npm <command> -h: show command usage - if (npm.config.get('usage') && - npm.commands[section] && - npm.commands[section].usage) { - npm.config.set('loglevel', 'silent') - log.level = 'silent' - output(npm.commands[section].usage) - return cb() + exec (args, cb) { + this.help(args).then(() => cb()).catch(cb) } - let pref = [1, 5, 7] - if (argnum) - pref = [argnum].concat(pref.filter(n => n !== argnum)) - - // npm help <section>: Try to find the path - const manroot = path.resolve(__dirname, '..', 'man') - - // legacy - if (section === 'global') - section = 'folders' - else if (section.match(/.*json/)) - section = section.replace('.json', '-json') - - // find either /section.n or /npm-section.n - // The glob is used in the glob. The regexp is used much - // further down. Globs and regexps are different - const compextglob = '.+(gz|bz2|lzma|[FYzZ]|xz)' - const compextre = '\\.(gz|bz2|lzma|[FYzZ]|xz)$' - const f = '+(npm-' + section + '|' + section + ').[0-9]?(' + compextglob + ')' - return glob(manroot + '/*/' + f, (er, mans) => { - if (er) - return cb(er) - - if (!mans.length) - return npm.commands['help-search'](args, cb) - - mans = mans.map((man) => { - const ext = path.extname(man) - if (man.match(new RegExp(compextre))) - man = path.basename(man, ext) - - return man + async help (args) { + const argv = this.npm.config.parsedArgv.cooked + + let argnum = 0 + if (args.length === 2 && ~~args[0]) + argnum = ~~args.shift() + + // npm help foo bar baz: search topics + if (args.length > 1 && args[0]) + return this.helpSearch(args) + + const affordances = { + 'find-dupes': 'dedupe', + } + let section = affordances[args[0]] || this.npm.deref(args[0]) || args[0] + + // npm help <noargs>: show basic usage + if (!section) { + npmUsage(this.npm, argv[0] === 'help') + return + } + + // npm <command> -h: show command usage + if (this.npm.config.get('usage') && + this.npm.commands[section] && + this.npm.commands[section].usage) { + this.npm.config.set('loglevel', 'silent') + log.level = 'silent' + output(this.npm.commands[section].usage) + return + } + + let pref = [1, 5, 7] + if (argnum) + pref = [argnum].concat(pref.filter(n => n !== argnum)) + + // npm help <section>: Try to find the path + const manroot = path.resolve(__dirname, '..', 'man') + + // legacy + if (section === 'global') + section = 'folders' + else if (section.match(/.*json/)) + section = section.replace('.json', '-json') + + // find either /section.n or /npm-section.n + // The glob is used in the glob. The regexp is used much + // further down. Globs and regexps are different + const compextglob = '.+(gz|bz2|lzma|[FYzZ]|xz)' + const compextre = '\\.(gz|bz2|lzma|[FYzZ]|xz)$' + const f = '+(npm-' + section + '|' + section + ').[0-9]?(' + compextglob + ')' + return new Promise((resolve, reject) => { + glob(manroot + '/*/' + f, async (er, mans) => { + if (er) + return reject(er) + + if (!mans.length) { + this.helpSearch(args).then(resolve).catch(reject) + return + } + + mans = mans.map((man) => { + const ext = path.extname(man) + if (man.match(new RegExp(compextre))) + man = path.basename(man, ext) + + return man + }) + + this.viewMan(this.pickMan(mans, pref), (err) => { + if (err) + return reject(err) + return resolve() + }) + }) }) + } - viewMan(pickMan(mans, pref), cb) - }) -} - -function pickMan (mans, pref_) { - const nre = /([0-9]+)$/ - const pref = {} - pref_.forEach((sect, i) => pref[sect] = i) - mans = mans.sort((a, b) => { - const an = a.match(nre)[1] - const bn = b.match(nre)[1] - return an === bn ? (a > b ? -1 : 1) - : pref[an] < pref[bn] ? -1 - : 1 - }) - return mans[0] -} + helpSearch (args) { + return new Promise((resolve, reject) => { + this.npm.commands['help-search'](args, (err) => { + // This would only error if args was empty, which it never is + /* istanbul ignore next */ + if (err) + return reject(err) -function viewMan (man, cb) { - const nre = /([0-9]+)$/ - const num = man.match(nre)[1] - const section = path.basename(man, '.' + num) - - // at this point, we know that the specified man page exists - const manpath = path.join(__dirname, '..', 'man') - const env = {} - Object.keys(process.env).forEach(function (i) { - env[i] = process.env[i] - }) - env.MANPATH = manpath - const viewer = npm.config.get('viewer') - - const opts = { - env, - stdio: 'inherit', + resolve() + }) + }) } - let bin = 'man' - const args = [] - switch (viewer) { - case 'woman': - bin = 'emacsclient' - args.push('-e', `(woman-find-file '${man}')`) - break - - case 'browser': - bin = false - try { - const url = htmlMan(man) - openUrl(url, 'help available at the following URL', cb) - } catch (err) { - return cb(err) - } - break - - default: - args.push(num, section) - break + pickMan (mans, pref_) { + const nre = /([0-9]+)$/ + const pref = {} + pref_.forEach((sect, i) => pref[sect] = i) + mans = mans.sort((a, b) => { + const an = a.match(nre)[1] + const bn = b.match(nre)[1] + return an === bn ? (a > b ? -1 : 1) + : pref[an] < pref[bn] ? -1 + : 1 + }) + return mans[0] } - if (bin) { - const proc = spawn(bin, args, opts) - proc.on('exit', (code) => { - if (code) - return cb(new Error(`help process exited with code: ${code}`)) + viewMan (man, cb) { + const nre = /([0-9]+)$/ + const num = man.match(nre)[1] + const section = path.basename(man, '.' + num) - return cb() + // at this point, we know that the specified man page exists + const manpath = path.join(__dirname, '..', 'man') + const env = {} + Object.keys(process.env).forEach(function (i) { + env[i] = process.env[i] }) + env.MANPATH = manpath + const viewer = this.npm.config.get('viewer') + + const opts = { + env, + stdio: 'inherit', + } + + let bin = 'man' + const args = [] + switch (viewer) { + case 'woman': + bin = 'emacsclient' + args.push('-e', `(woman-find-file '${man}')`) + break + + case 'browser': + bin = false + try { + const url = this.htmlMan(man) + openUrl(this.npm, url, 'help available at the following URL').then( + () => cb() + ).catch(cb) + } catch (err) { + cb(err) + } + break + + default: + args.push(num, section) + break + } + + if (bin) { + const proc = spawn(bin, args, opts) + proc.on('exit', (code) => { + if (code) + return cb(new Error(`help process exited with code: ${code}`)) + + return cb() + }) + } } -} -function htmlMan (man) { - let sect = +man.match(/([0-9]+)$/)[1] - const f = path.basename(man).replace(/[.]([0-9]+)$/, '') - switch (sect) { - case 1: - sect = 'commands' - break - case 5: - sect = 'configuring-npm' - break - case 7: - sect = 'using-npm' - break - default: - throw new Error('invalid man section: ' + sect) + htmlMan (man) { + let sect = +man.match(/([0-9]+)$/)[1] + const f = path.basename(man).replace(/[.]([0-9]+)$/, '') + switch (sect) { + case 1: + sect = 'commands' + break + case 5: + sect = 'configuring-npm' + break + case 7: + sect = 'using-npm' + break + default: + throw new Error('invalid man section: ' + sect) + } + return 'file://' + path.resolve(__dirname, '..', 'docs', 'output', sect, f + '.html') } - return 'file://' + path.resolve(__dirname, '..', 'docs', 'output', sect, f + '.html') } +module.exports = Help |