11cb0ef41Sopenharmony_ciconst spawn = require('@npmcli/promise-spawn') 21cb0ef41Sopenharmony_ciconst path = require('path') 31cb0ef41Sopenharmony_ciconst openUrl = require('../utils/open-url.js') 41cb0ef41Sopenharmony_ciconst { glob } = require('glob') 51cb0ef41Sopenharmony_ciconst localeCompare = require('@isaacs/string-locale-compare')('en') 61cb0ef41Sopenharmony_ciconst { deref } = require('../utils/cmd-list.js') 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ciconst globify = pattern => pattern.split('\\').join('/') 91cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js') 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ci// Strips out the number from foo.7 or foo.7. or foo.7.tgz 121cb0ef41Sopenharmony_ci// We don't currently compress our man pages but if we ever did this would 131cb0ef41Sopenharmony_ci// seamlessly continue supporting it 141cb0ef41Sopenharmony_ciconst manNumberRegex = /\.(\d+)(\.[^/\\]*)?$/ 151cb0ef41Sopenharmony_ci// hardcoded names for mansections 161cb0ef41Sopenharmony_ci// XXX: these are used in the docs workspace and should be exported 171cb0ef41Sopenharmony_ci// from npm so section names can changed more easily 181cb0ef41Sopenharmony_ciconst manSectionNames = { 191cb0ef41Sopenharmony_ci 1: 'commands', 201cb0ef41Sopenharmony_ci 5: 'configuring-npm', 211cb0ef41Sopenharmony_ci 7: 'using-npm', 221cb0ef41Sopenharmony_ci} 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ciclass Help extends BaseCommand { 251cb0ef41Sopenharmony_ci static description = 'Get help on npm' 261cb0ef41Sopenharmony_ci static name = 'help' 271cb0ef41Sopenharmony_ci static usage = ['<term> [<terms..>]'] 281cb0ef41Sopenharmony_ci static params = ['viewer'] 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci static async completion (opts, npm) { 311cb0ef41Sopenharmony_ci if (opts.conf.argv.remain.length > 2) { 321cb0ef41Sopenharmony_ci return [] 331cb0ef41Sopenharmony_ci } 341cb0ef41Sopenharmony_ci const g = path.resolve(npm.npmRoot, 'man/man[0-9]/*.[0-9]') 351cb0ef41Sopenharmony_ci let files = await glob(globify(g)) 361cb0ef41Sopenharmony_ci // preserve glob@8 behavior 371cb0ef41Sopenharmony_ci files = files.sort((a, b) => a.localeCompare(b, 'en')) 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci return Object.keys(files.reduce(function (acc, file) { 401cb0ef41Sopenharmony_ci file = path.basename(file).replace(/\.[0-9]+$/, '') 411cb0ef41Sopenharmony_ci file = file.replace(/^npm-/, '') 421cb0ef41Sopenharmony_ci acc[file] = true 431cb0ef41Sopenharmony_ci return acc 441cb0ef41Sopenharmony_ci }, { help: true })) 451cb0ef41Sopenharmony_ci } 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ci async exec (args) { 481cb0ef41Sopenharmony_ci // By default we search all of our man subdirectories, but if the user has 491cb0ef41Sopenharmony_ci // asked for a specific one we limit the search to just there 501cb0ef41Sopenharmony_ci const manSearch = /^\d+$/.test(args[0]) ? `man${args.shift()}` : 'man*' 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci if (!args.length) { 531cb0ef41Sopenharmony_ci return this.npm.output(this.npm.usage) 541cb0ef41Sopenharmony_ci } 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci // npm help foo bar baz: search topics 571cb0ef41Sopenharmony_ci if (args.length > 1) { 581cb0ef41Sopenharmony_ci return this.helpSearch(args) 591cb0ef41Sopenharmony_ci } 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci // `npm help package.json` 621cb0ef41Sopenharmony_ci const arg = (deref(args[0]) || args[0]).replace('.json', '-json') 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci // find either section.n or npm-section.n 651cb0ef41Sopenharmony_ci const f = globify(path.resolve(this.npm.npmRoot, `man/${manSearch}/?(npm-)${arg}.[0-9]*`)) 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci const [man] = await glob(f).then(r => r.sort((a, b) => { 681cb0ef41Sopenharmony_ci // Because the glob is (subtly) different from manNumberRegex, 691cb0ef41Sopenharmony_ci // we can't rely on it passing. 701cb0ef41Sopenharmony_ci const aManNumberMatch = a.match(manNumberRegex)?.[1] || 999 711cb0ef41Sopenharmony_ci const bManNumberMatch = b.match(manNumberRegex)?.[1] || 999 721cb0ef41Sopenharmony_ci if (aManNumberMatch !== bManNumberMatch) { 731cb0ef41Sopenharmony_ci return aManNumberMatch - bManNumberMatch 741cb0ef41Sopenharmony_ci } 751cb0ef41Sopenharmony_ci return localeCompare(a, b) 761cb0ef41Sopenharmony_ci })) 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci return man ? this.viewMan(man) : this.helpSearch(args) 791cb0ef41Sopenharmony_ci } 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci helpSearch (args) { 821cb0ef41Sopenharmony_ci return this.npm.exec('help-search', args) 831cb0ef41Sopenharmony_ci } 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci async viewMan (man) { 861cb0ef41Sopenharmony_ci const viewer = this.npm.config.get('viewer') 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_ci if (viewer === 'browser') { 891cb0ef41Sopenharmony_ci return openUrl(this.npm, this.htmlMan(man), 'help available at the following URL', true) 901cb0ef41Sopenharmony_ci } 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci let args = ['man', [man]] 931cb0ef41Sopenharmony_ci if (viewer === 'woman') { 941cb0ef41Sopenharmony_ci args = ['emacsclient', ['-e', `(woman-find-file '${man}')`]] 951cb0ef41Sopenharmony_ci } 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci return spawn(...args, { stdio: 'inherit' }).catch(err => { 981cb0ef41Sopenharmony_ci if (err.code) { 991cb0ef41Sopenharmony_ci throw new Error(`help process exited with code: ${err.code}`) 1001cb0ef41Sopenharmony_ci } else { 1011cb0ef41Sopenharmony_ci throw err 1021cb0ef41Sopenharmony_ci } 1031cb0ef41Sopenharmony_ci }) 1041cb0ef41Sopenharmony_ci } 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci // Returns the path to the html version of the man page 1071cb0ef41Sopenharmony_ci htmlMan (man) { 1081cb0ef41Sopenharmony_ci const sect = manSectionNames[man.match(manNumberRegex)[1]] 1091cb0ef41Sopenharmony_ci const f = path.basename(man).replace(manNumberRegex, '') 1101cb0ef41Sopenharmony_ci return 'file:///' + path.resolve(this.npm.npmRoot, `docs/output/${sect}/${f}.html`) 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci} 1131cb0ef41Sopenharmony_cimodule.exports = Help 114