Skip to content

Commit

Permalink
fix: make usage and completion static functions (#6477)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukekarrys committed May 22, 2023
1 parent 4f39e8c commit 4b5ccfc
Show file tree
Hide file tree
Showing 80 changed files with 452 additions and 441 deletions.
110 changes: 53 additions & 57 deletions lib/base-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,65 @@

const { relative } = require('path')

const ConfigDefinitions = require('./utils/config/definitions.js')
const definitions = require('./utils/config/definitions.js')
const getWorkspaces = require('./workspaces/get-workspaces.js')

const cmdAliases = require('./utils/cmd-list').aliases
const { aliases: cmdAliases } = require('./utils/cmd-list')

class BaseCommand {
static workspaces = false
static ignoreImplicitWorkspace = true

// these are all overridden by individual commands
static name = null
static description = null
static params = null

// this is a static so that we can read from it without instantiating a command
// which would require loading the config
static get describeUsage () {
const wrapWidth = 80
const { description, usage = [''], name, params } = this

const fullUsage = [
`${description}`,
'',
'Usage:',
...usage.map(u => `npm ${name} ${u}`.trim()),
]

if (params) {
let results = ''
let line = ''
for (const param of params) {
const paramUsage = `[${definitions[param].usage}]`
if (line.length + paramUsage.length > wrapWidth) {
results = [results, line].filter(Boolean).join('\n')
line = ''
}
line = [line, paramUsage].filter(Boolean).join(' ')
}
fullUsage.push('')
fullUsage.push('Options:')
fullUsage.push([results, line].filter(Boolean).join('\n'))
}

const aliases = Object.entries(cmdAliases).reduce((p, [k, v]) => {
return p.concat(v === name ? k : [])
}, [])

if (aliases.length) {
const plural = aliases.length === 1 ? '' : 'es'
fullUsage.push('')
fullUsage.push(`alias${plural}: ${aliases.join(', ')}`)
}

fullUsage.push('')
fullUsage.push(`Run "npm help ${name}" for more info`)

return fullUsage.join('\n')
}

constructor (npm) {
this.wrapWidth = 80
this.npm = npm

const { config } = this.npm
Expand All @@ -39,59 +87,7 @@ class BaseCommand {
}

get usage () {
const usage = [
`${this.description}`,
'',
'Usage:',
]

if (!this.constructor.usage) {
usage.push(`npm ${this.name}`)
} else {
usage.push(...this.constructor.usage.map(u => `npm ${this.name} ${u}`))
}

if (this.params) {
usage.push('')
usage.push('Options:')
usage.push(this.wrappedParams)
}

const aliases = Object.keys(cmdAliases).reduce((p, c) => {
if (cmdAliases[c] === this.name) {
p.push(c)
}
return p
}, [])

if (aliases.length === 1) {
usage.push('')
usage.push(`alias: ${aliases.join(', ')}`)
} else if (aliases.length > 1) {
usage.push('')
usage.push(`aliases: ${aliases.join(', ')}`)
}

usage.push('')
usage.push(`Run "npm help ${this.name}" for more info`)

return usage.join('\n')
}

get wrappedParams () {
let results = ''
let line = ''

for (const param of this.params) {
const usage = `[${ConfigDefinitions[param].usage}]`
if (line.length && line.length + usage.length > this.wrapWidth) {
results = [results, line].filter(Boolean).join('\n')
line = ''
}
line = [line, usage].filter(Boolean).join(' ')
}
results = [results, line].filter(Boolean).join('\n')
return results
return this.constructor.describeUsage
}

usageError (prefix = '') {
Expand Down
4 changes: 2 additions & 2 deletions lib/cli-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module.exports = async (process, validateEngines) => {

cmd = npm.argv.shift()
if (!cmd) {
npm.output(await npm.usage)
npm.output(npm.usage)
process.exitCode = 1
return exitHandler()
}
Expand All @@ -63,7 +63,7 @@ module.exports = async (process, validateEngines) => {
} catch (err) {
if (err.code === 'EUNKNOWNCOMMAND') {
const didYouMean = require('./utils/did-you-mean.js')
const suggestions = await didYouMean(npm, npm.localPrefix, cmd)
const suggestions = await didYouMean(npm.localPrefix, cmd)
npm.output(`Unknown command: "${cmd}"${suggestions}\n`)
npm.output('To see a list of supported npm commands, run:\n npm help')
process.exitCode = 1
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/access.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Access extends BaseCommand {
'revoke <scope:team> [<package>]',
]

async completion (opts) {
static async completion (opts) {
const argv = opts.conf.argv.remain
if (argv.length === 2) {
return commands
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ class Audit extends ArboristWorkspaceCmd {

static usage = ['[fix|signatures]']

async completion (opts) {
static async completion (opts) {
const argv = opts.conf.argv.remain

if (argv.length === 2) {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Cache extends BaseCommand {
'verify',
]

async completion (opts) {
static async completion (opts) {
const argv = opts.conf.argv.remain
if (argv.length === 2) {
return ['add', 'clean', 'verify', 'ls']
Expand Down
19 changes: 12 additions & 7 deletions lib/commands/completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ const fs = require('fs/promises')
const nopt = require('nopt')
const { resolve } = require('path')

const Npm = require('../npm.js')
const { definitions, shorthands } = require('../utils/config/index.js')
const { commands, aliases } = require('../utils/cmd-list.js')
const { commands, aliases, deref } = require('../utils/cmd-list.js')
const configNames = Object.keys(definitions)
const shorthandNames = Object.keys(shorthands)
const allConfs = configNames.concat(shorthandNames)
Expand All @@ -48,7 +49,7 @@ class Completion extends BaseCommand {
static name = 'completion'

// completion for the completion command
async completion (opts) {
static async completion (opts) {
if (opts.w > 2) {
return
}
Expand Down Expand Up @@ -156,10 +157,14 @@ class Completion extends BaseCommand {
// at this point, if words[1] is some kind of npm command,
// then complete on it.
// otherwise, do nothing
const impl = await this.npm.cmd(cmd)
if (impl.completion) {
const comps = await impl.completion(opts)
return this.wrap(opts, comps)
try {
const { completion } = Npm.cmd(cmd)
if (completion) {
const comps = await completion(opts, this.npm)
return this.wrap(opts, comps)
}
} catch {
// it wasnt a valid command, so do nothing
}
}

Expand Down Expand Up @@ -267,7 +272,7 @@ const cmdCompl = (opts, npm) => {
return matches
}

const derefs = new Set([...matches.map(c => npm.deref(c))])
const derefs = new Set([...matches.map(c => deref(c))])
if (derefs.size === 1) {
return [...derefs]
}
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class Config extends BaseCommand {

static skipConfigValidation = true

async completion (opts) {
static async completion (opts) {
const argv = opts.conf.argv.remain
if (argv[1] !== 'config') {
argv.unshift('config')
Expand Down
6 changes: 3 additions & 3 deletions lib/commands/deprecate.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ class Deprecate extends BaseCommand {

static ignoreImplicitWorkspace = false

async completion (opts) {
static async completion (opts, npm) {
if (opts.conf.argv.remain.length > 1) {
return []
}

const username = await getIdentity(this.npm, this.npm.flatOptions)
const packages = await libaccess.getPackages(username, this.npm.flatOptions)
const username = await getIdentity(npm, npm.flatOptions)
const packages = await libaccess.getPackages(username, npm.flatOptions)
return Object.keys(packages)
.filter((name) =>
packages[name] === 'write' &&
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/dist-tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DistTag extends BaseCommand {
static workspaces = true
static ignoreImplicitWorkspace = false

async completion (opts) {
static async completion (opts) {
const argv = opts.conf.argv.remain
if (argv.length === 2) {
return ['add', 'rm', 'ls']
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class Edit extends BaseCommand {

// TODO
/* istanbul ignore next */
async completion (opts) {
return completion(this.npm, opts)
static async completion (opts, npm) {
return completion(npm, opts)
}

async exec (args) {
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/explain.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class Explain extends ArboristWorkspaceCmd {

// TODO
/* istanbul ignore next */
async completion (opts) {
static async completion (opts, npm) {
const completion = require('../utils/completion/installed-deep.js')
return completion(this.npm, opts)
return completion(npm, opts)
}

async exec (args) {
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/explore.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Explore extends BaseCommand {

// TODO
/* istanbul ignore next */
async completion (opts) {
return completion(this.npm, opts)
static async completion (opts, npm) {
return completion(npm, opts)
}

async exec (args) {
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/fund.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ class Fund extends ArboristWorkspaceCmd {

// TODO
/* istanbul ignore next */
async completion (opts) {
static async completion (opts, npm) {
const completion = require('../utils/completion/installed-deep.js')
return completion(this.npm, opts)
return completion(npm, opts)
}

async exec (args) {
Expand Down
7 changes: 4 additions & 3 deletions lib/commands/get.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Npm = require('../npm.js')
const BaseCommand = require('../base-command.js')

class Get extends BaseCommand {
Expand All @@ -9,9 +10,9 @@ class Get extends BaseCommand {

// TODO
/* istanbul ignore next */
async completion (opts) {
const config = await this.npm.cmd('config')
return config.completion(opts)
static async completion (opts) {
const Config = Npm.cmd('config')
return Config.completion(opts)
}

async exec (args) {
Expand Down
9 changes: 5 additions & 4 deletions lib/commands/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const path = require('path')
const openUrl = require('../utils/open-url.js')
const { glob } = require('glob')
const localeCompare = require('@isaacs/string-locale-compare')('en')
const { deref } = require('../utils/cmd-list.js')

const globify = pattern => pattern.split('\\').join('/')
const BaseCommand = require('../base-command.js')
Expand All @@ -26,11 +27,11 @@ class Help extends BaseCommand {
static usage = ['<term> [<terms..>]']
static params = ['viewer']

async completion (opts) {
static async completion (opts, npm) {
if (opts.conf.argv.remain.length > 2) {
return []
}
const g = path.resolve(this.npm.npmRoot, 'man/man[0-9]/*.[0-9]')
const g = path.resolve(npm.npmRoot, 'man/man[0-9]/*.[0-9]')
let files = await glob(globify(g))
// preserve glob@8 behavior
files = files.sort((a, b) => a.localeCompare(b, 'en'))
Expand All @@ -49,7 +50,7 @@ class Help extends BaseCommand {
const manSearch = /^\d+$/.test(args[0]) ? `man${args.shift()}` : 'man*'

if (!args.length) {
return this.npm.output(await this.npm.usage)
return this.npm.output(this.npm.usage)
}

// npm help foo bar baz: search topics
Expand All @@ -58,7 +59,7 @@ class Help extends BaseCommand {
}

// `npm help package.json`
const arg = (this.npm.deref(args[0]) || args[0]).replace('.json', '-json')
const arg = (deref(args[0]) || args[0]).replace('.json', '-json')

// find either section.n or npm-section.n
const f = globify(path.resolve(this.npm.npmRoot, `man/${manSearch}/?(npm-)${arg}.[0-9]*`))
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Install extends ArboristWorkspaceCmd {

static usage = ['[<package-spec> ...]']

async completion (opts) {
static async completion (opts) {
const { partialWord } = opts
// install can complete to a folder with a package.json, or any package.
// if it has a slash, then it's gotta be a folder
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ class Link extends ArboristWorkspaceCmd {
...super.params,
]

async completion (opts) {
const dir = this.npm.globalDir
static async completion (opts, npm) {
const dir = npm.globalDir
const files = await readdir(dir)
return files.filter(f => !/^[._-]/.test(f))
}
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/ls.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class LS extends ArboristWorkspaceCmd {

// TODO
/* istanbul ignore next */
async completion (opts) {
static async completion (opts, npm) {
const completion = require('../utils/completion/installed-deep.js')
return completion(this.npm, opts)
return completion(npm, opts)
}

async exec (args) {
Expand Down
Loading

0 comments on commit 4b5ccfc

Please sign in to comment.