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