summaryrefslogtreecommitdiff
path: root/deps/npm/lib
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/lib')
-rw-r--r--deps/npm/lib/access.js285
-rw-r--r--deps/npm/lib/adduser.js105
-rw-r--r--deps/npm/lib/audit.js90
-rw-r--r--deps/npm/lib/auth/legacy.js8
-rw-r--r--deps/npm/lib/auth/oauth.js5
-rw-r--r--deps/npm/lib/auth/saml.js5
-rw-r--r--deps/npm/lib/auth/sso.js9
-rw-r--r--deps/npm/lib/bin.js33
-rw-r--r--deps/npm/lib/birthday.js27
-rw-r--r--deps/npm/lib/bugs.js69
-rw-r--r--deps/npm/lib/cache.js184
-rw-r--r--deps/npm/lib/ci.js108
-rw-r--r--deps/npm/lib/completion.js201
-rw-r--r--deps/npm/lib/config.js372
-rw-r--r--deps/npm/lib/dedupe.js46
-rw-r--r--deps/npm/lib/deprecate.js115
-rw-r--r--deps/npm/lib/diff.js417
-rw-r--r--deps/npm/lib/dist-tag.js181
-rw-r--r--deps/npm/lib/docs.js74
-rw-r--r--deps/npm/lib/doctor.js487
-rw-r--r--deps/npm/lib/edit.js70
-rw-r--r--deps/npm/lib/exec.js451
-rw-r--r--deps/npm/lib/explain.js150
-rw-r--r--deps/npm/lib/explore.js119
-rw-r--r--deps/npm/lib/find-dupes.js19
-rw-r--r--deps/npm/lib/fund.js344
-rw-r--r--deps/npm/lib/get.js30
-rw-r--r--deps/npm/lib/help-search.js312
-rw-r--r--deps/npm/lib/help.js357
-rw-r--r--deps/npm/lib/hook.js110
-rw-r--r--deps/npm/lib/init.js157
-rw-r--r--deps/npm/lib/install-ci-test.js32
-rw-r--r--deps/npm/lib/install-test.js34
-rw-r--r--deps/npm/lib/install.js233
-rw-r--r--deps/npm/lib/link.js257
-rw-r--r--deps/npm/lib/ll.js22
-rw-r--r--deps/npm/lib/logout.js76
-rw-r--r--deps/npm/lib/ls.js307
-rw-r--r--deps/npm/lib/npm.js62
-rw-r--r--deps/npm/lib/org.js249
-rw-r--r--deps/npm/lib/outdated.js369
-rw-r--r--deps/npm/lib/owner.js212
-rw-r--r--deps/npm/lib/pack.js63
-rw-r--r--deps/npm/lib/ping.js49
-rw-r--r--deps/npm/lib/prefix.js25
-rw-r--r--deps/npm/lib/profile.js603
-rw-r--r--deps/npm/lib/prune.js40
-rw-r--r--deps/npm/lib/publish.js210
-rw-r--r--deps/npm/lib/rebuild.js98
-rw-r--r--deps/npm/lib/repo.js83
-rw-r--r--deps/npm/lib/restart.js11
-rw-r--r--deps/npm/lib/root.js25
-rw-r--r--deps/npm/lib/run-script.js235
-rw-r--r--deps/npm/lib/search.js131
-rw-r--r--deps/npm/lib/set-script.js92
-rw-r--r--deps/npm/lib/set.js32
-rw-r--r--deps/npm/lib/shrinkwrap.js92
-rw-r--r--deps/npm/lib/star.js118
-rw-r--r--deps/npm/lib/stars.js61
-rw-r--r--deps/npm/lib/start.js11
-rw-r--r--deps/npm/lib/stop.js11
-rw-r--r--deps/npm/lib/team.js248
-rw-r--r--deps/npm/lib/test.js29
-rw-r--r--deps/npm/lib/token.js314
-rw-r--r--deps/npm/lib/uninstall.js87
-rw-r--r--deps/npm/lib/unpublish.js175
-rw-r--r--deps/npm/lib/unstar.js14
-rw-r--r--deps/npm/lib/update.js62
-rw-r--r--deps/npm/lib/utils/audit-error.js3
-rw-r--r--deps/npm/lib/utils/completion/installed-deep.js3
-rw-r--r--deps/npm/lib/utils/completion/installed-shallow.js6
-rw-r--r--deps/npm/lib/utils/explain-dep.js3
-rw-r--r--deps/npm/lib/utils/get-identity.js3
-rw-r--r--deps/npm/lib/utils/lifecycle-cmd.js21
-rw-r--r--deps/npm/lib/utils/npm-usage.js10
-rw-r--r--deps/npm/lib/utils/open-url.js41
-rw-r--r--deps/npm/lib/utils/read-local-package.js4
-rw-r--r--deps/npm/lib/utils/reify-finish.js9
-rw-r--r--deps/npm/lib/utils/reify-output.js13
-rw-r--r--deps/npm/lib/utils/usage.js4
-rw-r--r--deps/npm/lib/version.js127
-rw-r--r--deps/npm/lib/view.js636
-rw-r--r--deps/npm/lib/whoami.js31
83 files changed, 5623 insertions, 5003 deletions
diff --git a/deps/npm/lib/access.js b/deps/npm/lib/access.js
index 10b1e21e0c..e11934af43 100644
--- a/deps/npm/lib/access.js
+++ b/deps/npm/lib/access.js
@@ -3,25 +3,11 @@ const path = require('path')
const libaccess = require('libnpmaccess')
const readPackageJson = require('read-package-json-fast')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const usageUtil = require('./utils/usage.js')
const getIdentity = require('./utils/get-identity.js')
-const usage = usageUtil(
- 'npm access',
- 'npm access public [<package>]\n' +
- 'npm access restricted [<package>]\n' +
- 'npm access grant <read-only|read-write> <scope:team> [<package>]\n' +
- 'npm access revoke <scope:team> [<package>]\n' +
- 'npm access 2fa-required [<package>]\n' +
- 'npm access 2fa-not-required [<package>]\n' +
- 'npm access ls-packages [<user>|<scope>|<scope:team>]\n' +
- 'npm access ls-collaborators [<package> [<user>]]\n' +
- 'npm access edit [<package>]'
-)
-
const subcommands = [
'public',
'restricted',
@@ -34,152 +20,195 @@ const subcommands = [
'2fa-not-required',
]
-const UsageError = (msg) =>
- Object.assign(new Error(`\nUsage: ${msg}\n\n` + usage), {
- code: 'EUSAGE',
- })
-
-const cmd = (args, cb) =>
- access(args)
- .then(x => cb(null, x))
- .catch(err => err.code === 'EUSAGE'
- ? cb(err.message)
- : cb(err)
+class Access {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ get usage () {
+ return usageUtil(
+ 'access',
+ 'npm access public [<package>]\n' +
+ 'npm access restricted [<package>]\n' +
+ 'npm access grant <read-only|read-write> <scope:team> [<package>]\n' +
+ 'npm access revoke <scope:team> [<package>]\n' +
+ 'npm access 2fa-required [<package>]\n' +
+ 'npm access 2fa-not-required [<package>]\n' +
+ 'npm access ls-packages [<user>|<scope>|<scope:team>]\n' +
+ 'npm access ls-collaborators [<package> [<user>]]\n' +
+ 'npm access edit [<package>]'
)
+ }
-const access = async ([cmd, ...args], cb) => {
- const fn = subcommands.includes(cmd) && access[cmd]
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv.length === 2)
+ return subcommands
+
+ switch (argv[2]) {
+ case 'grant':
+ if (argv.length === 3)
+ return ['read-only', 'read-write']
+ else
+ return []
+
+ case 'public':
+ case 'restricted':
+ case 'ls-packages':
+ case 'ls-collaborators':
+ case 'edit':
+ case '2fa-required':
+ case '2fa-not-required':
+ case 'revoke':
+ return []
+ default:
+ throw new Error(argv[2] + ' not recognized')
+ }
+ }
- if (!cmd)
- throw UsageError('Subcommand is required.')
+ exec (args, cb) {
+ this.access(args)
+ .then(x => cb(null, x))
+ .catch(err => err.code === 'EUSAGE'
+ ? cb(err.message)
+ : cb(err)
+ )
+ }
- if (!fn)
- throw UsageError(`${cmd} is not a recognized subcommand.`)
+ async access ([cmd, ...args]) {
+ if (!cmd)
+ throw this.usageError('Subcommand is required.')
- return fn(args, { ...npm.flatOptions })
-}
+ if (!subcommands.includes(cmd) || !this[cmd])
+ throw this.usageError(`${cmd} is not a recognized subcommand.`)
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
- if (argv.length === 2)
- return subcommands
+ return this[cmd](args, { ...this.npm.flatOptions })
+ }
- switch (argv[2]) {
- case 'grant':
- if (argv.length === 3)
- return ['read-only', 'read-write']
- else
- return []
+ public ([pkg], opts) {
+ return this.modifyPackage(pkg, opts, libaccess.public)
+ }
- case 'public':
- case 'restricted':
- case 'ls-packages':
- case 'ls-collaborators':
- case 'edit':
- case '2fa-required':
- case '2fa-not-required':
- case 'revoke':
- return []
- default:
- throw new Error(argv[2] + ' not recognized')
+ restricted ([pkg], opts) {
+ return this.modifyPackage(pkg, opts, libaccess.restricted)
}
-}
-access.public = ([pkg], opts) =>
- modifyPackage(pkg, opts, libaccess.public)
+ async grant ([perms, scopeteam, pkg], opts) {
+ if (!perms || (perms !== 'read-only' && perms !== 'read-write'))
+ throw this.usageError('First argument must be either `read-only` or `read-write`.')
-access.restricted = ([pkg], opts) =>
- modifyPackage(pkg, opts, libaccess.restricted)
+ if (!scopeteam)
+ throw this.usageError('`<scope:team>` argument is required.')
-access.grant = async ([perms, scopeteam, pkg], opts) => {
- if (!perms || (perms !== 'read-only' && perms !== 'read-write'))
- throw UsageError('First argument must be either `read-only` or `read-write`.')
+ const [, scope, team] = scopeteam.match(/^@?([^:]+):(.*)$/) || []
- if (!scopeteam)
- throw UsageError('`<scope:team>` argument is required.')
+ if (!scope && !team) {
+ throw this.usageError(
+ 'Second argument used incorrect format.\n' +
+ 'Example: @example:developers'
+ )
+ }
- const [, scope, team] = scopeteam.match(/^@?([^:]+):(.*)$/) || []
+ return this.modifyPackage(pkg, opts, (pkgName, opts) =>
+ libaccess.grant(pkgName, scopeteam, perms, opts), false)
+ }
- if (!scope && !team) {
- throw UsageError(
- 'Second argument used incorrect format.\n' +
- 'Example: @example:developers'
- )
+ async revoke ([scopeteam, pkg], opts) {
+ if (!scopeteam)
+ throw this.usageError('`<scope:team>` argument is required.')
+
+ const [, scope, team] = scopeteam.match(/^@?([^:]+):(.*)$/) || []
+
+ if (!scope || !team) {
+ throw this.usageError(
+ 'First argument used incorrect format.\n' +
+ 'Example: @example:developers'
+ )
+ }
+
+ return this.modifyPackage(pkg, opts, (pkgName, opts) =>
+ libaccess.revoke(pkgName, scopeteam, opts))
}
- return modifyPackage(pkg, opts, (pkgName, opts) =>
- libaccess.grant(pkgName, scopeteam, perms, opts), false)
-}
+ get ['2fa-required'] () {
+ return this.tfaRequired
+ }
-access.revoke = async ([scopeteam, pkg], opts) => {
- if (!scopeteam)
- throw UsageError('`<scope:team>` argument is required.')
+ tfaRequired ([pkg], opts) {
+ return this.modifyPackage(pkg, opts, libaccess.tfaRequired, false)
+ }
- const [, scope, team] = scopeteam.match(/^@?([^:]+):(.*)$/) || []
+ get ['2fa-not-required'] () {
+ return this.tfaNotRequired
+ }
- if (!scope || !team) {
- throw UsageError(
- 'First argument used incorrect format.\n' +
- 'Example: @example:developers'
- )
+ tfaNotRequired ([pkg], opts) {
+ return this.modifyPackage(pkg, opts, libaccess.tfaNotRequired, false)
}
- return modifyPackage(pkg, opts, (pkgName, opts) =>
- libaccess.revoke(pkgName, scopeteam, opts))
-}
+ get ['ls-packages'] () {
+ return this.lsPackages
+ }
-access['2fa-required'] = access.tfaRequired = ([pkg], opts) =>
- modifyPackage(pkg, opts, libaccess.tfaRequired, false)
+ async lsPackages ([owner], opts) {
+ if (!owner)
+ owner = await getIdentity(this.npm, opts)
-access['2fa-not-required'] = access.tfaNotRequired = ([pkg], opts) =>
- modifyPackage(pkg, opts, libaccess.tfaNotRequired, false)
+ const pkgs = await libaccess.lsPackages(owner, opts)
-access['ls-packages'] = access.lsPackages = async ([owner], opts) => {
- if (!owner)
- owner = await getIdentity(opts)
+ // TODO - print these out nicely (breaking change)
+ output(JSON.stringify(pkgs, null, 2))
+ }
- const pkgs = await libaccess.lsPackages(owner, opts)
+ get ['ls-collaborators'] () {
+ return this.lsCollaborators
+ }
- // TODO - print these out nicely (breaking change)
- output(JSON.stringify(pkgs, null, 2))
-}
+ async lsCollaborators ([pkg, usr], opts) {
+ const pkgName = await this.getPackage(pkg, false)
+ const collabs = await libaccess.lsCollaborators(pkgName, usr, opts)
-access['ls-collaborators'] = access.lsCollaborators = async ([pkg, usr], opts) => {
- const pkgName = await getPackage(pkg, false)
- const collabs = await libaccess.lsCollaborators(pkgName, usr, opts)
+ // TODO - print these out nicely (breaking change)
+ output(JSON.stringify(collabs, null, 2))
+ }
- // TODO - print these out nicely (breaking change)
- output(JSON.stringify(collabs, null, 2))
-}
+ async edit () {
+ throw new Error('edit subcommand is not implemented yet')
+ }
-access.edit = () =>
- Promise.reject(new Error('edit subcommand is not implemented yet'))
-
-const modifyPackage = (pkg, opts, fn, requireScope = true) =>
- getPackage(pkg, requireScope)
- .then(pkgName => otplease(opts, opts => fn(pkgName, opts)))
-
-const getPackage = async (name, requireScope) => {
- if (name && name.trim())
- return name.trim()
- else {
- try {
- const pkg = await readPackageJson(path.resolve(npm.prefix, 'package.json'))
- name = pkg.name
- } catch (err) {
- if (err.code === 'ENOENT') {
- throw new Error(
- 'no package name passed to command and no package.json found'
- )
- } else
- throw err
+ modifyPackage (pkg, opts, fn, requireScope = true) {
+ return this.getPackage(pkg, requireScope)
+ .then(pkgName => otplease(opts, opts => fn(pkgName, opts)))
+ }
+
+ async getPackage (name, requireScope) {
+ if (name && name.trim())
+ return name.trim()
+ else {
+ try {
+ const pkg = await readPackageJson(path.resolve(this.npm.prefix, 'package.json'))
+ name = pkg.name
+ } catch (err) {
+ if (err.code === 'ENOENT') {
+ throw new Error(
+ 'no package name passed to command and no package.json found'
+ )
+ } else
+ throw err
+ }
+
+ if (requireScope && !name.match(/^@[^/]+\/.*$/))
+ throw this.usageError('This command is only available for scoped packages.')
+ else
+ return name
}
+ }
- if (requireScope && !name.match(/^@[^/]+\/.*$/))
- throw UsageError('This command is only available for scoped packages.')
- else
- return name
+ usageError (msg) {
+ return Object.assign(new Error(`\nUsage: ${msg}\n\n` + this.usage), {
+ code: 'EUSAGE',
+ })
}
}
-module.exports = Object.assign(cmd, { usage, completion, subcommands })
+module.exports = Access
diff --git a/deps/npm/lib/adduser.js b/deps/npm/lib/adduser.js
index c68c2b80f8..dac0f5a468 100644
--- a/deps/npm/lib/adduser.js
+++ b/deps/npm/lib/adduser.js
@@ -1,5 +1,4 @@
const log = require('npmlog')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
const replaceInfo = require('./utils/replace-info.js')
@@ -10,66 +9,76 @@ const authTypes = {
sso: require('./auth/sso.js'),
}
-const usage = usageUtil(
- 'adduser',
- 'npm adduser [--registry=url] [--scope=@orgname] [--always-auth]'
-)
+class AddUser {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => adduser(args).then(() => cb()).catch(cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'adduser',
+ 'npm adduser [--registry=url] [--scope=@orgname] [--always-auth]'
+ )
+ }
-const getRegistry = ({ scope, registry }) => {
- if (scope) {
- const scopedRegistry = npm.config.get(`${scope}:registry`)
- const cliRegistry = npm.config.get('registry', 'cli')
- if (scopedRegistry && !cliRegistry)
- return scopedRegistry
+ exec (args, cb) {
+ this.adduser(args).then(() => cb()).catch(cb)
}
- return registry
-}
-const getAuthType = ({ authType }) => {
- const type = authTypes[authType]
+ async adduser (args) {
+ const { scope } = this.npm.flatOptions
+ const registry = this.getRegistry(this.npm.flatOptions)
+ const auth = this.getAuthType(this.npm.flatOptions)
+ const creds = this.npm.config.getCredentialsByURI(registry)
- if (!type)
- throw new Error('no such auth module')
+ log.disableProgress()
- return type
-}
+ log.notice('', `Log in on ${replaceInfo(registry)}`)
-const updateConfig = async ({ newCreds, registry, scope }) => {
- npm.config.delete('_token', 'user') // prevent legacy pollution
+ const { message, newCreds } = await auth(this.npm, {
+ ...this.npm.flatOptions,
+ creds,
+ registry,
+ scope,
+ })
- if (scope)
- npm.config.set(scope + ':registry', registry, 'user')
+ await this.updateConfig({
+ newCreds,
+ registry,
+ scope,
+ })
- npm.config.setCredentialsByURI(registry, newCreds)
- await npm.config.save('user')
-}
+ output(message)
+ }
-const adduser = async (args) => {
- const { scope } = npm.flatOptions
- const registry = getRegistry(npm.flatOptions)
- const auth = getAuthType(npm.flatOptions)
- const creds = npm.config.getCredentialsByURI(registry)
+ getRegistry ({ scope, registry }) {
+ if (scope) {
+ const scopedRegistry = this.npm.config.get(`${scope}:registry`)
+ const cliRegistry = this.npm.config.get('registry', 'cli')
+ if (scopedRegistry && !cliRegistry)
+ return scopedRegistry
+ }
+ return registry
+ }
- log.disableProgress()
+ getAuthType ({ authType }) {
+ const type = authTypes[authType]
- log.notice('', `Log in on ${replaceInfo(registry)}`)
+ if (!type)
+ throw new Error('no such auth module')
- const { message, newCreds } = await auth({
- ...npm.flatOptions,
- creds,
- registry,
- scope,
- })
+ return type
+ }
- await updateConfig({
- newCreds,
- registry,
- scope,
- })
+ async updateConfig ({ newCreds, registry, scope }) {
+ this.npm.config.delete('_token', 'user') // prevent legacy pollution
- output(message)
-}
+ if (scope)
+ this.npm.config.set(scope + ':registry', registry, 'user')
-module.exports = Object.assign(cmd, { usage })
+ this.npm.config.setCredentialsByURI(registry, newCreds)
+ await this.npm.config.save('user')
+ }
+}
+module.exports = AddUser
diff --git a/deps/npm/lib/audit.js b/deps/npm/lib/audit.js
index 1b31401b1a..dfa01cb270 100644
--- a/deps/npm/lib/audit.js
+++ b/deps/npm/lib/audit.js
@@ -1,55 +1,65 @@
const Arborist = require('@npmcli/arborist')
const auditReport = require('npm-audit-report')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const reifyFinish = require('./utils/reify-finish.js')
const auditError = require('./utils/audit-error.js')
+const usageUtil = require('./utils/usage.js')
-const audit = async args => {
- const arb = new Arborist({
- ...npm.flatOptions,
- audit: true,
- path: npm.prefix,
- })
- const fix = args[0] === 'fix'
- await arb.audit({ fix })
- if (fix)
- await reifyFinish(arb)
- else {
- // will throw if there's an error, because this is an audit command
- auditError(arb.auditReport)
- const reporter = npm.flatOptions.json ? 'json' : 'detail'
- const result = auditReport(arb.auditReport, {
- ...npm.flatOptions,
- reporter,
- })
- process.exitCode = process.exitCode || result.exitCode
- output(result.report)
+class Audit {
+ constructor (npm) {
+ this.npm = npm
}
-}
-const cmd = (args, cb) => audit(args).then(() => cb()).catch(cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'audit',
+ 'npm audit [--json] [--production]' +
+ '\nnpm audit fix ' +
+ '[--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)]'
+ )
+ }
-const usageUtil = require('./utils/usage')
-const usage = usageUtil(
- 'audit',
- 'npm audit [--json] [--production]' +
- '\nnpm audit fix ' +
- '[--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)]'
-)
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
+ if (argv.length === 2)
+ return ['fix']
- if (argv.length === 2)
- return ['fix']
+ switch (argv[2]) {
+ case 'fix':
+ return []
+ default:
+ throw new Error(argv[2] + ' not recognized')
+ }
+ }
- switch (argv[2]) {
- case 'fix':
- return []
- default:
- throw new Error(argv[2] + ' not recognized')
+ exec (args, cb) {
+ this.audit(args).then(() => cb()).catch(cb)
+ }
+
+ async audit (args) {
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ audit: true,
+ path: this.npm.prefix,
+ })
+ const fix = args[0] === 'fix'
+ await arb.audit({ fix })
+ if (fix)
+ await reifyFinish(this.npm, arb)
+ else {
+ // will throw if there's an error, because this is an audit command
+ auditError(this.npm, arb.auditReport)
+ const reporter = this.npm.flatOptions.json ? 'json' : 'detail'
+ const result = auditReport(arb.auditReport, {
+ ...this.npm.flatOptions,
+ reporter,
+ })
+ process.exitCode = process.exitCode || result.exitCode
+ output(result.report)
+ }
}
}
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = Audit
diff --git a/deps/npm/lib/auth/legacy.js b/deps/npm/lib/auth/legacy.js
index f291ca794e..8659446dc4 100644
--- a/deps/npm/lib/auth/legacy.js
+++ b/deps/npm/lib/auth/legacy.js
@@ -4,11 +4,6 @@ const profile = require('npm-profile')
const openUrl = require('../utils/open-url.js')
const read = require('../utils/read-user-info.js')
-// TODO: refactor lib/utils/open-url and its usages
-const openerPromise = (url) => new Promise((resolve, reject) => {
- openUrl(url, 'to complete your login please visit', (er) => er ? reject(er) : resolve())
-})
-
const loginPrompter = async (creds) => {
const opts = { log: log }
@@ -19,7 +14,7 @@ const loginPrompter = async (creds) => {
return creds
}
-const login = async (opts) => {
+const login = async (npm, opts) => {
let res
const requestOTP = async () => {
@@ -54,6 +49,7 @@ const login = async (opts) => {
return newUser
}
+ const openerPromise = (url) => openUrl(npm, url, 'to complete your login please visit')
try {
res = await profile.login(openerPromise, loginPrompter, opts)
} catch (err) {
diff --git a/deps/npm/lib/auth/oauth.js b/deps/npm/lib/auth/oauth.js
index ee45317113..99c2ca0ca0 100644
--- a/deps/npm/lib/auth/oauth.js
+++ b/deps/npm/lib/auth/oauth.js
@@ -1,9 +1,8 @@
const sso = require('./sso.js')
-const npm = require('../npm.js')
-const login = (opts) => {
+const login = (npm, opts) => {
npm.config.set('sso-type', 'oauth')
- return sso(opts)
+ return sso(npm, opts)
}
module.exports = login
diff --git a/deps/npm/lib/auth/saml.js b/deps/npm/lib/auth/saml.js
index f30d82849d..3dd31ca013 100644
--- a/deps/npm/lib/auth/saml.js
+++ b/deps/npm/lib/auth/saml.js
@@ -1,9 +1,8 @@
const sso = require('./sso.js')
-const npm = require('../npm.js')
-const login = (opts) => {
+const login = (npm, opts) => {
npm.config.set('sso-type', 'saml')
- return sso(opts)
+ return sso(npm, opts)
}
module.exports = login
diff --git a/deps/npm/lib/auth/sso.js b/deps/npm/lib/auth/sso.js
index 378295f5f6..56cff3c06e 100644
--- a/deps/npm/lib/auth/sso.js
+++ b/deps/npm/lib/auth/sso.js
@@ -7,14 +7,11 @@
// CLI, we can remove this, and fold the lib/auth/legacy.js back into
// lib/adduser.js
-const { promisify } = require('util')
-
const log = require('npmlog')
const profile = require('npm-profile')
const npmFetch = require('npm-registry-fetch')
-const npm = require('../npm.js')
-const openUrl = promisify(require('../utils/open-url.js'))
+const openUrl = require('../utils/open-url.js')
const otplease = require('../utils/otplease.js')
const pollForSession = ({ registry, token, opts }) => {
@@ -38,7 +35,7 @@ function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time))
}
-const login = async ({ creds, registry, scope }) => {
+const login = async (npm, { creds, registry, scope }) => {
log.warn('deprecated', 'SSO --auth-type is deprecated')
const opts = { ...npm.flatOptions, creds, registry, scope }
@@ -65,7 +62,7 @@ const login = async ({ creds, registry, scope }) => {
if (!sso)
throw new Error('no SSO URL returned by services')
- await openUrl(sso, 'to complete your login please visit')
+ await openUrl(npm, sso, 'to complete your login please visit')
const username = await pollForSession({ registry, token, opts })
diff --git a/deps/npm/lib/bin.js b/deps/npm/lib/bin.js
index e627ce22f1..11490c41cb 100644
--- a/deps/npm/lib/bin.js
+++ b/deps/npm/lib/bin.js
@@ -1,13 +1,26 @@
-const npm = require('./npm.js')
const output = require('./utils/output.js')
+const envPath = require('./utils/path.js')
const usageUtil = require('./utils/usage.js')
-const PATH = require('./utils/path.js')
-const cmd = (args, cb) => bin(args).then(() => cb()).catch(cb)
-const usage = usageUtil('bin', 'npm bin [-g]')
-const bin = async (args, cb) => {
- const b = npm.bin
- output(b)
- if (npm.flatOptions.global && !PATH.includes(b))
- console.error('(not in PATH env variable)')
+
+class Bin {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('bin', 'npm bin [-g]')
+ }
+
+ exec (args, cb) {
+ this.bin(args).then(() => cb()).catch(cb)
+ }
+
+ async bin (args) {
+ const b = this.npm.bin
+ output(b)
+ if (this.npm.flatOptions.global && !envPath.includes(b))
+ console.error('(not in PATH env variable)')
+ }
}
-module.exports = Object.assign(cmd, { usage })
+module.exports = Bin
diff --git a/deps/npm/lib/birthday.js b/deps/npm/lib/birthday.js
index 6c71a9e715..5ea855512f 100644
--- a/deps/npm/lib/birthday.js
+++ b/deps/npm/lib/birthday.js
@@ -1,11 +1,18 @@
-const npm = require('./npm.js')
-module.exports = (_, cb) => {
- Object.defineProperty(npm, 'flatOptions', {
- value: {
- ...npm.flatOptions,
- package: ['@npmcli/npm-birthday'],
- yes: true,
- },
- })
- return npm.commands.exec(['npm-birthday'], cb)
+class Birthday {
+ constructor (npm) {
+ this.npm = npm
+ Object.defineProperty(this.npm, 'flatOptions', {
+ value: {
+ ...npm.flatOptions,
+ package: ['@npmcli/npm-birthday'],
+ yes: true,
+ },
+ })
+ }
+
+ exec (args, cb) {
+ return this.npm.commands.exec(['npm-birthday'], cb)
+ }
}
+
+module.exports = Birthday
diff --git a/deps/npm/lib/bugs.js b/deps/npm/lib/bugs.js
index 09856313ce..fb0d7c9277 100644
--- a/deps/npm/lib/bugs.js
+++ b/deps/npm/lib/bugs.js
@@ -1,46 +1,55 @@
const log = require('npmlog')
const pacote = require('pacote')
-const { promisify } = require('util')
-const openUrl = promisify(require('./utils/open-url.js'))
+const openUrl = require('./utils/open-url.js')
const usageUtil = require('./utils/usage.js')
-const npm = require('./npm.js')
const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js')
-const usage = usageUtil('bugs', 'npm bugs [<pkgname>]')
+class Bugs {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => bugs(args).then(() => cb()).catch(cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('bugs', 'npm bugs [<pkgname>]')
+ }
-const bugs = async args => {
- if (!args || !args.length)
- args = ['.']
+ exec (args, cb) {
+ this.bugs(args).then(() => cb()).catch(cb)
+ }
- await Promise.all(args.map(pkg => getBugs(pkg)))
-}
+ async bugs (args) {
+ if (!args || !args.length)
+ args = ['.']
-const getBugsUrl = mani => {
- if (mani.bugs) {
- if (typeof mani.bugs === 'string')
- return mani.bugs
+ await Promise.all(args.map(pkg => this.getBugs(pkg)))
+ }
- if (typeof mani.bugs === 'object' && mani.bugs.url)
- return mani.bugs.url
+ async getBugs (pkg) {
+ const opts = { ...this.npm.flatOptions, fullMetadata: true }
+ const mani = await pacote.manifest(pkg, opts)
+ const url = this.getBugsUrl(mani)
+ log.silly('bugs', 'url', url)
+ await openUrl(this.npm, url, `${mani.name} bug list available at the following URL`)
}
- // try to get it from the repo, if possible
- const info = hostedFromMani(mani)
- if (info)
- return info.bugs()
+ getBugsUrl (mani) {
+ if (mani.bugs) {
+ if (typeof mani.bugs === 'string')
+ return mani.bugs
- // just send them to the website, hopefully that has some info!
- return `https://www.npmjs.com/package/${mani.name}`
-}
+ if (typeof mani.bugs === 'object' && mani.bugs.url)
+ return mani.bugs.url
+ }
-const getBugs = async pkg => {
- const opts = { ...npm.flatOptions, fullMetadata: true }
- const mani = await pacote.manifest(pkg, opts)
- const url = getBugsUrl(mani)
- log.silly('bugs', 'url', url)
- await openUrl(url, `${mani.name} bug list available at the following URL`)
+ // try to get it from the repo, if possible
+ const info = hostedFromMani(mani)
+ if (info)
+ return info.bugs()
+
+ // just send them to the website, hopefully that has some info!
+ return `https://www.npmjs.com/package/${mani.name}`
+ }
}
-module.exports = Object.assign(cmd, { usage })
+module.exports = Bugs
diff --git a/deps/npm/lib/cache.js b/deps/npm/lib/cache.js
index 7b84353b4a..8469559764 100644
--- a/deps/npm/lib/cache.js
+++ b/deps/npm/lib/cache.js
@@ -1,62 +1,69 @@
const cacache = require('cacache')
const { promisify } = require('util')
const log = require('npmlog')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const pacote = require('pacote')
const path = require('path')
const rimraf = promisify(require('rimraf'))
const usageUtil = require('./utils/usage.js')
+class Cache {
+ constructor (npm) {
+ this.npm = npm
+ }
-const usage = usageUtil('cache',
- 'npm cache add <tarball file>' +
- '\nnpm cache add <folder>' +
- '\nnpm cache add <tarball url>' +
- '\nnpm cache add <git url>' +
- '\nnpm cache add <name>@<version>' +
- '\nnpm cache clean' +
- '\nnpm cache verify'
-)
-
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
- if (argv.length === 2)
- return ['add', 'clean', 'verify']
-
- // TODO - eventually...
- switch (argv[2]) {
- case 'verify':
- case 'clean':
- case 'add':
- return []
+ get usage () {
+ return usageUtil('cache',
+ 'npm cache add <tarball file>' +
+ '\nnpm cache add <folder>' +
+ '\nnpm cache add <tarball url>' +
+ '\nnpm cache add <git url>' +
+ '\nnpm cache add <name>@<version>' +
+ '\nnpm cache clean' +
+ '\nnpm cache verify'
+ )
}
-}
-const cmd = (args, cb) => cache(args).then(() => cb()).catch(cb)
-
-const cache = async (args) => {
- const cmd = args.shift()
- switch (cmd) {
- case 'rm': case 'clear': case 'clean':
- return await clean(args)
- case 'add':
- return await add(args)
- case 'verify': case 'check':
- return await verify()
- default:
- throw Object.assign(new Error(usage), { code: 'EUSAGE' })
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv.length === 2)
+ return ['add', 'clean', 'verify']
+
+ // TODO - eventually...
+ switch (argv[2]) {
+ case 'verify':
+ case 'clean':
+ case 'add':
+ return []
+ }
+ }
+
+ exec (args, cb) {
+ this.cache(args).then(() => cb()).catch(cb)
+ }
+
+ async cache (args) {
+ const cmd = args.shift()
+ switch (cmd) {
+ case 'rm': case 'clear': case 'clean':
+ return await this.clean(args)
+ case 'add':
+ return await this.add(args)
+ case 'verify': case 'check':
+ return await this.verify()
+ default:
+ throw Object.assign(new Error(this.usage), { code: 'EUSAGE' })
+ }
}
-}
-// npm cache clean [pkg]*
-const clean = async (args) => {
- if (args.length)
- throw new Error('npm cache clear does not accept arguments')
+ // npm cache clean [pkg]*
+ async clean (args) {
+ if (args.length)
+ throw new Error('npm cache clear does not accept arguments')
- const cachePath = path.join(npm.cache, '_cacache')
- if (!npm.flatOptions.force) {
- throw new Error(`As of npm@5, the npm cache self-heals from corruption issues
+ const cachePath = path.join(this.npm.cache, '_cacache')
+ if (!this.npm.flatOptions.force) {
+ throw new Error(`As of npm@5, the npm cache self-heals from corruption issues
by treating integrity mismatches as cache misses. As a result,
data extracted from the cache is guaranteed to be valid. If you
want to make sure everything is consistent, use \`npm cache verify\`
@@ -70,52 +77,53 @@ temporary cache instead of nuking the actual one.
If you're sure you want to delete the entire cache, rerun this command
with --force.`)
+ }
+ return rimraf(cachePath)
}
- return rimraf(cachePath)
-}
-// npm cache add <tarball-url>
-// npm cache add <pkg> <ver>
-// npm cache add <tarball>
-// npm cache add <folder>
-const add = async (args) => {
- const usage = 'Usage:\n' +
- ' npm cache add <tarball-url>\n' +
- ' npm cache add <pkg>@<ver>\n' +
- ' npm cache add <tarball>\n' +
- ' npm cache add <folder>\n'
- log.silly('cache add', 'args', args)
- const spec = args[0] && args[0] +
- (args[1] === undefined || args[1] === null ? '' : `@${args[1]}`)
-
- if (!spec)
- throw Object.assign(new Error(usage), { code: 'EUSAGE' })
-
- log.silly('cache add', 'spec', spec)
- const opts = { ...npm.flatOptions }
-
- // we ask pacote for the thing, and then just throw the data
- // away so that it tee-pipes it into the cache like it does
- // for a normal request.
- await pacote.tarball.stream(spec, stream => {
- stream.resume()
- return stream.promise()
- }, opts)
-}
+ // npm cache add <tarball-url>
+ // npm cache add <pkg> <ver>
+ // npm cache add <tarball>
+ // npm cache add <folder>
+ async add (args) {
+ const usage = 'Usage:\n' +
+ ' npm cache add <tarball-url>\n' +
+ ' npm cache add <pkg>@<ver>\n' +
+ ' npm cache add <tarball>\n' +
+ ' npm cache add <folder>\n'
+ log.silly('cache add', 'args', args)
+ const spec = args[0] && args[0] +
+ (args[1] === undefined || args[1] === null ? '' : `@${args[1]}`)
+
+ if (!spec)
+ throw Object.assign(new Error(usage), { code: 'EUSAGE' })
+
+ log.silly('cache add', 'spec', spec)
+ const opts = { ...this.npm.flatOptions }
-const verify = async () => {
- const cache = path.join(npm.cache, '_cacache')
- const prefix = cache.indexOf(process.env.HOME) === 0
- ? `~${cache.substr(process.env.HOME.length)}`
- : cache
- const stats = await cacache.verify(cache)
- output(`Cache verified and compressed (${prefix})`)
- output(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`)
- stats.badContentCount && output(`Corrupted content removed: ${stats.badContentCount}`)
- stats.reclaimedCount && output(`Content garbage-collected: ${stats.reclaimedCount} (${stats.reclaimedSize} bytes)`)
- stats.missingContent && output(`Missing content: ${stats.missingContent}`)
- output(`Index entries: ${stats.totalEntries}`)
- output(`Finished in ${stats.runTime.total / 1000}s`)
+ // we ask pacote for the thing, and then just throw the data
+ // away so that it tee-pipes it into the cache like it does
+ // for a normal request.
+ await pacote.tarball.stream(spec, stream => {
+ stream.resume()
+ return stream.promise()
+ }, opts)
+ }
+
+ async verify () {
+ const cache = path.join(this.npm.cache, '_cacache')
+ const prefix = cache.indexOf(process.env.HOME) === 0
+ ? `~${cache.substr(process.env.HOME.length)}`
+ : cache
+ const stats = await cacache.verify(cache)
+ output(`Cache verified and compressed (${prefix})`)
+ output(`Content verified: ${stats.verifiedContent} (${stats.keptSize} bytes)`)
+ stats.badContentCount && output(`Corrupted content removed: ${stats.badContentCount}`)
+ stats.reclaimedCount && output(`Content garbage-collected: ${stats.reclaimedCount} (${stats.reclaimedSize} bytes)`)
+ stats.missingContent && output(`Missing content: ${stats.missingContent}`)
+ output(`Index entries: ${stats.totalEntries}`)
+ output(`Finished in ${stats.runTime.total / 1000}s`)
+ }
}
-module.exports = Object.assign(cmd, { completion, usage })
+module.exports = Cache
diff --git a/deps/npm/lib/ci.js b/deps/npm/lib/ci.js
index 51c165acce..03a91a6046 100644
--- a/deps/npm/lib/ci.js
+++ b/deps/npm/lib/ci.js
@@ -7,13 +7,8 @@ const fs = require('fs')
const readdir = util.promisify(fs.readdir)
const log = require('npmlog')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('ci', 'npm ci')
-
-const cmd = (args, cb) => ci().then(() => cb()).catch(cb)
-
const removeNodeModules = async where => {
const rimrafOpts = { glob: false }
process.emit('time', 'npm-ci:rm')
@@ -24,55 +19,70 @@ const removeNodeModules = async where => {
process.emit('timeEnd', 'npm-ci:rm')
}
-const ci = async () => {
- if (npm.flatOptions.global) {
- const err = new Error('`npm ci` does not work for global packages')
- err.code = 'ECIGLOBAL'
- throw err
+class CI {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('ci', 'npm ci')
}
- const where = npm.prefix
- const { scriptShell, ignoreScripts } = npm.flatOptions
- const arb = new Arborist({ ...npm.flatOptions, path: where })
+ exec (args, cb) {
+ this.ci().then(() => cb()).catch(cb)
+ }
+
+ async ci () {
+ if (this.npm.flatOptions.global) {
+ const err = new Error('`npm ci` does not work for global packages')
+ err.code = 'ECIGLOBAL'
+ throw err
+ }
+
+ const where = this.npm.prefix
+ const { scriptShell, ignoreScripts } = this.npm.flatOptions
+ const arb = new Arborist({ ...this.npm.flatOptions, path: where })
- await Promise.all([
- arb.loadVirtual().catch(er => {
- log.verbose('loadVirtual', er.stack)
- const msg =
- 'The `npm ci` command can only install with an existing package-lock.json or\n' +
- 'npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or\n' +
- 'later to generate a package-lock.json file, then try again.'
- throw new Error(msg)
- }),
- removeNodeModules(where),
- ])
- // npm ci should never modify the lockfile or package.json
- await arb.reify({ ...npm.flatOptions, save: false })
+ await Promise.all([
+ arb.loadVirtual().catch(er => {
+ log.verbose('loadVirtual', er.stack)
+ const msg =
+ 'The `npm ci` command can only install with an existing package-lock.json or\n' +
+ 'npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or\n' +
+ 'later to generate a package-lock.json file, then try again.'
+ throw new Error(msg)
+ }),
+ removeNodeModules(where),
+ ])
+ // npm ci should never modify the lockfile or package.json
+ await arb.reify({ ...this.npm.flatOptions, save: false })
- // run the same set of scripts that `npm install` runs.
- if (!ignoreScripts) {
- const scripts = [
- 'preinstall',
- 'install',
- 'postinstall',
- 'prepublish', // XXX should we remove this finally??
- 'preprepare',
- 'prepare',
- 'postprepare',
- ]
- for (const event of scripts) {
- await runScript({
- path: where,
- args: [],
- scriptShell,
- stdio: 'inherit',
- stdioString: true,
- banner: log.level !== 'silent',
- event,
- })
+ // run the same set of scripts that `npm install` runs.
+ if (!ignoreScripts) {
+ const scripts = [
+ 'preinstall',
+ 'install',
+ 'postinstall',
+ 'prepublish', // XXX should we remove this finally??
+ 'preprepare',
+ 'prepare',
+ 'postprepare',
+ ]
+ for (const event of scripts) {
+ await runScript({
+ path: where,
+ args: [],
+ scriptShell,
+ stdio: 'inherit',
+ stdioString: true,
+ banner: log.level !== 'silent',
+ event,
+ })
+ }
}
+ await reifyFinish(this.npm, arb)
}
- await reifyFinish(arb)
}
-module.exports = Object.assign(cmd, {usage})
+module.exports = CI
diff --git a/deps/npm/lib/completion.js b/deps/npm/lib/completion.js
index b31867d988..4c37e6ef35 100644
--- a/deps/npm/lib/completion.js
+++ b/deps/npm/lib/completion.js
@@ -29,7 +29,6 @@
// as an array.
//
-const npm = require('./npm.js')
const { types, shorthands } = require('./utils/config.js')
const deref = require('./utils/deref-command.js')
const { aliases, cmdList, plumbing } = require('./utils/cmd-list.js')
@@ -44,115 +43,127 @@ const output = require('./utils/output.js')
const fileExists = require('./utils/file-exists.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('completion', 'source <(npm completion)')
const { promisify } = require('util')
-const cmd = (args, cb) => compl(args).then(() => cb()).catch(cb)
+class Completion {
+ constructor (npm) {
+ this.npm = npm
+ }
-// completion for the completion command
-const completion = async (opts) => {
- if (opts.w > 2)
- return
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('completion', 'source <(npm completion)')
+ }
- const { resolve } = require('path')
- const [bashExists, zshExists] = await Promise.all([
- fileExists(resolve(process.env.HOME, '.bashrc')),
- fileExists(resolve(process.env.HOME, '.zshrc')),
- ])
- const out = []
- if (zshExists)
- out.push(['>>', '~/.zshrc'])
-
- if (bashExists)
- out.push(['>>', '~/.bashrc'])
-
- return out
-}
+ // completion for the completion command
+ async completion (opts) {
+ if (opts.w > 2)
+ return
-const compl = async args => {
- if (isWindowsShell) {
- const msg = 'npm completion supported only in MINGW / Git bash on Windows'
- throw Object.assign(new Error(msg), {
- code: 'ENOTSUP',
- })
+ const { resolve } = require('path')
+ const [bashExists, zshExists] = await Promise.all([
+ fileExists(resolve(process.env.HOME, '.bashrc')),
+ fileExists(resolve(process.env.HOME, '.zshrc')),
+ ])
+ const out = []
+ if (zshExists)
+ out.push(['>>', '~/.zshrc'])
+
+ if (bashExists)
+ out.push(['>>', '~/.bashrc'])
+
+ return out
}
- const { COMP_CWORD, COMP_LINE, COMP_POINT } = process.env
+ exec (args, cb) {
+ this.compl(args).then(() => cb()).catch(cb)
+ }
- // if the COMP_* isn't in the env, then just dump the script.
- if (COMP_CWORD === undefined ||
+ async compl (args) {
+ if (isWindowsShell) {
+ const msg = 'npm completion supported only in MINGW / Git bash on Windows'
+ throw Object.assign(new Error(msg), {
+ code: 'ENOTSUP',
+ })
+ }
+
+ const { COMP_CWORD, COMP_LINE, COMP_POINT } = process.env
+
+ // if the COMP_* isn't in the env, then just dump the script.
+ if (COMP_CWORD === undefined ||
COMP_LINE === undefined ||
COMP_POINT === undefined)
- return dumpScript()
-
- // ok we're actually looking at the envs and outputting the suggestions
- // get the partial line and partial word,
- // if the point isn't at the end.
- // ie, tabbing at: npm foo b|ar
- const w = +COMP_CWORD
- const words = args.map(unescape)
- const word = words[w]
- const line = COMP_LINE
- const point = +COMP_POINT
- const partialLine = line.substr(0, point)
- const partialWords = words.slice(0, w)
-
- // figure out where in that last word the point is.
- const partialWordRaw = args[w]
- let i = partialWordRaw.length
- while (partialWordRaw.substr(0, i) !== partialLine.substr(-1 * i) && i > 0)
- i--
-
- const partialWord = unescape(partialWordRaw.substr(0, i))
- partialWords.push(partialWord)
-
- const opts = {
- words,
- w,
- word,
- line,
- lineLength: line.length,
- point,
- partialLine,
- partialWords,
- partialWord,
- raw: args,
- }
+ return dumpScript()
+
+ // ok we're actually looking at the envs and outputting the suggestions
+ // get the partial line and partial word,
+ // if the point isn't at the end.
+ // ie, tabbing at: npm foo b|ar
+ const w = +COMP_CWORD
+ const words = args.map(unescape)
+ const word = words[w]
+ const line = COMP_LINE
+ const point = +COMP_POINT
+ const partialLine = line.substr(0, point)
+ const partialWords = words.slice(0, w)
+
+ // figure out where in that last word the point is.
+ const partialWordRaw = args[w]
+ let i = partialWordRaw.length
+ while (partialWordRaw.substr(0, i) !== partialLine.substr(-1 * i) && i > 0)
+ i--
+
+ const partialWord = unescape(partialWordRaw.substr(0, i))
+ partialWords.push(partialWord)
+
+ const opts = {
+ words,
+ w,
+ word,
+ line,
+ lineLength: line.length,
+ point,
+ partialLine,
+ partialWords,
+ partialWord,
+ raw: args,
+ }
- if (partialWords.slice(0, -1).indexOf('--') === -1) {
- if (word.charAt(0) === '-')
- return wrap(opts, configCompl(opts))
+ if (partialWords.slice(0, -1).indexOf('--') === -1) {
+ if (word.charAt(0) === '-')
+ return wrap(opts, configCompl(opts))
- if (words[w - 1] &&
+ if (words[w - 1] &&
words[w - 1].charAt(0) === '-' &&
!isFlag(words[w - 1])) {
- // awaiting a value for a non-bool config.
- // don't even try to do this for now
- return wrap(opts, configValueCompl(opts))
+ // awaiting a value for a non-bool config.
+ // don't even try to do this for now
+ return wrap(opts, configValueCompl(opts))
+ }
}
- }
- // try to find the npm command.
- // it's the first thing after all the configs.
- // take a little shortcut and use npm's arg parsing logic.
- // don't have to worry about the last arg being implicitly
- // boolean'ed, since the last block will catch that.
- const parsed = opts.conf =
- nopt(types, shorthands, partialWords.slice(0, -1), 0)
- // check if there's a command already.
- const cmd = parsed.argv.remain[1]
- if (!cmd)
- return wrap(opts, cmdCompl(opts))
-
- Object.keys(parsed).forEach(k => npm.config.set(k, parsed[k]))
-
- // at this point, if words[1] is some kind of npm command,
- // then complete on it.
- // otherwise, do nothing
- const impl = npm.commands[cmd]
- if (impl && impl.completion) {
- const comps = await impl.completion(opts)
- return wrap(opts, comps)
+ // try to find the npm command.
+ // it's the first thing after all the configs.
+ // take a little shortcut and use npm's arg parsing logic.
+ // don't have to worry about the last arg being implicitly
+ // boolean'ed, since the last block will catch that.
+ const parsed = opts.conf =
+ nopt(types, shorthands, partialWords.slice(0, -1), 0)
+ // check if there's a command already.
+ const cmd = parsed.argv.remain[1]
+ if (!cmd)
+ return wrap(opts, cmdCompl(opts))
+
+ Object.keys(parsed).forEach(k => this.npm.config.set(k, parsed[k]))
+
+ // at this point, if words[1] is some kind of npm command,
+ // then complete on it.
+ // otherwise, do nothing
+ const impl = this.npm.commands[cmd]
+ if (impl && impl.completion) {
+ const comps = await impl.completion(opts)
+ return wrap(opts, comps)
+ }
}
}
@@ -266,4 +277,4 @@ const cmdCompl = opts => {
return fullList
}
-module.exports = Object.assign(cmd, { completion, usage })
+module.exports = Completion
diff --git a/deps/npm/lib/config.js b/deps/npm/lib/config.js
index e4da296de8..2805db9b80 100644
--- a/deps/npm/lib/config.js
+++ b/deps/npm/lib/config.js
@@ -1,4 +1,3 @@
-const npm = require('./npm.js')
const { defaults, types } = require('./utils/config.js')
const usageUtil = require('./utils/usage.js')
const output = require('./utils/output.js')
@@ -13,165 +12,173 @@ const { spawn } = require('child_process')
const { EOL } = require('os')
const ini = require('ini')
-const usage = usageUtil(
- 'config',
- 'npm config set <key>=<value> [<key>=<value> ...]' +
- '\nnpm config get [<key> [<key> ...]]' +
- '\nnpm config delete <key> [<key> ...]' +
- '\nnpm config list [--json]' +
- '\nnpm config edit' +
- '\nnpm set <key>=<value> [<key>=<value> ...]' +
- '\nnpm get [<key> [<key> ...]]'
-)
-
-const cmd = (args, cb) => config(args).then(() => cb()).catch(cb)
-
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
- if (argv[1] !== 'config')
- argv.unshift('config')
-
- if (argv.length === 2) {
- const cmds = ['get', 'set', 'delete', 'ls', 'rm', 'edit']
- if (opts.partialWord !== 'l')
- cmds.push('list')
-
- return cmds
+// take an array of `[key, value, k2=v2, k3, v3, ...]` and turn into
+// { key: value, k2: v2, k3: v3 }
+const keyValues = args => {
+ const kv = {}
+ for (let i = 0; i < args.length; i++) {
+ const arg = args[i].split('=')
+ const key = arg.shift()
+ const val = arg.length ? arg.join('=')
+ : i < args.length - 1 ? args[++i]
+ : ''
+ kv[key.trim()] = val.trim()
}
+ return kv
+}
- const action = argv[2]
- switch (action) {
- case 'set':
- // todo: complete with valid values, if possible.
- if (argv.length > 3)
- return []
+const publicVar = k => !/^(\/\/[^:]+:)?_/.test(k)
- // fallthrough
- /* eslint no-fallthrough:0 */
- case 'get':
- case 'delete':
- case 'rm':
- return Object.keys(types)
- case 'edit':
- case 'list':
- case 'ls':
- default:
- return []
+class Config {
+ constructor (npm) {
+ this.npm = npm
}
-}
-const UsageError = () =>
- Object.assign(new Error(usage), { code: 'EUSAGE' })
+ get usage () {
+ return usageUtil(
+ 'config',
+ 'npm config set <key>=<value> [<key>=<value> ...]' +
+ '\nnpm config get [<key> [<key> ...]]' +
+ '\nnpm config delete <key> [<key> ...]' +
+ '\nnpm config list [--json]' +
+ '\nnpm config edit' +
+ '\nnpm set <key>=<value> [<key>=<value> ...]' +
+ '\nnpm get [<key> [<key> ...]]'
+ )
+ }
+
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv[1] !== 'config')
+ argv.unshift('config')
+
+ if (argv.length === 2) {
+ const cmds = ['get', 'set', 'delete', 'ls', 'rm', 'edit']
+ if (opts.partialWord !== 'l')
+ cmds.push('list')
-const config = async ([action, ...args]) => {
- npm.log.disableProgress()
- try {
+ return cmds
+ }
+
+ const action = argv[2]
switch (action) {
case 'set':
- await set(args)
- break
+ // todo: complete with valid values, if possible.
+ if (argv.length > 3)
+ return []
+
+ // fallthrough
+ /* eslint no-fallthrough:0 */
case 'get':
- await get(args)
- break
case 'delete':
case 'rm':
- case 'del':
- await del(args)
- break
+ return Object.keys(types)
+ case 'edit':
case 'list':
case 'ls':
- await (npm.flatOptions.json ? listJson() : list())
- break
- case 'edit':
- await edit()
- break
default:
- throw UsageError()
+ return []
}
- } finally {
- npm.log.enableProgress()
}
-}
-// take an array of `[key, value, k2=v2, k3, v3, ...]` and turn into
-// { key: value, k2: v2, k3: v3 }
-const keyValues = args => {
- const kv = {}
- for (let i = 0; i < args.length; i++) {
- const arg = args[i].split('=')
- const key = arg.shift()
- const val = arg.length ? arg.join('=')
- : i < args.length - 1 ? args[++i]
- : ''
- kv[key.trim()] = val.trim()
+ exec (args, cb) {
+ this.config(args).then(() => cb()).catch(cb)
}
- return kv
-}
-const set = async (args) => {
- if (!args.length)
- throw UsageError()
-
- const where = npm.flatOptions.global ? 'global' : 'user'
- for (const [key, val] of Object.entries(keyValues(args))) {
- npm.log.info('config', 'set %j %j', key, val)
- npm.config.set(key, val || '', where)
- if (!npm.config.validate(where))
- npm.log.warn('config', 'omitting invalid config values')
+ async config ([action, ...args]) {
+ this.npm.log.disableProgress()
+ try {
+ switch (action) {
+ case 'set':
+ await this.set(args)
+ break
+ case 'get':
+ await this.get(args)
+ break
+ case 'delete':
+ case 'rm':
+ case 'del':
+ await this.del(args)
+ break
+ case 'list':
+ case 'ls':
+ await (this.npm.flatOptions.json ? this.listJson() : this.list())
+ break
+ case 'edit':
+ await this.edit()
+ break
+ default:
+ throw this.usageError()
+ }
+ } finally {
+ this.npm.log.enableProgress()
+ }
}
- await npm.config.save(where)
-}
+ async set (args) {
+ if (!args.length)
+ throw this.usageError()
+
+ const where = this.npm.flatOptions.global ? 'global' : 'user'
+ for (const [key, val] of Object.entries(keyValues(args))) {
+ this.npm.log.info('config', 'set %j %j', key, val)
+ this.npm.config.set(key, val || '', where)
+ if (!this.npm.config.validate(where))
+ this.npm.log.warn('config', 'omitting invalid config values')
+ }
+
+ await this.npm.config.save(where)
+ }
-const get = async keys => {
- if (!keys.length)
- return list()
+ async get (keys) {
+ if (!keys.length)
+ return this.list()
- const out = []
- for (const key of keys) {
- if (!publicVar(key))
- throw `The ${key} option is protected, and cannot be retrieved in this way`
+ const out = []
+ for (const key of keys) {
+ if (!publicVar(key))
+ throw `The ${key} option is protected, and cannot be retrieved in this way`
- const pref = keys.length > 1 ? `${key}=` : ''
- out.push(pref + npm.config.get(key))
+ const pref = keys.length > 1 ? `${key}=` : ''
+ out.push(pref + this.npm.config.get(key))
+ }
+ output(out.join('\n'))
}
- output(out.join('\n'))
-}
-const del = async keys => {
- if (!keys.length)
- throw UsageError()
+ async del (keys) {
+ if (!keys.length)
+ throw this.usageError()
- const where = npm.flatOptions.global ? 'global' : 'user'
- for (const key of keys)
- npm.config.delete(key, where)
- await npm.config.save(where)
-}
+ const where = this.npm.flatOptions.global ? 'global' : 'user'
+ for (const key of keys)
+ this.npm.config.delete(key, where)
+ await this.npm.config.save(where)
+ }
-const edit = async () => {
- const { editor: e, global } = npm.flatOptions
- const where = global ? 'global' : 'user'
- const file = npm.config.data.get(where).source
-
- // save first, just to make sure it's synced up
- // this also removes all the comments from the last time we edited it.
- await npm.config.save(where)
-
- const data = (
- await readFile(file, 'utf8').catch(() => '')
- ).replace(/\r\n/g, '\n')
- const defData = Object.entries(defaults).reduce((str, [key, val]) => {
- const obj = { [key]: val }
- const i = ini.stringify(obj)
- .replace(/\r\n/g, '\n') // normalizes output from ini.stringify
- .replace(/\n$/m, '')
- .replace(/^/g, '; ')
- .replace(/\n/g, '\n; ')
- .split('\n')
- return str + '\n' + i
- }, '')
-
- const tmpData = `;;;;
+ async edit () {
+ const { editor: e, global } = this.npm.flatOptions
+ const where = global ? 'global' : 'user'
+ const file = this.npm.config.data.get(where).source
+
+ // save first, just to make sure it's synced up
+ // this also removes all the comments from the last time we edited it.
+ await this.npm.config.save(where)
+
+ const data = (
+ await readFile(file, 'utf8').catch(() => '')
+ ).replace(/\r\n/g, '\n')
+ const defData = Object.entries(defaults).reduce((str, [key, val]) => {
+ const obj = { [key]: val }
+ const i = ini.stringify(obj)
+ .replace(/\r\n/g, '\n') // normalizes output from ini.stringify
+ .replace(/\n$/m, '')
+ .replace(/^/g, '; ')
+ .replace(/\n/g, '\n; ')
+ .split('\n')
+ return str + '\n' + i
+ }, '')
+
+ const tmpData = `;;;;
; npm ${where}config file: ${file}
; this is a simple ini-formatted file
; lines that start with semi-colons are comments
@@ -190,64 +197,67 @@ ${data.split('\n').sort((a, b) => a.localeCompare(b)).join('\n').trim()}
${defData}
`.split('\n').join(EOL)
- await mkdirp(dirname(file))
- await writeFile(file, tmpData, 'utf8')
- await new Promise((resolve, reject) => {
- const [bin, ...args] = e.split(/\s+/)
- const editor = spawn(bin, [...args, file], { stdio: 'inherit' })
- editor.on('exit', (code) => {
- if (code)
- return reject(new Error(`editor process exited with code: ${code}`))
- return resolve()
+ await mkdirp(dirname(file))
+ await writeFile(file, tmpData, 'utf8')
+ await new Promise((resolve, reject) => {
+ const [bin, ...args] = e.split(/\s+/)
+ const editor = spawn(bin, [...args, file], { stdio: 'inherit' })
+ editor.on('exit', (code) => {
+ if (code)
+ return reject(new Error(`editor process exited with code: ${code}`))
+ return resolve()
+ })
})
- })
-}
-
-const publicVar = k => !/^(\/\/[^:]+:)?_/.test(k)
+ }
-const list = async () => {
- const msg = []
- const { long } = npm.flatOptions
- for (const [where, { data, source }] of npm.config.data.entries()) {
- if (where === 'default' && !long)
- continue
+ async list () {
+ const msg = []
+ const { long } = this.npm.flatOptions
+ for (const [where, { data, source }] of this.npm.config.data.entries()) {
+ if (where === 'default' && !long)
+ continue
+
+ const keys = Object.keys(data).sort((a, b) => a.localeCompare(b))
+ if (!keys.length)
+ continue
+
+ msg.push(`; "${where}" config from ${source}`, '')
+ for (const k of keys) {
+ const v = publicVar(k) ? JSON.stringify(data[k]) : '(protected)'
+ const src = this.npm.config.find(k)
+ const overridden = src !== where
+ msg.push((overridden ? '; ' : '') +
+ `${k} = ${v} ${overridden ? `; overridden by ${src}` : ''}`)
+ }
+ msg.push('')
+ }
- const keys = Object.keys(data).sort((a, b) => a.localeCompare(b))
- if (!keys.length)
- continue
-
- msg.push(`; "${where}" config from ${source}`, '')
- for (const k of keys) {
- const v = publicVar(k) ? JSON.stringify(data[k]) : '(protected)'
- const src = npm.config.find(k)
- const overridden = src !== where
- msg.push((overridden ? '; ' : '') +
- `${k} = ${v} ${overridden ? `; overridden by ${src}` : ''}`)
+ if (!long) {
+ msg.push(
+ `; node bin location = ${process.execPath}`,
+ `; cwd = ${process.cwd()}`,
+ `; HOME = ${process.env.HOME}`,
+ '; Run `npm config ls -l` to show all defaults.'
+ )
}
- msg.push('')
- }
- if (!long) {
- msg.push(
- `; node bin location = ${process.execPath}`,
- `; cwd = ${process.cwd()}`,
- `; HOME = ${process.env.HOME}`,
- '; Run `npm config ls -l` to show all defaults.'
- )
+ output(msg.join('\n').trim())
}
- output(msg.join('\n').trim())
-}
+ async listJson () {
+ const publicConf = {}
+ for (const key in this.npm.config.list[0]) {
+ if (!publicVar(key))
+ continue
-const listJson = async () => {
- const publicConf = {}
- for (const key in npm.config.list[0]) {
- if (!publicVar(key))
- continue
+ publicConf[key] = this.npm.config.get(key)
+ }
+ output(JSON.stringify(publicConf, null, 2))
+ }
- publicConf[key] = npm.config.get(key)
+ usageError () {
+ return Object.assign(new Error(this.usage), { code: 'EUSAGE' })
}
- output(JSON.stringify(publicConf, null, 2))
}
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = Config
diff --git a/deps/npm/lib/dedupe.js b/deps/npm/lib/dedupe.js
index 2211fcac8b..59978895ef 100644
--- a/deps/npm/lib/dedupe.js
+++ b/deps/npm/lib/dedupe.js
@@ -1,29 +1,39 @@
// dedupe duplicated packages, or find them in the tree
-const npm = require('./npm.js')
const Arborist = require('@npmcli/arborist')
const usageUtil = require('./utils/usage.js')
const reifyFinish = require('./utils/reify-finish.js')
-const usage = usageUtil('dedupe', 'npm dedupe')
+class Dedupe {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => dedupe(args).then(() => cb()).catch(cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('dedupe', 'npm dedupe')
+ }
-const dedupe = async (args) => {
- if (npm.flatOptions.global) {
- const er = new Error('`npm dedupe` does not work in global mode.')
- er.code = 'EDEDUPEGLOBAL'
- throw er
+ exec (args, cb) {
+ this.dedupe(args).then(() => cb()).catch(cb)
}
- const dryRun = (args && args.dryRun) || npm.flatOptions.dryRun
- const where = npm.prefix
- const arb = new Arborist({
- ...npm.flatOptions,
- path: where,
- dryRun,
- })
- await arb.dedupe(npm.flatOptions)
- await reifyFinish(arb)
+ async dedupe (args) {
+ if (this.npm.config.get('global')) {
+ const er = new Error('`npm dedupe` does not work in global mode.')
+ er.code = 'EDEDUPEGLOBAL'
+ throw er
+ }
+
+ const dryRun = this.npm.config.get('dry-run')
+ const where = this.npm.prefix
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ path: where,
+ dryRun,
+ })
+ await arb.dedupe(this.npm.flatOptions)
+ await reifyFinish(this.npm, arb)
+ }
}
-module.exports = Object.assign(cmd, { usage })
+module.exports = Dedupe
diff --git a/deps/npm/lib/deprecate.js b/deps/npm/lib/deprecate.js
index 42d099b544..48f27ab6c3 100644
--- a/deps/npm/lib/deprecate.js
+++ b/deps/npm/lib/deprecate.js
@@ -1,4 +1,3 @@
-const npm = require('./npm.js')
const fetch = require('npm-registry-fetch')
const otplease = require('./utils/otplease.js')
const npa = require('npm-package-arg')
@@ -7,67 +6,77 @@ const getIdentity = require('./utils/get-identity.js')
const libaccess = require('libnpmaccess')
const usageUtil = require('./utils/usage.js')
-const UsageError = () =>
- Object.assign(new Error(`\nUsage: ${usage}`), {
- code: 'EUSAGE',
- })
+class Deprecate {
+ constructor (npm) {
+ this.npm = npm
+ }
-const usage = usageUtil(
- 'deprecate',
- 'npm deprecate <pkg>[@<version>] <message>'
-)
+ get usage () {
+ return usageUtil(
+ 'deprecate',
+ 'npm deprecate <pkg>[@<version>] <message>'
+ )
+ }
-const completion = async (opts) => {
- if (opts.conf.argv.remain.length > 1)
- return []
+ async completion (opts) {
+ if (opts.conf.argv.remain.length > 1)
+ return []
- const username = await getIdentity(npm.flatOptions)
- const packages = await libaccess.lsPackages(username, npm.flatOptions)
- return Object.keys(packages)
- .filter((name) =>
- packages[name] === 'write' &&
- (opts.conf.argv.remain.length === 0 ||
- name.startsWith(opts.conf.argv.remain[0])))
-}
-
-const cmd = (args, cb) =>
- deprecate(args)
- .then(() => cb())
- .catch(err => cb(err.code === 'EUSAGE' ? err.message : err))
+ const username = await getIdentity(this.npm, this.npm.flatOptions)
+ const packages = await libaccess.lsPackages(username, this.npm.flatOptions)
+ return Object.keys(packages)
+ .filter((name) =>
+ packages[name] === 'write' &&
+ (opts.conf.argv.remain.length === 0 ||
+ name.startsWith(opts.conf.argv.remain[0])))
+ }
-const deprecate = async ([pkg, msg]) => {
- if (!pkg || !msg)
- throw UsageError()
+ exec (args, cb) {
+ this.deprecate(args)
+ .then(() => cb())
+ .catch(err => cb(err.code === 'EUSAGE' ? err.message : err))
+ }
- // fetch the data and make sure it exists.
- const p = npa(pkg)
- // npa makes the default spec "latest", but for deprecation
- // "*" is the appropriate default.
- const spec = p.rawSpec === '' ? '*' : p.fetchSpec
+ async deprecate ([pkg, msg]) {
+ if (!pkg || !msg)
+ throw this.usageError()
- if (semver.validRange(spec, true) === null)
- throw new Error(`invalid version range: ${spec}`)
+ // fetch the data and make sure it exists.
+ const p = npa(pkg)
+ // npa makes the default spec "latest", but for deprecation
+ // "*" is the appropriate default.
+ const spec = p.rawSpec === '' ? '*' : p.fetchSpec
- const uri = '/' + p.escapedName
- const packument = await fetch.json(uri, {
- ...npm.flatOptions,
- spec: p,
- query: { write: true },
- })
+ if (semver.validRange(spec, true) === null)
+ throw new Error(`invalid version range: ${spec}`)
- Object.keys(packument.versions)
- .filter(v => semver.satisfies(v, spec, { includePrerelease: true }))
- .forEach(v => {
- packument.versions[v].deprecated = msg
+ const uri = '/' + p.escapedName
+ const packument = await fetch.json(uri, {
+ ...this.npm.flatOptions,
+ spec: p,
+ query: { write: true },
})
- return otplease(npm.flatOptions, opts => fetch(uri, {
- ...opts,
- spec: p,
- method: 'PUT',
- body: packument,
- ignoreBody: true,
- }))
+ Object.keys(packument.versions)
+ .filter(v => semver.satisfies(v, spec, { includePrerelease: true }))
+ .forEach(v => {
+ packument.versions[v].deprecated = msg
+ })
+
+ return otplease(this.npm.flatOptions, opts => fetch(uri, {
+ ...opts,
+ spec: p,
+ method: 'PUT',
+ body: packument,
+ ignoreBody: true,
+ }))
+ }
+
+ usageError () {
+ return Object.assign(new Error(`\nUsage: ${this.usage}`), {
+ code: 'EUSAGE',
+ })
+ }
}
-module.exports = Object.assign(cmd, { completion, usage })
+module.exports = Deprecate
diff --git a/deps/npm/lib/diff.js b/deps/npm/lib/diff.js
index 9ef5a78a20..859e6f76fe 100644
--- a/deps/npm/lib/diff.js
+++ b/deps/npm/lib/diff.js
@@ -8,258 +8,271 @@ const npmlog = require('npmlog')
const pacote = require('pacote')
const pickManifest = require('npm-pick-manifest')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const output = require('./utils/output.js')
const readLocalPkg = require('./utils/read-local-package.js')
-const usage = usageUtil(
- 'diff',
- 'npm diff [...<paths>]' +
- '\nnpm diff --diff=<pkg-name> [...<paths>]' +
- '\nnpm diff --diff=<version-a> [--diff=<version-b>] [...<paths>]' +
- '\nnpm diff --diff=<spec-a> [--diff=<spec-b>] [...<paths>]' +
- '\nnpm diff [--diff-ignore-all-space] [--diff-name-only] [...<paths>] [...<paths>]'
-)
-
-const cmd = (args, cb) => diff(args).then(() => cb()).catch(cb)
-
-const where = () => {
- const globalTop = resolve(npm.globalDir, '..')
- const { global } = npm.flatOptions
- return global ? globalTop : npm.prefix
-}
+class Diff {
+ constructor (npm) {
+ this.npm = npm
+ }
-const diff = async (args) => {
- const specs = npm.flatOptions.diff.filter(d => d)
- if (specs.length > 2) {
- throw new TypeError(
- 'Can\'t use more than two --diff arguments.\n\n' +
- `Usage:\n${usage}`
+ get usage () {
+ return usageUtil(
+ 'diff',
+ 'npm diff [...<paths>]' +
+ '\nnpm diff --diff=<pkg-name> [...<paths>]' +
+ '\nnpm diff --diff=<version-a> [--diff=<version-b>] [...<paths>]' +
+ '\nnpm diff --diff=<spec-a> [--diff=<spec-b>] [...<paths>]' +
+ '\nnpm diff [--diff-ignore-all-space] [--diff-name-only] [...<paths>] [...<paths>]'
)
}
- const [a, b] = await retrieveSpecs(specs)
- npmlog.info('diff', { src: a, dst: b })
-
- const res = await libdiff([a, b], { ...npm.flatOptions, diffFiles: args })
- return output(res)
-}
+ get where () {
+ const globalTop = resolve(this.npm.globalDir, '..')
+ const { global } = this.npm.flatOptions
+ return global ? globalTop : this.npm.prefix
+ }
-const retrieveSpecs = ([a, b]) => {
- // no arguments, defaults to comparing cwd
- // to its latest published registry version
- if (!a)
- return defaultSpec()
+ exec (args, cb) {
+ this.diff(args).then(() => cb()).catch(cb)
+ }
- // single argument, used to compare wanted versions of an
- // installed dependency or to compare the cwd to a published version
- if (!b)
- return transformSingleSpec(a)
+ async diff (args) {
+ const specs = this.npm.flatOptions.diff.filter(d => d)
+ if (specs.length > 2) {
+ throw new TypeError(
+ 'Can\'t use more than two --diff arguments.\n\n' +
+ `Usage:\n${this.usage}`
+ )
+ }
- return convertVersionsToSpecs([a, b])
- .then(findVersionsByPackageName)
-}
+ const [a, b] = await this.retrieveSpecs(specs)
+ npmlog.info('diff', { src: a, dst: b })
-const defaultSpec = async () => {
- let noPackageJson
- let pkgName
- try {
- pkgName = await readLocalPkg()
- } catch (e) {
- npmlog.verbose('diff', 'could not read project dir package.json')
- noPackageJson = true
+ const res = await libdiff([a, b], {
+ ...this.npm.flatOptions,
+ diffFiles: args,
+ where: this.where,
+ })
+ return output(res)
}
- if (!pkgName || noPackageJson) {
- throw new Error(
- 'Needs multiple arguments to compare or run from a project dir.\n\n' +
- `Usage:\n${usage}`
- )
- }
+ async retrieveSpecs ([a, b]) {
+ // no arguments, defaults to comparing cwd
+ // to its latest published registry version
+ if (!a)
+ return this.defaultSpec()
- return [
- `${pkgName}@${npm.flatOptions.defaultTag}`,
- `file:${npm.prefix}`,
- ]
-}
+ // single argument, used to compare wanted versions of an
+ // installed dependency or to compare the cwd to a published version
+ if (!b)
+ return this.transformSingleSpec(a)
-const transformSingleSpec = async (a) => {
- let noPackageJson
- let pkgName
- try {
- pkgName = await readLocalPkg()
- } catch (e) {
- npmlog.verbose('diff', 'could not read project dir package.json')
- noPackageJson = true
+ const specs = await this.convertVersionsToSpecs([a, b])
+ return this.findVersionsByPackageName(specs)
}
- const missingPackageJson = new Error(
- 'Needs multiple arguments to compare or run from a project dir.\n\n' +
- `Usage:\n${usage}`
- )
- const specSelf = () => {
- if (noPackageJson)
- throw missingPackageJson
-
- return `file:${npm.prefix}`
- }
+ async defaultSpec () {
+ let noPackageJson
+ let pkgName
+ try {
+ pkgName = await readLocalPkg(this.npm)
+ } catch (e) {
+ npmlog.verbose('diff', 'could not read project dir package.json')
+ noPackageJson = true
+ }
- // using a valid semver range, that means it should just diff
- // the cwd against a published version to the registry using the
- // same project name and the provided semver range
- if (semver.validRange(a)) {
- if (!pkgName)
- throw missingPackageJson
+ if (!pkgName || noPackageJson) {
+ throw new Error(
+ 'Needs multiple arguments to compare or run from a project dir.\n\n' +
+ `Usage:\n${this.usage}`
+ )
+ }
return [
- `${pkgName}@${a}`,
- specSelf(),
+ `${pkgName}@${this.npm.flatOptions.defaultTag}`,
+ `file:${this.npm.prefix}`,
]
}
- // when using a single package name as arg and it's part of the current
- // install tree, then retrieve the current installed version and compare
- // it against the same value `npm outdated` would suggest you to update to
- const spec = npa(a)
- if (spec.registry) {
- let actualTree
- let node
+ async transformSingleSpec (a) {
+ let noPackageJson
+ let pkgName
try {
- const opts = {
- ...npm.flatOptions,
- path: where(),
- }
- const arb = new Arborist(opts)
- actualTree = await arb.loadActual(opts)
- node = actualTree &&
- actualTree.inventory.query('name', spec.name)
- .values().next().value
+ pkgName = await readLocalPkg(this.npm)
} catch (e) {
- npmlog.verbose('diff', 'failed to load actual install tree')
+ npmlog.verbose('diff', 'could not read project dir package.json')
+ noPackageJson = true
+ }
+ const missingPackageJson = new Error(
+ 'Needs multiple arguments to compare or run from a project dir.\n\n' +
+ `Usage:\n${this.usage}`
+ )
+
+ const specSelf = () => {
+ if (noPackageJson)
+ throw missingPackageJson
+
+ return `file:${this.npm.prefix}`
}
- if (!node || !node.name || !node.package || !node.package.version) {
+ // using a valid semver range, that means it should just diff
+ // the cwd against a published version to the registry using the
+ // same project name and the provided semver range
+ if (semver.validRange(a)) {
+ if (!pkgName)
+ throw missingPackageJson
+
return [
- `${spec.name}@${spec.fetchSpec}`,
+ `${pkgName}@${a}`,
specSelf(),
]
}
- const tryRootNodeSpec = () =>
- (actualTree && actualTree.edgesOut.get(spec.name) || {}).spec
-
- const tryAnySpec = () => {
- for (const edge of node.edgesIn)
- return edge.spec
- }
+ // when using a single package name as arg and it's part of the current
+ // install tree, then retrieve the current installed version and compare
+ // it against the same value `npm outdated` would suggest you to update to
+ const spec = npa(a)
+ if (spec.registry) {
+ let actualTree
+ let node
+ try {
+ const opts = {
+ ...this.npm.flatOptions,
+ path: this.where,
+ }
+ const arb = new Arborist(opts)
+ actualTree = await arb.loadActual(opts)
+ node = actualTree &&
+ actualTree.inventory.query('name', spec.name)
+ .values().next().value
+ } catch (e) {
+ npmlog.verbose('diff', 'failed to load actual install tree')
+ }
- const aSpec = `file:${node.realpath}`
-
- // finds what version of the package to compare against, if a exact
- // version or tag was passed than it should use that, otherwise
- // work from the top of the arborist tree to find the original semver
- // range declared in the package that depends on the package.
- let bSpec
- if (spec.rawSpec)
- bSpec = spec.rawSpec
- else {
- const bTargetVersion =
- tryRootNodeSpec()
- || tryAnySpec()
-
- // figure out what to compare against,
- // follows same logic to npm outdated "Wanted" results
- const packument = await pacote.packument(spec, {
- ...npm.flatOptions,
- preferOnline: true,
- })
- bSpec = pickManifest(
- packument,
- bTargetVersion,
- { ...npm.flatOptions }
- ).version
- }
+ if (!node || !node.name || !node.package || !node.package.version) {
+ return [
+ `${spec.name}@${spec.fetchSpec}`,
+ specSelf(),
+ ]
+ }
- return [
- `${spec.name}@${aSpec}`,
- `${spec.name}@${bSpec}`,
- ]
- } else if (spec.type === 'directory') {
- return [
- `file:${spec.fetchSpec}`,
- specSelf(),
- ]
- } else {
- throw new Error(
- 'Spec type not supported.\n\n' +
- `Usage:\n${usage}`
- )
- }
-}
+ const tryRootNodeSpec = () =>
+ (actualTree && actualTree.edgesOut.get(spec.name) || {}).spec
-const convertVersionsToSpecs = async ([a, b]) => {
- const semverA = semver.validRange(a)
- const semverB = semver.validRange(b)
+ const tryAnySpec = () => {
+ for (const edge of node.edgesIn)
+ return edge.spec
+ }
- // both specs are semver versions, assume current project dir name
- if (semverA && semverB) {
- let pkgName
- try {
- pkgName = await readLocalPkg()
- } catch (e) {
- npmlog.verbose('diff', 'could not read project dir package.json')
- }
+ const aSpec = `file:${node.realpath}`
+
+ // finds what version of the package to compare against, if a exact
+ // version or tag was passed than it should use that, otherwise
+ // work from the top of the arborist tree to find the original semver
+ // range declared in the package that depends on the package.
+ let bSpec
+ if (spec.rawSpec)
+ bSpec = spec.rawSpec
+ else {
+ const bTargetVersion =
+ tryRootNodeSpec()
+ || tryAnySpec()
+
+ // figure out what to compare against,
+ // follows same logic to npm outdated "Wanted" results
+ const packument = await pacote.packument(spec, {
+ ...this.npm.flatOptions,
+ preferOnline: true,
+ })
+ bSpec = pickManifest(
+ packument,
+ bTargetVersion,
+ { ...this.npm.flatOptions }
+ ).version
+ }
- if (!pkgName) {
+ return [
+ `${spec.name}@${aSpec}`,
+ `${spec.name}@${bSpec}`,
+ ]
+ } else if (spec.type === 'directory') {
+ return [
+ `file:${spec.fetchSpec}`,
+ specSelf(),
+ ]
+ } else {
throw new Error(
- 'Needs to be run from a project dir in order to diff two versions.\n\n' +
- `Usage:\n${usage}`
+ 'Spec type not supported.\n\n' +
+ `Usage:\n${this.usage}`
)
}
- return [`${pkgName}@${a}`, `${pkgName}@${b}`]
}
- // otherwise uses the name from the other arg to
- // figure out the spec.name of what to compare
- if (!semverA && semverB)
- return [a, `${npa(a).name}@${b}`]
+ async convertVersionsToSpecs ([a, b]) {
+ const semverA = semver.validRange(a)
+ const semverB = semver.validRange(b)
+
+ // both specs are semver versions, assume current project dir name
+ if (semverA && semverB) {
+ let pkgName
+ try {
+ pkgName = await readLocalPkg(this.npm)
+ } catch (e) {
+ npmlog.verbose('diff', 'could not read project dir package.json')
+ }
+
+ if (!pkgName) {
+ throw new Error(
+ 'Needs to be run from a project dir in order to diff two versions.\n\n' +
+ `Usage:\n${this.usage}`
+ )
+ }
+ return [`${pkgName}@${a}`, `${pkgName}@${b}`]
+ }
- if (semverA && !semverB)
- return [`${npa(b).name}@${a}`, b]
+ // otherwise uses the name from the other arg to
+ // figure out the spec.name of what to compare
+ if (!semverA && semverB)
+ return [a, `${npa(a).name}@${b}`]
- // no valid semver ranges used
- return [a, b]
-}
+ if (semverA && !semverB)
+ return [`${npa(b).name}@${a}`, b]
-const findVersionsByPackageName = async (specs) => {
- let actualTree
- try {
- const opts = {
- ...npm.flatOptions,
- path: where(),
- }
- const arb = new Arborist(opts)
- actualTree = await arb.loadActual(opts)
- } catch (e) {
- npmlog.verbose('diff', 'failed to load actual install tree')
+ // no valid semver ranges used
+ return [a, b]
}
- return specs.map(i => {
- const spec = npa(i)
- if (spec.rawSpec)
- return i
+ async findVersionsByPackageName (specs) {
+ let actualTree
+ try {
+ const opts = {
+ ...this.npm.flatOptions,
+ path: this.where,
+ }
+ const arb = new Arborist(opts)
+ actualTree = await arb.loadActual(opts)
+ } catch (e) {
+ npmlog.verbose('diff', 'failed to load actual install tree')
+ }
+
+ return specs.map(i => {
+ const spec = npa(i)
+ if (spec.rawSpec)
+ return i
- const node = actualTree
- && actualTree.inventory.query('name', spec.name)
- .values().next().value
+ const node = actualTree
+ && actualTree.inventory.query('name', spec.name)
+ .values().next().value
- const res = !node || !node.package || !node.package.version
- ? spec.fetchSpec
- : `file:${node.realpath}`
+ const res = !node || !node.package || !node.package.version
+ ? spec.fetchSpec
+ : `file:${node.realpath}`
- return `${spec.name}@${res}`
- })
+ return `${spec.name}@${res}`
+ })
+ }
}
-module.exports = Object.assign(cmd, { usage })
+module.exports = Diff
diff --git a/deps/npm/lib/dist-tag.js b/deps/npm/lib/dist-tag.js
index e958bb7544..171a88c527 100644
--- a/deps/npm/lib/dist-tag.js
+++ b/deps/npm/lib/dist-tag.js
@@ -3,69 +3,77 @@ const npa = require('npm-package-arg')
const regFetch = require('npm-registry-fetch')
const semver = require('semver')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const readLocalPkgName = require('./utils/read-local-package.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'dist-tag',
- 'npm dist-tag add <pkg>@<version> [<tag>]' +
- '\nnpm dist-tag rm <pkg> <tag>' +
- '\nnpm dist-tag ls [<pkg>]'
-)
-
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
- if (argv.length === 2)
- return ['add', 'rm', 'ls']
-
- switch (argv[2]) {
- default:
- return []
+class DistTag {
+ constructor (npm) {
+ this.npm = npm
}
-}
-const cmd = (args, cb) => distTag(args).then(() => cb()).catch(cb)
+ get usage () {
+ return usageUtil(
+ 'dist-tag',
+ 'npm dist-tag add <pkg>@<version> [<tag>]' +
+ '\nnpm dist-tag rm <pkg> <tag>' +
+ '\nnpm dist-tag ls [<pkg>]'
+ )
+ }
-const distTag = async ([cmdName, pkg, tag]) => {
- const opts = npm.flatOptions
- const has = (items) => new Set(items).has(cmdName)
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv.length === 2)
+ return ['add', 'rm', 'ls']
- if (has(['add', 'a', 'set', 's']))
- return add(pkg, tag, opts)
+ switch (argv[2]) {
+ default:
+ return []
+ }
+ }
- if (has(['rm', 'r', 'del', 'd', 'remove']))
- return remove(pkg, tag, opts)
+ exec (args, cb) {
+ this.distTag(args).then(() => cb()).catch(cb)
+ }
- if (has(['ls', 'l', 'sl', 'list']))
- return list(pkg, opts)
+ async distTag ([cmdName, pkg, tag]) {
+ const opts = this.npm.flatOptions
+ const has = (items) => new Set(items).has(cmdName)
- if (!pkg) {
- // when only using the pkg name the default behavior
- // should be listing the existing tags
- return list(cmdName, opts)
- } else
- throw usage
-}
+ if (has(['add', 'a', 'set', 's']))
+ return this.add(pkg, tag, opts)
-function add (spec, tag, opts) {
- spec = npa(spec || '')
- const version = spec.rawSpec
- const defaultTag = tag || opts.defaultTag
+ if (has(['rm', 'r', 'del', 'd', 'remove']))
+ return this.remove(pkg, tag, opts)
- log.verbose('dist-tag add', defaultTag, 'to', spec.name + '@' + version)
+ if (has(['ls', 'l', 'sl', 'list']))
+ return this.list(pkg, opts)
- if (!spec.name || !version || !defaultTag)
- throw usage
+ if (!pkg) {
+ // when only using the pkg name the default behavior
+ // should be listing the existing tags
+ return this.list(cmdName, opts)
+ } else
+ throw this.usage
+ }
+
+ async add (spec, tag, opts) {
+ spec = npa(spec || '')
+ const version = spec.rawSpec
+ const defaultTag = tag || opts.defaultTag
+
+ log.verbose('dist-tag add', defaultTag, 'to', spec.name + '@' + version)
- const t = defaultTag.trim()
+ if (!spec.name || !version || !defaultTag)
+ throw this.usage
- if (semver.validRange(t))
- throw new Error('Tag name must not be a valid SemVer range: ' + t)
+ const t = defaultTag.trim()
- return fetchTags(spec, opts).then(tags => {
+ if (semver.validRange(t))
+ throw new Error('Tag name must not be a valid SemVer range: ' + t)
+
+ const tags = await this.fetchTags(spec, opts)
if (tags[t] === version) {
log.warn('dist-tag add', t, 'is already set to version', version)
return
@@ -82,20 +90,18 @@ function add (spec, tag, opts) {
},
spec,
}
- return otplease(reqOpts, reqOpts => regFetch(url, reqOpts)).then(() => {
- output(`+${t}: ${spec.name}@${version}`)
- })
- })
-}
+ await otplease(reqOpts, reqOpts => regFetch(url, reqOpts))
+ output(`+${t}: ${spec.name}@${version}`)
+ }
-function remove (spec, tag, opts) {
- spec = npa(spec || '')
- log.verbose('dist-tag del', tag, 'from', spec.name)
+ async remove (spec, tag, opts) {
+ spec = npa(spec || '')
+ log.verbose('dist-tag del', tag, 'from', spec.name)
- if (!spec.name)
- throw usage
+ if (!spec.name)
+ throw this.usage
- return fetchTags(spec, opts).then(tags => {
+ const tags = await this.fetchTags(spec, opts)
if (!tags[tag]) {
log.info('dist-tag del', tag, 'is not a dist-tag on', spec.name)
throw new Error(tag + ' is not a dist-tag on ' + spec.name)
@@ -109,50 +115,43 @@ function remove (spec, tag, opts) {
method: 'DELETE',
spec,
}
- return otplease(reqOpts, reqOpts => regFetch(url, reqOpts)).then(() => {
- output(`-${tag}: ${spec.name}@${version}`)
- })
- })
-}
+ await otplease(reqOpts, reqOpts => regFetch(url, reqOpts))
+ output(`-${tag}: ${spec.name}@${version}`)
+ }
-function list (spec, opts) {
- if (!spec) {
- return readLocalPkgName().then(pkg => {
+ async list (spec, opts) {
+ if (!spec) {
+ const pkg = await readLocalPkgName(this.npm)
if (!pkg)
- throw usage
+ throw this.usage
- return list(pkg, opts)
- })
+ return this.list(pkg, opts)
+ }
+ spec = npa(spec)
+
+ try {
+ const tags = await this.fetchTags(spec, opts)
+ const msg =
+ Object.keys(tags).map(k => `${k}: ${tags[k]}`).sort().join('\n')
+ output(msg)
+ return tags
+ } catch (err) {
+ log.error('dist-tag ls', "Couldn't get dist-tag data for", spec)
+ throw err
+ }
}
- spec = npa(spec)
-
- return fetchTags(spec, opts).then(tags => {
- const msg =
- Object.keys(tags).map(k => `${k}: ${tags[k]}`).sort().join('\n')
- output(msg)
- return tags
- }, err => {
- log.error('dist-tag ls', "Couldn't get dist-tag data for", spec)
- throw err
- })
-}
-function fetchTags (spec, opts) {
- return regFetch.json(
- `/-/package/${spec.escapedName}/dist-tags`,
- {
- ...opts,
- 'prefer-online': true,
- spec,
- }
- ).then(data => {
+ async fetchTags (spec, opts) {
+ const data = await regFetch.json(
+ `/-/package/${spec.escapedName}/dist-tags`,
+ { ...opts, 'prefer-online': true, spec }
+ )
if (data && typeof data === 'object')
delete data._etag
if (!data || !Object.keys(data).length)
throw new Error('No dist-tags found for ' + spec.name)
return data
- })
+ }
}
-
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = DistTag
diff --git a/deps/npm/lib/docs.js b/deps/npm/lib/docs.js
index fa0adb3d37..2dad7a26db 100644
--- a/deps/npm/lib/docs.js
+++ b/deps/npm/lib/docs.js
@@ -1,39 +1,47 @@
const log = require('npmlog')
const pacote = require('pacote')
-const { promisify } = require('util')
-const openUrl = promisify(require('./utils/open-url.js'))
+const openUrl = require('./utils/open-url.js')
const usageUtil = require('./utils/usage.js')
-const npm = require('./npm.js')
const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js')
-const usage = usageUtil('docs', 'npm docs [<pkgname> [<pkgname> ...]]')
-
-const cmd = (args, cb) => docs(args).then(() => cb()).catch(cb)
-
-const docs = async args => {
- if (!args || !args.length)
- args = ['.']
-
- await Promise.all(args.map(pkg => getDocs(pkg)))
-}
-
-const getDocsUrl = mani => {
- if (mani.homepage)
- return mani.homepage
-
- const info = hostedFromMani(mani)
- if (info)
- return info.docs()
-
- return 'https://www.npmjs.com/package/' + mani.name
+class Docs {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('docs', 'npm docs [<pkgname> [<pkgname> ...]]')
+ }
+
+ exec (args, cb) {
+ this.docs(args).then(() => cb()).catch(cb)
+ }
+
+ async docs (args) {
+ if (!args || !args.length)
+ args = ['.']
+
+ await Promise.all(args.map(pkg => this.getDocs(pkg)))
+ }
+
+ async getDocs (pkg) {
+ const opts = { ...this.npm.flatOptions, fullMetadata: true }
+ const mani = await pacote.manifest(pkg, opts)
+ const url = this.getDocsUrl(mani)
+ log.silly('docs', 'url', url)
+ await openUrl(this.npm, url, `${mani.name} docs available at the following URL`)
+ }
+
+ getDocsUrl (mani) {
+ if (mani.homepage)
+ return mani.homepage
+
+ const info = hostedFromMani(mani)
+ if (info)
+ return info.docs()
+
+ return 'https://www.npmjs.com/package/' + mani.name
+ }
}
-
-const getDocs = async pkg => {
- const opts = { ...npm.flatOptions, fullMetadata: true }
- const mani = await pacote.manifest(pkg, opts)
- const url = getDocsUrl(mani)
- log.silly('docs', 'url', url)
- await openUrl(url, `${mani.name} docs available at the following URL`)
-}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Docs
diff --git a/deps/npm/lib/doctor.js b/deps/npm/lib/doctor.js
index e149aec128..81860004e3 100644
--- a/deps/npm/lib/doctor.js
+++ b/deps/npm/lib/doctor.js
@@ -1,79 +1,22 @@
-const npm = require('./npm.js')
-
+const cacache = require('cacache')
const chalk = require('chalk')
-const ansiTrim = require('./utils/ansi-trim.js')
+const fs = require('fs')
+const fetch = require('make-fetch-happen')
const table = require('text-table')
-const output = require('./utils/output.js')
-const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('doctor', 'npm doctor')
-const { resolve } = require('path')
-
-const ping = require('./utils/ping.js')
-const checkPing = async () => {
- const tracker = npm.log.newItem('checkPing', 1)
- tracker.info('checkPing', 'Pinging registry')
- try {
- await ping(npm.flatOptions)
- return ''
- } catch (er) {
- if (/^E\d{3}$/.test(er.code || ''))
- throw er.code.substr(1) + ' ' + er.message
- else
- throw er.message
- } finally {
- tracker.finish()
- }
-}
-
+const which = require('which')
const pacote = require('pacote')
-const getLatestNpmVersion = async () => {
- const tracker = npm.log.newItem('getLatestNpmVersion', 1)
- tracker.info('getLatestNpmVersion', 'Getting npm package information')
- try {
- const latest = (await pacote.manifest('npm@latest', npm.flatOptions)).version
- if (semver.gte(npm.version, latest))
- return `current: v${npm.version}, latest: v${latest}`
- else
- throw `Use npm v${latest}`
- } finally {
- tracker.finish()
- }
-}
-
+const { resolve } = require('path')
const semver = require('semver')
-const fetch = require('make-fetch-happen')
-const getLatestNodejsVersion = async () => {
- // XXX get the latest in the current major as well
- const current = process.version
- const currentRange = `^${current}`
- const url = 'https://nodejs.org/dist/index.json'
- const tracker = npm.log.newItem('getLatestNodejsVersion', 1)
- tracker.info('getLatestNodejsVersion', 'Getting Node.js release information')
- try {
- const res = await fetch(url, { method: 'GET', ...npm.flatOptions })
- const data = await res.json()
- let maxCurrent = '0.0.0'
- let maxLTS = '0.0.0'
- for (const { lts, version } of data) {
- if (lts && semver.gt(version, maxLTS))
- maxLTS = version
-
- if (semver.satisfies(version, currentRange) &&
- semver.gt(version, maxCurrent))
- maxCurrent = version
- }
- const recommended = semver.gt(maxCurrent, maxLTS) ? maxCurrent : maxLTS
- if (semver.gte(process.version, recommended))
- return `current: ${current}, recommended: ${recommended}`
- else
- throw `Use node ${recommended} (current: ${current})`
- } finally {
- tracker.finish()
- }
-}
-
const { promisify } = require('util')
-const fs = require('fs')
+const ansiTrim = require('./utils/ansi-trim.js')
+const isWindows = require('./utils/is-windows.js')
+const output = require('./utils/output.js')
+const ping = require('./utils/ping.js')
+const usageUtil = require('./utils/usage.js')
+const { defaults: { registry: defaultRegistry } } = require('./utils/config.js')
+const lstat = promisify(fs.lstat)
+const readdir = promisify(fs.readdir)
+const access = promisify(fs.access)
const { R_OK, W_OK, X_OK } = fs.constants
const maskLabel = mask => {
const label = []
@@ -88,200 +31,268 @@ const maskLabel = mask => {
return label.join(', ')
}
-const lstat = promisify(fs.lstat)
-const readdir = promisify(fs.readdir)
-const access = promisify(fs.access)
-const isWindows = require('./utils/is-windows.js')
-const checkFilesPermission = async (root, shouldOwn, mask = null) => {
- if (mask === null)
- mask = shouldOwn ? R_OK | W_OK : R_OK
-
- let ok = true
-
- const tracker = npm.log.newItem(root, 1)
-
- try {
- const uid = process.getuid()
- const gid = process.getgid()
- const files = new Set([root])
- for (const f of files) {
- tracker.silly('checkFilesPermission', f.substr(root.length + 1))
- const st = await lstat(f)
- .catch(er => {
- ok = false
- tracker.warn('checkFilesPermission', 'error getting info for ' + f)
- })
- tracker.completeWork(1)
-
- if (!st)
- continue
+class Doctor {
+ constructor (npm) {
+ this.npm = npm
+ }
- if (shouldOwn && (uid !== st.uid || gid !== st.gid)) {
- tracker.warn('checkFilesPermission', 'should be owner of ' + f)
- ok = false
- }
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('doctor', 'npm doctor')
+ }
- if (!st.isDirectory() && !st.isFile())
- continue
+ exec (args, cb) {
+ this.doctor(args).then(() => cb()).catch(cb)
+ }
+ async doctor (args) {
+ this.npm.log.info('Running checkup')
+
+ // each message is [title, ok, message]
+ const messages = []
+
+ const actions = [
+ ['npm ping', 'checkPing', []],
+ ['npm -v', 'getLatestNpmVersion', []],
+ ['node -v', 'getLatestNodejsVersion', []],
+ ['npm config get registry', 'checkNpmRegistry', []],
+ ['which git', 'getGitPath', []],
+ ...(isWindows ? [] : [
+ ['Perms check on cached files', 'checkFilesPermission', [this.npm.cache, true, R_OK]],
+ ['Perms check on local node_modules', 'checkFilesPermission', [this.npm.localDir, true]],
+ ['Perms check on global node_modules', 'checkFilesPermission', [this.npm.globalDir, false]],
+ ['Perms check on local bin folder', 'checkFilesPermission', [this.npm.localBin, false, R_OK | W_OK | X_OK]],
+ ['Perms check on global bin folder', 'checkFilesPermission', [this.npm.globalBin, false, X_OK]],
+ ]),
+ ['Verify cache contents', 'verifyCachedFiles', [this.npm.flatOptions.cache]],
+ // TODO:
+ // - ensure arborist.loadActual() runs without errors and no invalid edges
+ // - ensure package-lock.json matches loadActual()
+ // - verify loadActual without hidden lock file matches hidden lockfile
+ // - verify all local packages have bins linked
+ ]
+
+ // Do the actual work
+ for (const [msg, fn, args] of actions) {
+ const line = [msg]
try {
- await access(f, mask)
+ line.push(true, await this[fn](...args))
} catch (er) {
- ok = false
- const msg = `Missing permissions on ${f} (expect: ${maskLabel(mask)})`
- tracker.error('checkFilesPermission', msg)
- continue
+ line.push(false, er)
}
+ messages.push(line)
+ }
- if (st.isDirectory()) {
- const entries = await readdir(f)
- .catch(er => {
- ok = false
- tracker.warn('checkFilesPermission', 'error reading directory ' + f)
- return []
- })
- for (const entry of entries)
- files.add(resolve(f, entry))
+ const outHead = ['Check', 'Value', 'Recommendation/Notes']
+ .map(!this.npm.color ? h => h : h => chalk.underline(h))
+ let allOk = true
+ const outBody = messages.map(!this.npm.color
+ ? item => {
+ allOk = allOk && item[1]
+ item[1] = item[1] ? 'ok' : 'not ok'
+ item[2] = String(item[2])
+ return item
}
+ : item => {
+ allOk = allOk && item[1]
+ if (!item[1]) {
+ item[0] = chalk.red(item[0])
+ item[2] = chalk.magenta(String(item[2]))
+ }
+ item[1] = item[1] ? chalk.green('ok') : chalk.red('not ok')
+ return item
+ })
+ const outTable = [outHead, ...outBody]
+ const tableOpts = {
+ stringLength: s => ansiTrim(s).length,
}
- } finally {
- tracker.finish()
- if (!ok) {
- throw `Check the permissions of files in ${root}` +
- (shouldOwn ? ' (should be owned by current user)' : '')
- } else
+
+ const silent = this.npm.log.levels[this.npm.log.level] >
+ this.npm.log.levels.error
+ if (!silent) {
+ output(table(outTable, tableOpts))
+ if (!allOk)
+ console.error('')
+ }
+ if (!allOk)
+ throw 'Some problems found. See above for recommendations.'
+ }
+
+ async checkPing () {
+ const tracker = this.npm.log.newItem('checkPing', 1)
+ tracker.info('checkPing', 'Pinging registry')
+ try {
+ await ping(this.npm.flatOptions)
return ''
+ } catch (er) {
+ if (/^E\d{3}$/.test(er.code || ''))
+ throw er.code.substr(1) + ' ' + er.message
+ else
+ throw er.message
+ } finally {
+ tracker.finish()
+ }
}
-}
-const which = require('which')
-const getGitPath = async () => {
- const tracker = npm.log.newItem('getGitPath', 1)
- tracker.info('getGitPath', 'Finding git in your PATH')
- try {
- return await which('git').catch(er => {
- tracker.warn(er)
- throw "Install git and ensure it's in your PATH."
- })
- } finally {
- tracker.finish()
+ async getLatestNpmVersion () {
+ const tracker = this.npm.log.newItem('getLatestNpmVersion', 1)
+ tracker.info('getLatestNpmVersion', 'Getting npm package information')
+ try {
+ const latest = (await pacote.manifest('npm@latest', this.npm.flatOptions)).version
+ if (semver.gte(this.npm.version, latest))
+ return `current: v${this.npm.version}, latest: v${latest}`
+ else
+ throw `Use npm v${latest}`
+ } finally {
+ tracker.finish()
+ }
}
-}
-const cacache = require('cacache')
-const verifyCachedFiles = async () => {
- const tracker = npm.log.newItem('verifyCachedFiles', 1)
- tracker.info('verifyCachedFiles', 'Verifying the npm cache')
- try {
- const stats = await cacache.verify(npm.flatOptions.cache)
- const {
- badContentCount,
- reclaimedCount,
- missingContent,
- reclaimedSize,
- } = stats
- if (badContentCount || reclaimedCount || missingContent) {
- if (badContentCount)
- tracker.warn('verifyCachedFiles', `Corrupted content removed: ${badContentCount}`)
-
- if (reclaimedCount)
- tracker.warn('verifyCachedFiles', `Content garbage-collected: ${reclaimedCount} (${reclaimedSize} bytes)`)
-
- if (missingContent)
- tracker.warn('verifyCachedFiles', `Missing content: ${missingContent}`)
-
- tracker.warn('verifyCachedFiles', 'Cache issues have been fixed')
+ async getLatestNodejsVersion () {
+ // XXX get the latest in the current major as well
+ const current = process.version
+ const currentRange = `^${current}`
+ const url = 'https://nodejs.org/dist/index.json'
+ const tracker = this.npm.log.newItem('getLatestNodejsVersion', 1)
+ tracker.info('getLatestNodejsVersion', 'Getting Node.js release information')
+ try {
+ const res = await fetch(url, { method: 'GET', ...this.npm.flatOptions })
+ const data = await res.json()
+ let maxCurrent = '0.0.0'
+ let maxLTS = '0.0.0'
+ for (const { lts, version } of data) {
+ if (lts && semver.gt(version, maxLTS))
+ maxLTS = version
+
+ if (semver.satisfies(version, currentRange) &&
+ semver.gt(version, maxCurrent))
+ maxCurrent = version
+ }
+ const recommended = semver.gt(maxCurrent, maxLTS) ? maxCurrent : maxLTS
+ if (semver.gte(process.version, recommended))
+ return `current: ${current}, recommended: ${recommended}`
+ else
+ throw `Use node ${recommended} (current: ${current})`
+ } finally {
+ tracker.finish()
}
- tracker.info('verifyCachedFiles', `Verification complete. Stats: ${
- JSON.stringify(stats, null, 2)
- }`)
- return `verified ${stats.verifiedContent} tarballs`
- } finally {
- tracker.finish()
}
-}
-const { defaults: { registry: defaultRegistry } } = require('./utils/config.js')
-const checkNpmRegistry = async () => {
- if (npm.flatOptions.registry !== defaultRegistry)
- throw `Try \`npm config set registry=${defaultRegistry}\``
- else
- return `using default registry (${defaultRegistry})`
-}
+ async checkFilesPermission (root, shouldOwn, mask = null) {
+ if (mask === null)
+ mask = shouldOwn ? R_OK | W_OK : R_OK
+
+ let ok = true
+
+ const tracker = this.npm.log.newItem(root, 1)
-const cmd = (args, cb) => doctor(args).then(() => cb()).catch(cb)
-
-const doctor = async args => {
- npm.log.info('Running checkup')
-
- // each message is [title, ok, message]
- const messages = []
-
- const actions = [
- ['npm ping', checkPing, []],
- ['npm -v', getLatestNpmVersion, []],
- ['node -v', getLatestNodejsVersion, []],
- ['npm config get registry', checkNpmRegistry, []],
- ['which git', getGitPath, []],
- ...(isWindows ? [] : [
- ['Perms check on cached files', checkFilesPermission, [npm.cache, true, R_OK]],
- ['Perms check on local node_modules', checkFilesPermission, [npm.localDir, true]],
- ['Perms check on global node_modules', checkFilesPermission, [npm.globalDir, false]],
- ['Perms check on local bin folder', checkFilesPermission, [npm.localBin, false, R_OK | W_OK | X_OK]],
- ['Perms check on global bin folder', checkFilesPermission, [npm.globalBin, false, X_OK]],
- ]),
- ['Verify cache contents', verifyCachedFiles, [npm.flatOptions.cache]],
- // TODO:
- // - ensure arborist.loadActual() runs without errors and no invalid edges
- // - ensure package-lock.json matches loadActual()
- // - verify loadActual without hidden lock file matches hidden lockfile
- // - verify all local packages have bins linked
- ]
-
- for (const [msg, fn, args] of actions) {
- const line = [msg]
try {
- line.push(true, await fn(...args))
- } catch (er) {
- line.push(false, er)
+ const uid = process.getuid()
+ const gid = process.getgid()
+ const files = new Set([root])
+ for (const f of files) {
+ tracker.silly('checkFilesPermission', f.substr(root.length + 1))
+ const st = await lstat(f)
+ .catch(er => {
+ ok = false
+ tracker.warn('checkFilesPermission', 'error getting info for ' + f)
+ })
+
+ tracker.completeWork(1)
+
+ if (!st)
+ continue
+
+ if (shouldOwn && (uid !== st.uid || gid !== st.gid)) {
+ tracker.warn('checkFilesPermission', 'should be owner of ' + f)
+ ok = false
+ }
+
+ if (!st.isDirectory() && !st.isFile())
+ continue
+
+ try {
+ await access(f, mask)
+ } catch (er) {
+ ok = false
+ const msg = `Missing permissions on ${f} (expect: ${maskLabel(mask)})`
+ tracker.error('checkFilesPermission', msg)
+ continue
+ }
+
+ if (st.isDirectory()) {
+ const entries = await readdir(f)
+ .catch(er => {
+ ok = false
+ tracker.warn('checkFilesPermission', 'error reading directory ' + f)
+ return []
+ })
+ for (const entry of entries)
+ files.add(resolve(f, entry))
+ }
+ }
+ } finally {
+ tracker.finish()
+ if (!ok) {
+ throw `Check the permissions of files in ${root}` +
+ (shouldOwn ? ' (should be owned by current user)' : '')
+ } else
+ return ''
}
- messages.push(line)
}
- const silent = npm.log.levels[npm.log.level] > npm.log.levels.error
-
- const outHead = ['Check', 'Value', 'Recommendation/Notes']
- .map(!npm.color ? h => h : h => chalk.underline(h))
- let allOk = true
- const outBody = messages.map(!npm.color
- ? item => {
- allOk = allOk && item[1]
- item[1] = item[1] ? 'ok' : 'not ok'
- item[2] = String(item[2])
- return item
+ async getGitPath () {
+ const tracker = this.npm.log.newItem('getGitPath', 1)
+ tracker.info('getGitPath', 'Finding git in your PATH')
+ try {
+ return await which('git').catch(er => {
+ tracker.warn(er)
+ throw "Install git and ensure it's in your PATH."
+ })
+ } finally {
+ tracker.finish()
}
- : item => {
- allOk = allOk && item[1]
- if (!item[1]) {
- item[0] = chalk.red(item[0])
- item[2] = chalk.magenta(String(item[2]))
+ }
+
+ async verifyCachedFiles () {
+ const tracker = this.npm.log.newItem('verifyCachedFiles', 1)
+ tracker.info('verifyCachedFiles', 'Verifying the npm cache')
+ try {
+ const stats = await cacache.verify(this.npm.flatOptions.cache)
+ const {
+ badContentCount,
+ reclaimedCount,
+ missingContent,
+ reclaimedSize,
+ } = stats
+ if (badContentCount || reclaimedCount || missingContent) {
+ if (badContentCount)
+ tracker.warn('verifyCachedFiles', `Corrupted content removed: ${badContentCount}`)
+
+ if (reclaimedCount)
+ tracker.warn('verifyCachedFiles', `Content garbage-collected: ${reclaimedCount} (${reclaimedSize} bytes)`)
+
+ if (missingContent)
+ tracker.warn('verifyCachedFiles', `Missing content: ${missingContent}`)
+
+ tracker.warn('verifyCachedFiles', 'Cache issues have been fixed')
}
- item[1] = item[1] ? chalk.green('ok') : chalk.red('not ok')
- return item
- })
- const outTable = [outHead, ...outBody]
- const tableOpts = {
- stringLength: s => ansiTrim(s).length,
+ tracker.info('verifyCachedFiles', `Verification complete. Stats: ${
+ JSON.stringify(stats, null, 2)
+ }`)
+ return `verified ${stats.verifiedContent} tarballs`
+ } finally {
+ tracker.finish()
+ }
}
- if (!silent) {
- output(table(outTable, tableOpts))
- if (!allOk)
- console.error('')
+ async checkNpmRegistry () {
+ if (this.npm.flatOptions.registry !== defaultRegistry)
+ throw `Try \`npm config set registry=${defaultRegistry}\``
+ else
+ return `using default registry (${defaultRegistry})`
}
- if (!allOk)
- throw 'Some problems found. See above for recommendations.'
}
-module.exports = Object.assign(cmd, { usage })
+module.exports = Doctor
diff --git a/deps/npm/lib/edit.js b/deps/npm/lib/edit.js
index 9ae6349262..a7dbb38205 100644
--- a/deps/npm/lib/edit.js
+++ b/deps/npm/lib/edit.js
@@ -4,33 +4,55 @@
const { resolve } = require('path')
const fs = require('graceful-fs')
const { spawn } = require('child_process')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const splitPackageNames = require('./utils/split-package-names.js')
-
-const usage = usageUtil('edit', 'npm edit <pkg>[/<subpkg>...]')
const completion = require('./utils/completion/installed-shallow.js')
-function edit (args, cb) {
- if (args.length !== 1)
- return cb(usage)
-
- const path = splitPackageNames(args[0])
- const dir = resolve(npm.dir, path)
-
- fs.lstat(dir, (err) => {
- if (err)
- return cb(err)
-
- const [bin, ...args] = npm.config.get('editor').split(/\s+/)
- const editor = spawn(bin, [...args, dir], { stdio: 'inherit' })
- editor.on('exit', (code) => {
- if (code)
- return cb(new Error(`editor process exited with code: ${code}`))
-
- npm.commands.rebuild([dir], cb)
+class Edit {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('edit', 'npm edit <pkg>[/<subpkg>...]')
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
+
+ exec (args, cb) {
+ this.edit(args).then(() => cb()).catch(cb)
+ }
+
+ async edit (args) {
+ if (args.length !== 1)
+ throw new Error(this.usage)
+
+ const path = splitPackageNames(args[0])
+ const dir = resolve(this.npm.dir, path)
+
+ // graceful-fs does not promisify
+ await new Promise((resolve, reject) => {
+ fs.lstat(dir, (err) => {
+ if (err)
+ return reject(err)
+ const [bin, ...args] = this.npm.config.get('editor').split(/\s+/)
+ const editor = spawn(bin, [...args, dir], { stdio: 'inherit' })
+ editor.on('exit', (code) => {
+ if (code)
+ return reject(new Error(`editor process exited with code: ${code}`))
+ this.npm.commands.rebuild([dir], (err) => {
+ if (err)
+ return reject(err)
+
+ resolve()
+ })
+ })
+ })
})
- })
+ }
}
-
-module.exports = Object.assign(edit, { completion, usage })
+module.exports = Edit
diff --git a/deps/npm/lib/exec.js b/deps/npm/lib/exec.js
index dab65c23a3..d1db491285 100644
--- a/deps/npm/lib/exec.js
+++ b/deps/npm/lib/exec.js
@@ -1,28 +1,18 @@
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('exec',
- 'Run a command from a local or remote npm package.\n\n' +
-
- 'npm exec -- <pkg>[@<version>] [args...]\n' +
- 'npm exec --package=<pkg>[@<version>] -- <cmd> [args...]\n' +
- 'npm exec -c \'<cmd> [args...]\'\n' +
- 'npm exec --package=foo -c \'<cmd> [args...]\'\n' +
- '\n' +
- 'npx <pkg>[@<specifier>] [args...]\n' +
- 'npx -p <pkg>[@<specifier>] <cmd> [args...]\n' +
- 'npx -c \'<cmd> [args...]\'\n' +
- 'npx -p <pkg>[@<specifier>] -c \'<cmd> [args...]\'' +
- '\n' +
- 'Run without --call or positional args to open interactive subshell\n',
-
- '\n--package=<pkg> (may be specified multiple times)\n' +
- '-p is a shorthand for --package only when using npx executable\n' +
- '-c <cmd> --call=<cmd> (may not be mixed with positional arguments)'
-)
-
const { promisify } = require('util')
const read = promisify(require('read'))
+const mkdirp = require('mkdirp-infer-owner')
+const readPackageJson = require('read-package-json-fast')
+const Arborist = require('@npmcli/arborist')
+const runScript = require('@npmcli/run-script')
+const { resolve, delimiter } = require('path')
+const ciDetect = require('@npmcli/ci-detect')
+const crypto = require('crypto')
+const pacote = require('pacote')
+const npa = require('npm-package-arg')
+const fileExists = require('./utils/file-exists.js')
+const PATH = require('./utils/path.js')
// it's like this:
//
@@ -49,237 +39,258 @@ const read = promisify(require('read'))
// runScript({ pkg, event: 'npx', ... })
// process.env.npm_lifecycle_event = 'npx'
-const mkdirp = require('mkdirp-infer-owner')
-const readPackageJson = require('read-package-json-fast')
-const Arborist = require('@npmcli/arborist')
-const runScript = require('@npmcli/run-script')
-const { resolve, delimiter } = require('path')
-const ciDetect = require('@npmcli/ci-detect')
-const crypto = require('crypto')
-const pacote = require('pacote')
-const npa = require('npm-package-arg')
-const fileExists = require('./utils/file-exists.js')
-const PATH = require('./utils/path.js')
-
-const cmd = (args, cb) => exec(args).then(() => cb()).catch(cb)
-
-const run = async ({ args, call, pathArr, shell }) => {
- // turn list of args into command string
- const script = call || args.shift() || shell
-
- // do the fakey runScript dance
- // still should work if no package.json in cwd
- const realPkg = await readPackageJson(`${npm.localPrefix}/package.json`)
- .catch(() => ({}))
- const pkg = {
- ...realPkg,
- scripts: {
- ...(realPkg.scripts || {}),
- npx: script,
- },
+class Exec {
+ constructor (npm) {
+ this.npm = npm
}
- npm.log.disableProgress()
- try {
- if (script === shell) {
- if (process.stdin.isTTY) {
- if (ciDetect())
- return npm.log.warn('exec', 'Interactive mode disabled in CI environment')
- output(`\nEntering npm script environment\nType 'exit' or ^D when finished\n`)
- }
- }
- return await runScript({
- ...npm.flatOptions,
- pkg,
- banner: false,
- // we always run in cwd, not --prefix
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- args,
- env: {
- PATH: pathArr.join(delimiter),
- },
- stdio: 'inherit',
- })
- } finally {
- npm.log.enableProgress()
+ get usage () {
+ return usageUtil('exec',
+ 'Run a command from a local or remote npm package.\n\n' +
+
+ 'npm exec -- <pkg>[@<version>] [args...]\n' +
+ 'npm exec --package=<pkg>[@<version>] -- <cmd> [args...]\n' +
+ 'npm exec -c \'<cmd> [args...]\'\n' +
+ 'npm exec --package=foo -c \'<cmd> [args...]\'\n' +
+ '\n' +
+ 'npx <pkg>[@<specifier>] [args...]\n' +
+ 'npx -p <pkg>[@<specifier>] <cmd> [args...]\n' +
+ 'npx -c \'<cmd> [args...]\'\n' +
+ 'npx -p <pkg>[@<specifier>] -c \'<cmd> [args...]\'' +
+ '\n' +
+ 'Run without --call or positional args to open interactive subshell\n',
+
+ '\n--package=<pkg> (may be specified multiple times)\n' +
+ '-p is a shorthand for --package only when using npx executable\n' +
+ '-c <cmd> --call=<cmd> (may not be mixed with positional arguments)'
+ )
}
-}
-
-const exec = async args => {
- const { package: packages, call, shell } = npm.flatOptions
- if (call && args.length)
- throw usage
+ exec (args, cb) {
+ this._exec(args).then(() => cb()).catch(cb)
+ }
- const pathArr = [...PATH]
+ // When commands go async and we can dump the boilerplate exec methods this
+ // can be named correctly
+ async _exec (args) {
+ const { package: packages, call, shell } = this.npm.flatOptions
- // nothing to maybe install, skip the arborist dance
- if (!call && !args.length && !packages.length) {
- return await run({
- args,
- call,
- shell,
- pathArr,
- })
- }
+ if (call && args.length)
+ throw this.usage
- const needPackageCommandSwap = args.length && !packages.length
- // if there's an argument and no package has been explicitly asked for
- // check the local and global bin paths for a binary named the same as
- // the argument and run it if it exists, otherwise fall through to
- // the behavior of treating the single argument as a package name
- if (needPackageCommandSwap) {
- let binExists = false
- if (await fileExists(`${npm.localBin}/${args[0]}`)) {
- pathArr.unshift(npm.localBin)
- binExists = true
- } else if (await fileExists(`${npm.globalBin}/${args[0]}`)) {
- pathArr.unshift(npm.globalBin)
- binExists = true
- }
+ const pathArr = [...PATH]
- if (binExists) {
- return await run({
+ // nothing to maybe install, skip the arborist dance
+ if (!call && !args.length && !packages.length) {
+ return await this.run({
args,
call,
- pathArr,
shell,
+ pathArr,
})
}
- packages.push(args[0])
- }
+ const needPackageCommandSwap = args.length && !packages.length
+ // if there's an argument and no package has been explicitly asked for
+ // check the local and global bin paths for a binary named the same as
+ // the argument and run it if it exists, otherwise fall through to
+ // the behavior of treating the single argument as a package name
+ if (needPackageCommandSwap) {
+ let binExists = false
+ if (await fileExists(`${this.npm.localBin}/${args[0]}`)) {
+ pathArr.unshift(this.npm.localBin)
+ binExists = true
+ } else if (await fileExists(`${this.npm.globalBin}/${args[0]}`)) {
+ pathArr.unshift(this.npm.globalBin)
+ binExists = true
+ }
- // If we do `npm exec foo`, and have a `foo` locally, then we'll
- // always use that, so we don't really need to fetch the manifest.
- // So: run npa on each packages entry, and if it is a name with a
- // rawSpec==='', then try to readPackageJson at
- // node_modules/${name}/package.json, and only pacote fetch if
- // that fails.
- const manis = await Promise.all(packages.map(async p => {
- const spec = npa(p, npm.localPrefix)
- if (spec.type === 'tag' && spec.rawSpec === '') {
- // fall through to the pacote.manifest() approach
- try {
- const pj = resolve(npm.localPrefix, 'node_modules', spec.name)
- return await readPackageJson(pj)
- } catch (er) {}
+ if (binExists) {
+ return await this.run({
+ args,
+ call,
+ pathArr,
+ shell,
+ })
+ }
+
+ packages.push(args[0])
}
- // Force preferOnline to true so we are making sure to pull in the latest
- // This is especially useful if the user didn't give us a version, and
- // they expect to be running @latest
- return await pacote.manifest(p, {
- ...npm.flatOptions,
- preferOnline: true,
- })
- }))
-
- if (needPackageCommandSwap)
- args[0] = getBinFromManifest(manis[0])
-
- // figure out whether we need to install stuff, or if local is fine
- const localArb = new Arborist({
- ...npm.flatOptions,
- path: npm.localPrefix,
- })
- const tree = await localArb.loadActual()
-
- // do we have all the packages in manifest list?
- const needInstall = manis.some(mani => manifestMissing(tree, mani))
-
- if (needInstall) {
- const installDir = cacheInstallDir(packages)
- await mkdirp(installDir)
- const arb = new Arborist({ ...npm.flatOptions, path: installDir })
- const tree = await arb.loadActual()
-
- // at this point, we have to ensure that we get the exact same
- // version, because it's something that has only ever been installed
- // by npm exec in the cache install directory
- const add = manis.filter(mani => manifestMissing(tree, {
- ...mani,
- _from: `${mani.name}@${mani.version}`,
+
+ // If we do `npm exec foo`, and have a `foo` locally, then we'll
+ // always use that, so we don't really need to fetch the manifest.
+ // So: run npa on each packages entry, and if it is a name with a
+ // rawSpec==='', then try to readPackageJson at
+ // node_modules/${name}/package.json, and only pacote fetch if
+ // that fails.
+ const manis = await Promise.all(packages.map(async p => {
+ const spec = npa(p, this.npm.localPrefix)
+ if (spec.type === 'tag' && spec.rawSpec === '') {
+ // fall through to the pacote.manifest() approach
+ try {
+ const pj = resolve(this.npm.localPrefix, 'node_modules', spec.name)
+ return await readPackageJson(pj)
+ } catch (er) {}
+ }
+ // Force preferOnline to true so we are making sure to pull in the latest
+ // This is especially useful if the user didn't give us a version, and
+ // they expect to be running @latest
+ return await pacote.manifest(p, {
+ ...this.npm.flatOptions,
+ preferOnline: true,
+ })
}))
- .map(mani => mani._from)
- .sort((a, b) => a.localeCompare(b))
-
- // no need to install if already present
- if (add.length) {
- if (!npm.flatOptions.yes) {
- // set -n to always say no
- if (npm.flatOptions.yes === false)
- throw 'canceled'
-
- if (!process.stdin.isTTY || ciDetect()) {
- npm.log.warn('exec', `The following package${
+
+ if (needPackageCommandSwap)
+ args[0] = this.getBinFromManifest(manis[0])
+
+ // figure out whether we need to install stuff, or if local is fine
+ const localArb = new Arborist({
+ ...this.npm.flatOptions,
+ path: this.npm.localPrefix,
+ })
+ const tree = await localArb.loadActual()
+
+ // do we have all the packages in manifest list?
+ const needInstall = manis.some(mani => this.manifestMissing(tree, mani))
+
+ if (needInstall) {
+ const installDir = this.cacheInstallDir(packages)
+ await mkdirp(installDir)
+ const arb = new Arborist({ ...this.npm.flatOptions, path: installDir })
+ const tree = await arb.loadActual()
+
+ // at this point, we have to ensure that we get the exact same
+ // version, because it's something that has only ever been installed
+ // by npm exec in the cache install directory
+ const add = manis.filter(mani => this.manifestMissing(tree, {
+ ...mani,
+ _from: `${mani.name}@${mani.version}`,
+ }))
+ .map(mani => mani._from)
+ .sort((a, b) => a.localeCompare(b))
+
+ // no need to install if already present
+ if (add.length) {
+ if (!this.npm.flatOptions.yes) {
+ // set -n to always say no
+ if (this.npm.flatOptions.yes === false)
+ throw 'canceled'
+
+ if (!process.stdin.isTTY || ciDetect()) {
+ this.npm.log.warn('exec', `The following package${
add.length === 1 ? ' was' : 's were'
} not found and will be installed: ${
add.map((pkg) => pkg.replace(/@$/, '')).join(', ')
}`)
- } else {
- const addList = add.map(a => ` ${a.replace(/@$/, '')}`)
- .join('\n') + '\n'
- const prompt = `Need to install the following packages:\n${
+ } else {
+ const addList = add.map(a => ` ${a.replace(/@$/, '')}`)
+ .join('\n') + '\n'
+ const prompt = `Need to install the following packages:\n${
addList
}Ok to proceed? `
- const confirm = await read({ prompt, default: 'y' })
- if (confirm.trim().toLowerCase().charAt(0) !== 'y')
- throw 'canceled'
+ const confirm = await read({ prompt, default: 'y' })
+ if (confirm.trim().toLowerCase().charAt(0) !== 'y')
+ throw 'canceled'
+ }
}
+ await arb.reify({ ...this.npm.flatOptions, add })
}
- await arb.reify({ ...npm.flatOptions, add })
+ pathArr.unshift(resolve(installDir, 'node_modules/.bin'))
}
- pathArr.unshift(resolve(installDir, 'node_modules/.bin'))
+
+ return await this.run({ args, call, pathArr, shell })
}
- return await run({ args, call, pathArr, shell })
-}
+ async run ({ args, call, pathArr, shell }) {
+ // turn list of args into command string
+ const script = call || args.shift() || shell
+
+ // do the fakey runScript dance
+ // still should work if no package.json in cwd
+ const realPkg = await readPackageJson(`${this.npm.localPrefix}/package.json`)
+ .catch(() => ({}))
+ const pkg = {
+ ...realPkg,
+ scripts: {
+ ...(realPkg.scripts || {}),
+ npx: script,
+ },
+ }
-const manifestMissing = (tree, mani) => {
- // if the tree doesn't have a child by that name/version, return true
- // true means we need to install it
- const child = tree.children.get(mani.name)
- // if no child, we have to load it
- if (!child)
- return true
+ this.npm.log.disableProgress()
+ try {
+ if (script === shell) {
+ if (process.stdin.isTTY) {
+ if (ciDetect())
+ return this.npm.log.warn('exec', 'Interactive mode disabled in CI environment')
+ output(`\nEntering npm script environment\nType 'exit' or ^D when finished\n`)
+ }
+ }
+ return await runScript({
+ ...this.npm.flatOptions,
+ pkg,
+ banner: false,
+ // we always run in cwd, not --prefix
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ args,
+ env: {
+ PATH: pathArr.join(delimiter),
+ },
+ stdio: 'inherit',
+ })
+ } finally {
+ this.npm.log.enableProgress()
+ }
+ }
- // if no version/tag specified, allow whatever's there
- if (mani._from === `${mani.name}@`)
- return false
+ manifestMissing (tree, mani) {
+ // if the tree doesn't have a child by that name/version, return true
+ // true means we need to install it
+ const child = tree.children.get(mani.name)
+ // if no child, we have to load it
+ if (!child)
+ return true
- // otherwise the version has to match what we WOULD get
- return child.version !== mani.version
-}
+ // if no version/tag specified, allow whatever's there
+ if (mani._from === `${mani.name}@`)
+ return false
-const getBinFromManifest = mani => {
- // if we have a bin matching (unscoped portion of) packagename, use that
- // otherwise if there's 1 bin or all bin value is the same (alias), use that,
- // otherwise fail
- const bin = mani.bin || {}
- if (new Set(Object.values(bin)).size === 1)
- return Object.keys(bin)[0]
-
- // XXX probably a util to parse this better?
- const name = mani.name.replace(/^@[^/]+\//, '')
- if (bin[name])
- return name
-
- // XXX need better error message
- throw Object.assign(new Error('could not determine executable to run'), {
- pkgid: mani._id,
- })
-}
+ // otherwise the version has to match what we WOULD get
+ return child.version !== mani.version
+ }
-// only packages not found in ${prefix}/node_modules
-const cacheInstallDir = packages =>
- resolve(npm.config.get('cache'), '_npx', getHash(packages))
+ getBinFromManifest (mani) {
+ // if we have a bin matching (unscoped portion of) packagename, use that
+ // otherwise if there's 1 bin or all bin value is the same (alias), use
+ // that, otherwise fail
+ const bin = mani.bin || {}
+ if (new Set(Object.values(bin)).size === 1)
+ return Object.keys(bin)[0]
+
+ // XXX probably a util to parse this better?
+ const name = mani.name.replace(/^@[^/]+\//, '')
+ if (bin[name])
+ return name
+
+ // XXX need better error message
+ throw Object.assign(new Error('could not determine executable to run'), {
+ pkgid: mani._id,
+ })
+ }
-const getHash = packages =>
- crypto.createHash('sha512')
- .update(packages.sort((a, b) => a.localeCompare(b)).join('\n'))
- .digest('hex')
- .slice(0, 16)
+ cacheInstallDir (packages) {
+ // only packages not found in ${prefix}/node_modules
+ return resolve(this.npm.config.get('cache'), '_npx', this.getHash(packages))
+ }
-module.exports = Object.assign(cmd, { usage })
+ getHash (packages) {
+ return crypto.createHash('sha512')
+ .update(packages.sort((a, b) => a.localeCompare(b)).join('\n'))
+ .digest('hex')
+ .slice(0, 16)
+ }
+}
+module.exports = Exec
diff --git a/deps/npm/lib/explain.js b/deps/npm/lib/explain.js
index a0a4427bcc..01541040ef 100644
--- a/deps/npm/lib/explain.js
+++ b/deps/npm/lib/explain.js
@@ -1,5 +1,4 @@
const usageUtil = require('./utils/usage.js')
-const npm = require('./npm.js')
const { explainNode } = require('./utils/explain-dep.js')
const completion = require('./utils/completion/installed-deep.js')
const output = require('./utils/output.js')
@@ -9,86 +8,101 @@ const semver = require('semver')
const { relative, resolve } = require('path')
const validName = require('validate-npm-package-name')
-const usage = usageUtil('explain', 'npm explain <folder | specifier>')
-
-const cmd = (args, cb) => explain(args).then(() => cb()).catch(cb)
+class Explain {
+ constructor (npm) {
+ this.npm = npm
+ }
-const explain = async (args) => {
- if (!args.length)
- throw usage
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('explain', 'npm explain <folder | specifier>')
+ }
- const arb = new Arborist({ path: npm.prefix, ...npm.flatOptions })
- const tree = await arb.loadActual()
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
- const nodes = new Set()
- for (const arg of args) {
- for (const node of getNodes(tree, arg))
- nodes.add(node)
+ exec (args, cb) {
+ this.explain(args).then(() => cb()).catch(cb)
}
- if (nodes.size === 0)
- throw `No dependencies found matching ${args.join(', ')}`
- const expls = []
- for (const node of nodes) {
- const { extraneous, dev, optional, devOptional, peer, inBundle } = node
- const expl = node.explain()
- if (extraneous)
- expl.extraneous = true
- else {
- expl.dev = dev
- expl.optional = optional
- expl.devOptional = devOptional
- expl.peer = peer
- expl.bundled = inBundle
+ async explain (args) {
+ if (!args.length)
+ throw this.usage
+
+ const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions })
+ const tree = await arb.loadActual()
+
+ const nodes = new Set()
+ for (const arg of args) {
+ for (const node of this.getNodes(tree, arg))
+ nodes.add(node)
}
- expls.push(expl)
- }
+ if (nodes.size === 0)
+ throw `No dependencies found matching ${args.join(', ')}`
- if (npm.flatOptions.json)
- output(JSON.stringify(expls, null, 2))
- else {
- output(expls.map(expl => {
- return explainNode(expl, Infinity, npm.color)
- }).join('\n\n'))
+ const expls = []
+ for (const node of nodes) {
+ const { extraneous, dev, optional, devOptional, peer, inBundle } = node
+ const expl = node.explain()
+ if (extraneous)
+ expl.extraneous = true
+ else {
+ expl.dev = dev
+ expl.optional = optional
+ expl.devOptional = devOptional
+ expl.peer = peer
+ expl.bundled = inBundle
+ }
+ expls.push(expl)
+ }
+
+ if (this.npm.flatOptions.json)
+ output(JSON.stringify(expls, null, 2))
+ else {
+ output(expls.map(expl => {
+ return explainNode(expl, Infinity, this.npm.color)
+ }).join('\n\n'))
+ }
}
-}
-const getNodes = (tree, arg) => {
- // if it's just a name, return packages by that name
- const { validForOldPackages: valid } = validName(arg)
- if (valid)
- return tree.inventory.query('name', arg)
+ getNodes (tree, arg) {
+ // if it's just a name, return packages by that name
+ const { validForOldPackages: valid } = validName(arg)
+ if (valid)
+ return tree.inventory.query('name', arg)
- // if it's a location, get that node
- const maybeLoc = arg.replace(/\\/g, '/').replace(/\/+$/, '')
- const nodeByLoc = tree.inventory.get(maybeLoc)
- if (nodeByLoc)
- return [nodeByLoc]
+ // if it's a location, get that node
+ const maybeLoc = arg.replace(/\\/g, '/').replace(/\/+$/, '')
+ const nodeByLoc = tree.inventory.get(maybeLoc)
+ if (nodeByLoc)
+ return [nodeByLoc]
- // maybe a path to a node_modules folder
- const maybePath = relative(npm.prefix, resolve(maybeLoc))
- .replace(/\\/g, '/').replace(/\/+$/, '')
- const nodeByPath = tree.inventory.get(maybePath)
- if (nodeByPath)
- return [nodeByPath]
+ // maybe a path to a node_modules folder
+ const maybePath = relative(this.npm.prefix, resolve(maybeLoc))
+ .replace(/\\/g, '/').replace(/\/+$/, '')
+ const nodeByPath = tree.inventory.get(maybePath)
+ if (nodeByPath)
+ return [nodeByPath]
- // otherwise, try to select all matching nodes
- try {
- return getNodesByVersion(tree, arg)
- } catch (er) {
- return []
+ // otherwise, try to select all matching nodes
+ try {
+ return this.getNodesByVersion(tree, arg)
+ } catch (er) {
+ return []
+ }
}
-}
-const getNodesByVersion = (tree, arg) => {
- const spec = npa(arg, npm.prefix)
- if (spec.type !== 'version' && spec.type !== 'range')
- return []
+ getNodesByVersion (tree, arg) {
+ const spec = npa(arg, this.npm.prefix)
+ if (spec.type !== 'version' && spec.type !== 'range')
+ return []
- return tree.inventory.filter(node => {
- return node.package.name === spec.name &&
- semver.satisfies(node.package.version, spec.rawSpec)
- })
+ return tree.inventory.filter(node => {
+ return node.package.name === spec.name &&
+ semver.satisfies(node.package.version, spec.rawSpec)
+ })
+ }
}
-
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = Explain
diff --git a/deps/npm/lib/explore.js b/deps/npm/lib/explore.js
index e9b09707ec..fdfe6e1bcf 100644
--- a/deps/npm/lib/explore.js
+++ b/deps/npm/lib/explore.js
@@ -1,69 +1,82 @@
// npm explore <pkg>[@<version>]
// open a subshell to the package folder.
-const usageUtil = require('./utils/usage.js')
-const completion = require('./utils/completion/installed-shallow.js')
-const usage = usageUtil('explore', 'npm explore <pkg> [ -- <command>]')
const rpj = require('read-package-json-fast')
-
-const cmd = (args, cb) => explore(args).then(() => cb()).catch(cb)
-
-const output = require('./utils/output.js')
-const npm = require('./npm.js')
-
const runScript = require('@npmcli/run-script')
const { join, resolve, relative } = require('path')
+const completion = require('./utils/completion/installed-shallow.js')
+const output = require('./utils/output.js')
+const usageUtil = require('./utils/usage.js')
-const explore = async args => {
- if (args.length < 1 || !args[0])
- throw usage
+class Explore {
+ constructor (npm) {
+ this.npm = npm
+ }
- const pkgname = args.shift()
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('explore', 'npm explore <pkg> [ -- <command>]')
+ }
- // detect and prevent any .. shenanigans
- const path = join(npm.dir, join('/', pkgname))
- if (relative(path, npm.dir) === '')
- throw usage
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
- // run as if running a script named '_explore', which we set to either
- // the set of arguments, or the shell config, and let @npmcli/run-script
- // handle all the escaping and PATH setup stuff.
+ exec (args, cb) {
+ this.explore(args).then(() => cb()).catch(cb)
+ }
- const pkg = await rpj(resolve(path, 'package.json')).catch(er => {
- npm.log.error('explore', `It doesn't look like ${pkgname} is installed.`)
- throw er
- })
+ async explore (args) {
+ if (args.length < 1 || !args[0])
+ throw this.usage
- const { shell } = npm.flatOptions
- pkg.scripts = {
- ...(pkg.scripts || {}),
- _explore: args.join(' ').trim() || shell,
- }
+ const pkgname = args.shift()
+
+ // detect and prevent any .. shenanigans
+ const path = join(this.npm.dir, join('/', pkgname))
+ if (relative(path, this.npm.dir) === '')
+ throw this.usage
- if (!args.length)
- output(`\nExploring ${path}\nType 'exit' or ^D when finished\n`)
- npm.log.disableProgress()
- try {
- return await runScript({
- ...npm.flatOptions,
- pkg,
- banner: false,
- path,
- stdioString: true,
- event: '_explore',
- stdio: 'inherit',
- }).catch(er => {
- process.exitCode = typeof er.code === 'number' && er.code !== 0 ? er.code
- : 1
- // if it's not an exit error, or non-interactive, throw it
- const isProcExit = er.message === 'command failed' &&
- (typeof er.code === 'number' || /^SIG/.test(er.signal || ''))
- if (args.length || !isProcExit)
- throw er
+ // run as if running a script named '_explore', which we set to either
+ // the set of arguments, or the shell config, and let @npmcli/run-script
+ // handle all the escaping and PATH setup stuff.
+
+ const pkg = await rpj(resolve(path, 'package.json')).catch(er => {
+ this.npm.log.error('explore', `It doesn't look like ${pkgname} is installed.`)
+ throw er
})
- } finally {
- npm.log.enableProgress()
+
+ const { shell } = this.npm.flatOptions
+ pkg.scripts = {
+ ...(pkg.scripts || {}),
+ _explore: args.join(' ').trim() || shell,
+ }
+
+ if (!args.length)
+ output(`\nExploring ${path}\nType 'exit' or ^D when finished\n`)
+ this.npm.log.disableProgress()
+ try {
+ return await runScript({
+ ...this.npm.flatOptions,
+ pkg,
+ banner: false,
+ path,
+ stdioString: true,
+ event: '_explore',
+ stdio: 'inherit',
+ }).catch(er => {
+ process.exitCode = typeof er.code === 'number' && er.code !== 0 ? er.code
+ : 1
+ // if it's not an exit error, or non-interactive, throw it
+ const isProcExit = er.message === 'command failed' &&
+ (typeof er.code === 'number' || /^SIG/.test(er.signal || ''))
+ if (args.length || !isProcExit)
+ throw er
+ })
+ } finally {
+ this.npm.log.enableProgress()
+ }
}
}
-
-module.exports = Object.assign(cmd, { completion, usage })
+module.exports = Explore
diff --git a/deps/npm/lib/find-dupes.js b/deps/npm/lib/find-dupes.js
index 19e7ea6a7c..5061be9cc3 100644
--- a/deps/npm/lib/find-dupes.js
+++ b/deps/npm/lib/find-dupes.js
@@ -1,8 +1,19 @@
// dedupe duplicated packages, or find them in the tree
-const dedupe = require('./dedupe.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('find-dupes', 'npm find-dupes')
-const cmd = (args, cb) => dedupe({ dryRun: true }, cb)
+class FindDupes {
+ constructor (npm) {
+ this.npm = npm
+ }
-module.exports = Object.assign(cmd, { usage })
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('find-dupes', 'npm find-dupes')
+ }
+
+ exec (args, cb) {
+ this.npm.config.set('dry-run', true)
+ this.npm.commands.dedupe([], cb)
+ }
+}
+module.exports = FindDupes
diff --git a/deps/npm/lib/fund.js b/deps/npm/lib/fund.js
index 41dd48c465..1e97242664 100644
--- a/deps/npm/lib/fund.js
+++ b/deps/npm/lib/fund.js
@@ -11,200 +11,210 @@ const {
isValidFunding,
} = require('libnpmfund')
-const npm = require('./npm.js')
const completion = require('./utils/completion/installed-deep.js')
const output = require('./utils/output.js')
const openUrl = require('./utils/open-url.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'fund',
- 'npm fund',
- 'npm fund [--json] [--browser] [--unicode] [[<@scope>/]<pkg> [--which=<fundingSourceNumber>]'
-)
-
-const cmd = (args, cb) => fund(args).then(() => cb()).catch(cb)
-
-function printJSON (fundingInfo) {
- return JSON.stringify(fundingInfo, null, 2)
-}
-
const getPrintableName = ({ name, version }) => {
const printableVersion = version ? `@${version}` : ''
return `${name}${printableVersion}`
}
-function printHuman (fundingInfo, { color, unicode }) {
- const seenUrls = new Map()
-
- const tree = obj =>
- archy(obj, '', { unicode })
-
- const result = depth({
- tree: fundingInfo,
-
- // composes human readable package name
- // and creates a new archy item for readable output
- visit: ({ name, version, funding }) => {
- const [fundingSource] = []
- .concat(normalizeFunding(funding))
- .filter(isValidFunding)
- const { url } = fundingSource || {}
- const pkgRef = getPrintableName({ name, version })
- let item = {
- label: pkgRef,
- }
-
- if (url) {
- item.label = tree({
- label: color ? chalk.bgBlack.white(url) : url,
- nodes: [pkgRef],
- }).trim()
-
- // stacks all packages together under the same item
- if (seenUrls.has(url)) {
- item = seenUrls.get(url)
- item.label += `, ${pkgRef}`
- return null
- } else
- seenUrls.set(url, item)
- }
-
- return item
- },
-
- // puts child nodes back into returned archy
- // output while also filtering out missing items
- leave: (item, children) => {
- if (item)
- item.nodes = children.filter(Boolean)
-
- return item
- },
-
- // turns tree-like object return by libnpmfund
- // into children to be properly read by treeverse
- getChildren: (node) =>
- Object.keys(node.dependencies || {})
- .map(key => ({
- name: key,
- ...node.dependencies[key],
- })),
- })
-
- const res = tree(result)
- return color ? chalk.reset(res) : res
-}
+class Fund {
+ constructor (npm) {
+ this.npm = npm
+ }
-async function openFundingUrl ({ path, tree, spec, fundingSourceNumber }) {
- const arg = npa(spec, path)
- const retrievePackageMetadata = () => {
- if (arg.type === 'directory') {
- if (tree.path === arg.fetchSpec) {
- // matches cwd, e.g: npm fund .
- return tree.package
- } else {
- // matches any file path within current arborist inventory
- for (const item of tree.inventory.values()) {
- if (item.path === arg.fetchSpec)
- return item.package
- }
- }
- } else {
- // tries to retrieve a package from arborist inventory
- // by matching resulted package name from the provided spec
- const [item] = [...tree.inventory.query('name', arg.name)]
- .filter(i => semver.valid(i.package.version))
- .sort((a, b) => semver.rcompare(a.package.version, b.package.version))
-
- if (item)
- return item.package
- }
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'fund',
+ 'npm fund',
+ 'npm fund [--json] [--browser] [--unicode] [[<@scope>/]<pkg> [--which=<fundingSourceNumber>]'
+ )
}
- const { funding } = retrievePackageMetadata() ||
- await pacote.manifest(arg, npm.flatOptions).catch(() => ({}))
-
- const validSources = []
- .concat(normalizeFunding(funding))
- .filter(isValidFunding)
-
- const matchesValidSource =
- validSources.length === 1 ||
- (fundingSourceNumber > 0 && fundingSourceNumber <= validSources.length)
-
- if (matchesValidSource) {
- const index = fundingSourceNumber ? fundingSourceNumber - 1 : 0
- const { type, url } = validSources[index]
- const typePrefix = type ? `${type} funding` : 'Funding'
- const msg = `${typePrefix} available at the following URL`
- return new Promise((resolve, reject) =>
- openUrl(url, msg, err => err
- ? reject(err)
- : resolve()
- ))
- } else if (validSources.length && !(fundingSourceNumber >= 1)) {
- validSources.forEach(({ type, url }, i) => {
- const typePrefix = type ? `${type} funding` : 'Funding'
- const msg = `${typePrefix} available at the following URL`
- output(`${i + 1}: ${msg}: ${url}`)
- })
- output('Run `npm fund [<@scope>/]<pkg> --which=1`, for example, to open the first funding URL listed in that package')
- } else {
- const noFundingError = new Error(`No valid funding method available for: ${spec}`)
- noFundingError.code = 'ENOFUND'
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
- throw noFundingError
+ exec (args, cb) {
+ this.fund(args).then(() => cb()).catch(cb)
}
-}
-const fund = async (args) => {
- const opts = npm.flatOptions
- const spec = args[0]
- const numberArg = opts.which
+ async fund (args) {
+ const opts = this.npm.flatOptions
+ const spec = args[0]
+ const numberArg = opts.which
- const fundingSourceNumber = numberArg && parseInt(numberArg, 10)
+ const fundingSourceNumber = numberArg && parseInt(numberArg, 10)
- const badFundingSourceNumber =
- numberArg !== undefined &&
+ const badFundingSourceNumber =
+ numberArg !== undefined &&
(String(fundingSourceNumber) !== numberArg || fundingSourceNumber < 1)
- if (badFundingSourceNumber) {
- const err = new Error('`npm fund [<@scope>/]<pkg> [--which=fundingSourceNumber]` must be given a positive integer')
- err.code = 'EFUNDNUMBER'
- throw err
+ if (badFundingSourceNumber) {
+ const err = new Error('`npm fund [<@scope>/]<pkg> [--which=fundingSourceNumber]` must be given a positive integer')
+ err.code = 'EFUNDNUMBER'
+ throw err
+ }
+
+ if (opts.global) {
+ const err = new Error('`npm fund` does not support global packages')
+ err.code = 'EFUNDGLOBAL'
+ throw err
+ }
+
+ const where = this.npm.prefix
+ const arb = new Arborist({ ...opts, path: where })
+ const tree = await arb.loadActual()
+
+ if (spec) {
+ await this.openFundingUrl({
+ path: where,
+ tree,
+ spec,
+ fundingSourceNumber,
+ })
+ return
+ }
+
+ const print = opts.json
+ ? this.printJSON
+ : this.printHuman
+
+ output(
+ print(
+ getFundingInfo(tree),
+ opts
+ )
+ )
}
- if (opts.global) {
- const err = new Error('`npm fund` does not support global packages')
- err.code = 'EFUNDGLOBAL'
- throw err
+ printJSON (fundingInfo) {
+ return JSON.stringify(fundingInfo, null, 2)
}
- const where = npm.prefix
- const arb = new Arborist({ ...opts, path: where })
- const tree = await arb.loadActual()
+ printHuman (fundingInfo, { color, unicode }) {
+ const seenUrls = new Map()
+
+ const tree = obj =>
+ archy(obj, '', { unicode })
+
+ const result = depth({
+ tree: fundingInfo,
+
+ // composes human readable package name
+ // and creates a new archy item for readable output
+ visit: ({ name, version, funding }) => {
+ const [fundingSource] = []
+ .concat(normalizeFunding(funding))
+ .filter(isValidFunding)
+ const { url } = fundingSource || {}
+ const pkgRef = getPrintableName({ name, version })
+ let item = {
+ label: pkgRef,
+ }
+
+ if (url) {
+ item.label = tree({
+ label: color ? chalk.bgBlack.white(url) : url,
+ nodes: [pkgRef],
+ }).trim()
+
+ // stacks all packages together under the same item
+ if (seenUrls.has(url)) {
+ item = seenUrls.get(url)
+ item.label += `, ${pkgRef}`
+ return null
+ } else
+ seenUrls.set(url, item)
+ }
- if (spec) {
- await openFundingUrl({
- path: where,
- tree,
- spec,
- fundingSourceNumber,
+ return item
+ },
+
+ // puts child nodes back into returned archy
+ // output while also filtering out missing items
+ leave: (item, children) => {
+ if (item)
+ item.nodes = children.filter(Boolean)
+
+ return item
+ },
+
+ // turns tree-like object return by libnpmfund
+ // into children to be properly read by treeverse
+ getChildren: (node) =>
+ Object.keys(node.dependencies || {})
+ .map(key => ({
+ name: key,
+ ...node.dependencies[key],
+ })),
})
- return
+
+ const res = tree(result)
+ return color ? chalk.reset(res) : res
}
- const print = opts.json
- ? printJSON
- : printHuman
+ async openFundingUrl ({ path, tree, spec, fundingSourceNumber }) {
+ const arg = npa(spec, path)
+ const retrievePackageMetadata = () => {
+ if (arg.type === 'directory') {
+ if (tree.path === arg.fetchSpec) {
+ // matches cwd, e.g: npm fund .
+ return tree.package
+ } else {
+ // matches any file path within current arborist inventory
+ for (const item of tree.inventory.values()) {
+ if (item.path === arg.fetchSpec)
+ return item.package
+ }
+ }
+ } else {
+ // tries to retrieve a package from arborist inventory
+ // by matching resulted package name from the provided spec
+ const [item] = [...tree.inventory.query('name', arg.name)]
+ .filter(i => semver.valid(i.package.version))
+ .sort((a, b) => semver.rcompare(a.package.version, b.package.version))
+
+ if (item)
+ return item.package
+ }
+ }
+
+ const { funding } = retrievePackageMetadata() ||
+ await pacote.manifest(arg, this.npm.flatOptions).catch(() => ({}))
- output(
- print(
- getFundingInfo(tree),
- opts
- )
- )
-}
+ const validSources = []
+ .concat(normalizeFunding(funding))
+ .filter(isValidFunding)
+
+ const matchesValidSource =
+ validSources.length === 1 ||
+ (fundingSourceNumber > 0 && fundingSourceNumber <= validSources.length)
+
+ if (matchesValidSource) {
+ const index = fundingSourceNumber ? fundingSourceNumber - 1 : 0
+ const { type, url } = validSources[index]
+ const typePrefix = type ? `${type} funding` : 'Funding'
+ const msg = `${typePrefix} available at the following URL`
+ return openUrl(this.npm, url, msg)
+ } else if (validSources.length && !(fundingSourceNumber >= 1)) {
+ validSources.forEach(({ type, url }, i) => {
+ const typePrefix = type ? `${type} funding` : 'Funding'
+ const msg = `${typePrefix} available at the following URL`
+ output(`${i + 1}: ${msg}: ${url}`)
+ })
+ output('Run `npm fund [<@scope>/]<pkg> --which=1`, for example, to open the first funding URL listed in that package')
+ } else {
+ const noFundingError = new Error(`No valid funding method available for: ${spec}`)
+ noFundingError.code = 'ENOFUND'
-module.exports = Object.assign(cmd, { usage, completion })
+ throw noFundingError
+ }
+ }
+}
+module.exports = Fund
diff --git a/deps/npm/lib/get.js b/deps/npm/lib/get.js
index 8a416027d7..a5b2f55144 100644
--- a/deps/npm/lib/get.js
+++ b/deps/npm/lib/get.js
@@ -1,15 +1,25 @@
-const npm = require('./npm.js')
-const config = require('./config.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'get',
- 'npm get [<key> ...] (See `npm config`)'
-)
+class Get {
+ constructor (npm) {
+ this.npm = npm
+ }
-const completion = config.completion
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'get',
+ 'npm get [<key> ...] (See `npm config`)'
+ )
+ }
-const cmd = (args, cb) =>
- npm.commands.config(['get'].concat(args), cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return this.npm.commands.config.completion(opts)
+ }
-module.exports = Object.assign(cmd, { usage, completion })
+ exec (args, cb) {
+ this.npm.commands.config(['get'].concat(args), cb)
+ }
+}
+module.exports = Get
diff --git a/deps/npm/lib/help-search.js b/deps/npm/lib/help-search.js
index b184735048..ed2bc23b91 100644
--- a/deps/npm/lib/help-search.js
+++ b/deps/npm/lib/help-search.js
@@ -1,203 +1,211 @@
const fs = require('fs')
const path = require('path')
-const npm = require('./npm.js')
const color = require('ansicolors')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
+const npmUsage = require('./utils/npm-usage.js')
const { promisify } = require('util')
const glob = promisify(require('glob'))
const readFile = promisify(fs.readFile)
const didYouMean = require('./utils/did-you-mean.js')
const { cmdList } = require('./utils/cmd-list.js')
-const usage = usageUtil('help-search', 'npm help-search <text>')
-
-const npmUsage = require('./utils/npm-usage.js')
+class HelpSearch {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => helpSearch(args).then(() => cb()).catch(cb)
+ get usage () {
+ return usageUtil('help-search', 'npm help-search <text>')
+ }
-const helpSearch = async args => {
- if (!args.length)
- throw usage
+ exec (args, cb) {
+ this.helpSearch(args).then(() => cb()).catch(cb)
+ }
- const docPath = path.resolve(__dirname, '..', 'docs/content')
+ async helpSearch (args) {
+ if (!args.length)
+ throw this.usage
+
+ const docPath = path.resolve(__dirname, '..', 'docs/content')
+
+ const files = await glob(`${docPath}/*/*.md`)
+ const data = await this.readFiles(files)
+ const results = await this.searchFiles(args, data, files)
+ // if only one result, then just show that help section.
+ if (results.length === 1) {
+ return this.npm.commands.help([path.basename(results[0].file, '.md')], er => {
+ if (er)
+ throw er
+ })
+ }
- const files = await glob(`${docPath}/*/*.md`)
- const data = await readFiles(files)
- const results = await searchFiles(args, data, files)
- // if only one result, then just show that help section.
- if (results.length === 1) {
- return npm.commands.help([path.basename(results[0].file, '.md')], er => {
- if (er)
- throw er
- })
+ const formatted = this.formatResults(args, results)
+ if (!formatted.trim())
+ npmUsage(this.npm, false)
+ else {
+ output(formatted)
+ output(didYouMean(args[0], cmdList))
+ }
}
- const formatted = formatResults(args, results)
- if (!formatted.trim())
- npmUsage(false)
- else {
- output(formatted)
- output(didYouMean(args[0], cmdList))
+ async readFiles (files) {
+ const res = {}
+ await Promise.all(files.map(async file => {
+ res[file] = (await readFile(file, 'utf8'))
+ .replace(/^---\n(.*\n)*?---\n/, '').trim()
+ }))
+ return res
}
-}
-const readFiles = async files => {
- const res = {}
- await Promise.all(files.map(async file => {
- res[file] = (await readFile(file, 'utf8'))
- .replace(/^---\n(.*\n)*?---\n/, '').trim()
- }))
- return res
-}
+ async searchFiles (args, data, files) {
+ const results = []
+ for (const [file, content] of Object.entries(data)) {
+ const lowerCase = content.toLowerCase()
+ // skip if no matches at all
+ if (!args.some(a => lowerCase.includes(a.toLowerCase())))
+ continue
+
+ const lines = content.split(/\n+/)
+
+ // if a line has a search term, then skip it and the next line.
+ // if the next line has a search term, then skip all 3
+ // otherwise, set the line to null. then remove the nulls.
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i]
+ const nextLine = lines[i + 1]
+ let match = false
+ if (nextLine) {
+ match = args.some(a =>
+ nextLine.toLowerCase().includes(a.toLowerCase()))
+ if (match) {
+ // skip over the next line, and the line after it.
+ i += 2
+ continue
+ }
+ }
+
+ match = args.some(a => line.toLowerCase().includes(a.toLowerCase()))
-const searchFiles = async (args, data, files) => {
- const results = []
- for (const [file, content] of Object.entries(data)) {
- const lowerCase = content.toLowerCase()
- // skip if no matches at all
- if (!args.some(a => lowerCase.includes(a.toLowerCase())))
- continue
-
- const lines = content.split(/\n+/)
-
- // if a line has a search term, then skip it and the next line.
- // if the next line has a search term, then skip all 3
- // otherwise, set the line to null. then remove the nulls.
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i]
- const nextLine = lines[i + 1]
- let match = false
- if (nextLine) {
- match = args.some(a => nextLine.toLowerCase().includes(a.toLowerCase()))
if (match) {
- // skip over the next line, and the line after it.
- i += 2
+ // skip over the next line
+ i++
continue
}
- }
- match = args.some(a => line.toLowerCase().includes(a.toLowerCase()))
-
- if (match) {
- // skip over the next line
- i++
- continue
+ lines[i] = null
}
- lines[i] = null
- }
-
- // now squish any string of nulls into a single null
- const pruned = lines.reduce((l, r) => {
- if (!(r === null && l[l.length - 1] === null))
- l.push(r)
+ // now squish any string of nulls into a single null
+ const pruned = lines.reduce((l, r) => {
+ if (!(r === null && l[l.length - 1] === null))
+ l.push(r)
- return l
- }, [])
+ return l
+ }, [])
- if (pruned[pruned.length - 1] === null)
- pruned.pop()
+ if (pruned[pruned.length - 1] === null)
+ pruned.pop()
- if (pruned[0] === null)
- pruned.shift()
+ if (pruned[0] === null)
+ pruned.shift()
- // now count how many args were found
- const found = {}
- let totalHits = 0
- for (const line of pruned) {
- for (const arg of args) {
- const hit = (line || '').toLowerCase()
- .split(arg.toLowerCase()).length - 1
+ // now count how many args were found
+ const found = {}
+ let totalHits = 0
+ for (const line of pruned) {
+ for (const arg of args) {
+ const hit = (line || '').toLowerCase()
+ .split(arg.toLowerCase()).length - 1
- if (hit > 0) {
- found[arg] = (found[arg] || 0) + hit
- totalHits += hit
+ if (hit > 0) {
+ found[arg] = (found[arg] || 0) + hit
+ totalHits += hit
+ }
}
}
+
+ const cmd = 'npm help ' +
+ path.basename(file, '.md').replace(/^npm-/, '')
+ results.push({
+ file,
+ cmd,
+ lines: pruned,
+ found: Object.keys(found),
+ hits: found,
+ totalHits,
+ })
}
- const cmd = 'npm help ' +
- path.basename(file, '.md').replace(/^npm-/, '')
- results.push({
- file,
- cmd,
- lines: pruned,
- found: Object.keys(found),
- hits: found,
- totalHits,
- })
+ // sort results by number of results found, then by number of hits
+ // then by number of matching lines
+
+ // coverage is ignored here because the contents of results are
+ // nondeterministic due to either glob or readFiles or Object.entries
+ return results.sort(/* istanbul ignore next */ (a, b) =>
+ a.found.length > b.found.length ? -1
+ : a.found.length < b.found.length ? 1
+ : a.totalHits > b.totalHits ? -1
+ : a.totalHits < b.totalHits ? 1
+ : a.lines.length > b.lines.length ? -1
+ : a.lines.length < b.lines.length ? 1
+ : 0).slice(0, 10)
}
- // sort results by number of results found, then by number of hits
- // then by number of matching lines
-
- // coverage is ignored here because the contents of results are
- // nondeterministic due to either glob or readFiles or Object.entries
- return results.sort(/* istanbul ignore next */ (a, b) =>
- a.found.length > b.found.length ? -1
- : a.found.length < b.found.length ? 1
- : a.totalHits > b.totalHits ? -1
- : a.totalHits < b.totalHits ? 1
- : a.lines.length > b.lines.length ? -1
- : a.lines.length < b.lines.length ? 1
- : 0).slice(0, 10)
-}
-
-const formatResults = (args, results) => {
- const cols = Math.min(process.stdout.columns || Infinity, 80) + 1
+ formatResults (args, results) {
+ const cols = Math.min(process.stdout.columns || Infinity, 80) + 1
- const out = results.map(res => {
- const out = [res.cmd]
- const r = Object.keys(res.hits)
- .map(k => `${k}:${res.hits[k]}`)
- .sort((a, b) => a > b ? 1 : -1)
- .join(' ')
+ const out = results.map(res => {
+ const out = [res.cmd]
+ const r = Object.keys(res.hits)
+ .map(k => `${k}:${res.hits[k]}`)
+ .sort((a, b) => a > b ? 1 : -1)
+ .join(' ')
- out.push(' '.repeat((Math.max(1, cols - out.join(' ').length - r.length - 1))))
- out.push(r)
+ out.push(' '.repeat((Math.max(1, cols - out.join(' ').length - r.length - 1))))
+ out.push(r)
- if (!npm.flatOptions.long)
- return out.join('')
+ if (!this.npm.flatOptions.long)
+ return out.join('')
- out.unshift('\n\n')
- out.push('\n')
- out.push('-'.repeat(cols - 1) + '\n')
- res.lines.forEach((line, i) => {
- if (line === null || i > 3)
- return
+ out.unshift('\n\n')
+ out.push('\n')
+ out.push('-'.repeat(cols - 1) + '\n')
+ res.lines.forEach((line, i) => {
+ if (line === null || i > 3)
+ return
- if (!npm.color) {
- out.push(line + '\n')
- return
- }
- const hilitLine = []
- for (const arg of args) {
- const finder = line.toLowerCase().split(arg.toLowerCase())
- let p = 0
- for (const f of finder) {
- hilitLine.push(line.substr(p, f.length))
- const word = line.substr(p + f.length, arg.length)
- const hilit = color.bgBlack(color.red(word))
- hilitLine.push(hilit)
- p += f.length + arg.length
+ if (!this.npm.color) {
+ out.push(line + '\n')
+ return
}
- }
- out.push(hilitLine.join('') + '\n')
- })
+ const hilitLine = []
+ for (const arg of args) {
+ const finder = line.toLowerCase().split(arg.toLowerCase())
+ let p = 0
+ for (const f of finder) {
+ hilitLine.push(line.substr(p, f.length))
+ const word = line.substr(p + f.length, arg.length)
+ const hilit = color.bgBlack(color.red(word))
+ hilitLine.push(hilit)
+ p += f.length + arg.length
+ }
+ }
+ out.push(hilitLine.join('') + '\n')
+ })
- return out.join('')
- }).join('\n')
+ return out.join('')
+ }).join('\n')
- const finalOut = results.length && !npm.flatOptions.long
- ? 'Top hits for ' + (args.map(JSON.stringify).join(' ')) + '\n' +
+ const finalOut = results.length && !this.npm.flatOptions.long
+ ? 'Top hits for ' + (args.map(JSON.stringify).join(' ')) + '\n' +
'—'.repeat(cols - 1) + '\n' +
out + '\n' +
'—'.repeat(cols - 1) + '\n' +
'(run with -l or --long to see more context)'
- : out
+ : out
- return finalOut.trim()
+ return finalOut.trim()
+ }
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = HelpSearch
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
diff --git a/deps/npm/lib/hook.js b/deps/npm/lib/hook.js
index 7d69ccbf2a..312f542d7c 100644
--- a/deps/npm/lib/hook.js
+++ b/deps/npm/lib/hook.js
@@ -1,53 +1,62 @@
const hookApi = require('libnpmhook')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const relativeDate = require('tiny-relative-date')
const Table = require('cli-table3')
-
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('hook', [
- 'npm hook add <pkg> <url> <secret> [--type=<type>]',
- 'npm hook ls [pkg]',
- 'npm hook rm <id>',
- 'npm hook update <id> <url> <secret>',
-].join('\n'))
-const cmd = (args, cb) => hook(args).then(() => cb()).catch(cb)
+class Hook {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ get usage () {
+ return usageUtil('hook', [
+ 'npm hook add <pkg> <url> <secret> [--type=<type>]',
+ 'npm hook ls [pkg]',
+ 'npm hook rm <id>',
+ 'npm hook update <id> <url> <secret>',
+ ].join('\n'))
+ }
+
+ exec (args, cb) {
+ this.hook(args).then(() => cb()).catch(cb)
+ }
-const hook = async (args) => otplease(npm.flatOptions, opts => {
- switch (args[0]) {
- case 'add':
- return add(args[1], args[2], args[3], opts)
- case 'ls':
- return ls(args[1], opts)
- case 'rm':
- return rm(args[1], opts)
- case 'update':
- case 'up':
- return update(args[1], args[2], args[3], opts)
- default:
- throw usage
+ async hook (args) {
+ return otplease(this.npm.flatOptions, (opts) => {
+ switch (args[0]) {
+ case 'add':
+ return this.add(args[1], args[2], args[3], opts)
+ case 'ls':
+ return this.ls(args[1], opts)
+ case 'rm':
+ return this.rm(args[1], opts)
+ case 'update':
+ case 'up':
+ return this.update(args[1], args[2], args[3], opts)
+ default:
+ throw this.usage
+ }
+ })
}
-})
-const add = (pkg, uri, secret, opts) => {
- hookApi.add(pkg, uri, secret, opts).then(hook => {
+ async add (pkg, uri, secret, opts) {
+ const hook = await hookApi.add(pkg, uri, secret, opts)
if (opts.json)
output(JSON.stringify(hook, null, 2))
else if (opts.parseable) {
output(Object.keys(hook).join('\t'))
output(Object.keys(hook).map(k => hook[k]).join('\t'))
} else if (!opts.silent && opts.loglevel !== 'silent') {
- output(`+ ${hookName(hook)} ${
+ output(`+ ${this.hookName(hook)} ${
opts.unicode ? ' ➜ ' : ' -> '
} ${hook.endpoint}`)
}
- })
-}
+ }
-const ls = (pkg, opts) => {
- return hookApi.ls({ ...opts, package: pkg }).then(hooks => {
+ async ls (pkg, opts) {
+ const hooks = await hookApi.ls({ ...opts, package: pkg })
if (opts.json)
output(JSON.stringify(hooks, null, 2))
else if (opts.parseable) {
@@ -67,7 +76,7 @@ const ls = (pkg, opts) => {
hooks.forEach((hook) => {
table.push([
{ rowSpan: 2, content: hook.id },
- hookName(hook),
+ this.hookName(hook),
hook.endpoint,
])
if (hook.last_delivery) {
@@ -83,46 +92,43 @@ const ls = (pkg, opts) => {
})
output(table.toString())
}
- })
-}
+ }
-const rm = (id, opts) => {
- return hookApi.rm(id, opts).then(hook => {
+ async rm (id, opts) {
+ const hook = await hookApi.rm(id, opts)
if (opts.json)
output(JSON.stringify(hook, null, 2))
else if (opts.parseable) {
output(Object.keys(hook).join('\t'))
output(Object.keys(hook).map(k => hook[k]).join('\t'))
} else if (!opts.silent && opts.loglevel !== 'silent') {
- output(`- ${hookName(hook)} ${
+ output(`- ${this.hookName(hook)} ${
opts.unicode ? ' ✘ ' : ' X '
} ${hook.endpoint}`)
}
- })
-}
+ }
-const update = (id, uri, secret, opts) => {
- return hookApi.update(id, uri, secret, opts).then(hook => {
+ async update (id, uri, secret, opts) {
+ const hook = await hookApi.update(id, uri, secret, opts)
if (opts.json)
output(JSON.stringify(hook, null, 2))
else if (opts.parseable) {
output(Object.keys(hook).join('\t'))
output(Object.keys(hook).map(k => hook[k]).join('\t'))
} else if (!opts.silent && opts.loglevel !== 'silent') {
- output(`+ ${hookName(hook)} ${
+ output(`+ ${this.hookName(hook)} ${
opts.unicode ? ' ➜ ' : ' -> '
} ${hook.endpoint}`)
}
- })
-}
+ }
-const hookName = (hook) => {
- let target = hook.name
- if (hook.type === 'scope')
- target = '@' + target
- if (hook.type === 'owner')
- target = '~' + target
- return target
+ hookName (hook) {
+ let target = hook.name
+ if (hook.type === 'scope')
+ target = '@' + target
+ if (hook.type === 'owner')
+ target = '~' + target
+ return target
+ }
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Hook
diff --git a/deps/npm/lib/init.js b/deps/npm/lib/init.js
index a029779f89..af97a9614e 100644
--- a/deps/npm/lib/init.js
+++ b/deps/npm/lib/init.js
@@ -1,88 +1,97 @@
const initJson = require('init-package-json')
const npa = require('npm-package-arg')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const output = require('./utils/output.js')
-const usage = usageUtil(
- 'init',
- '\nnpm init [--force|-f|--yes|-y|--scope]' +
- '\nnpm init <@scope> (same as `npx <@scope>/create`)' +
- '\nnpm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)'
-)
-
-const cmd = (args, cb) => init(args).then(() => cb()).catch(cb)
+class Init {
+ constructor (npm) {
+ this.npm = npm
+ }
-const init = async args => {
- // the new npx style way
- if (args.length) {
- const initerName = args[0]
- let packageName = initerName
- if (/^@[^/]+$/.test(initerName))
- packageName = initerName + '/create'
- else {
- const req = npa(initerName)
- if (req.type === 'git' && req.hosted) {
- const { user, project } = req.hosted
- packageName = initerName
- .replace(user + '/' + project, user + '/create-' + project)
- } else if (req.registry) {
- packageName = req.name.replace(/^(@[^/]+\/)?/, '$1create-')
- if (req.rawSpec)
- packageName += '@' + req.rawSpec
- } else {
- throw Object.assign(new Error(
- 'Unrecognized initializer: ' + initerName +
- '\nFor more package binary executing power check out `npx`:' +
- '\nhttps://www.npmjs.com/package/npx'
- ), { code: 'EUNSUPPORTED' })
- }
- }
- npm.config.set('package', [])
- const newArgs = [packageName, ...args.slice(1)]
- return new Promise((res, rej) => {
- npm.commands.exec(newArgs, er => er ? rej(er) : res())
- })
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'init',
+ '\nnpm init [--force|-f|--yes|-y|--scope]' +
+ '\nnpm init <@scope> (same as `npx <@scope>/create`)' +
+ '\nnpm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)'
+ )
}
- // the old way
- const dir = process.cwd()
- npm.log.pause()
- npm.log.disableProgress()
- const initFile = npm.config.get('init-module')
- if (!npm.flatOptions.yes && !npm.flatOptions.force) {
- output([
- 'This utility will walk you through creating a package.json file.',
- 'It only covers the most common items, and tries to guess sensible defaults.',
- '',
- 'See `npm help init` for definitive documentation on these fields',
- 'and exactly what they do.',
- '',
- 'Use `npm install <pkg>` afterwards to install a package and',
- 'save it as a dependency in the package.json file.',
- '',
- 'Press ^C at any time to quit.',
- ].join('\n'))
+ exec (args, cb) {
+ this.init(args).then(() => cb()).catch(cb)
}
- // XXX promisify init-package-json
- await new Promise((res, rej) => {
- initJson(dir, initFile, npm.config, (er, data) => {
- npm.log.resume()
- npm.log.enableProgress()
- npm.log.silly('package data', data)
- if (er && er.message === 'canceled') {
- npm.log.warn('init', 'canceled')
- return res()
- }
- if (er)
- rej(er)
+
+ async init (args) {
+ // the new npx style way
+ if (args.length) {
+ const initerName = args[0]
+ let packageName = initerName
+ if (/^@[^/]+$/.test(initerName))
+ packageName = initerName + '/create'
else {
- npm.log.info('init', 'written successfully')
- res(data)
+ const req = npa(initerName)
+ if (req.type === 'git' && req.hosted) {
+ const { user, project } = req.hosted
+ packageName = initerName
+ .replace(user + '/' + project, user + '/create-' + project)
+ } else if (req.registry) {
+ packageName = req.name.replace(/^(@[^/]+\/)?/, '$1create-')
+ if (req.rawSpec)
+ packageName += '@' + req.rawSpec
+ } else {
+ throw Object.assign(new Error(
+ 'Unrecognized initializer: ' + initerName +
+ '\nFor more package binary executing power check out `npx`:' +
+ '\nhttps://www.npmjs.com/package/npx'
+ ), { code: 'EUNSUPPORTED' })
+ }
}
+ this.npm.config.set('package', [])
+ const newArgs = [packageName, ...args.slice(1)]
+ return new Promise((res, rej) => {
+ this.npm.commands.exec(newArgs, er => er ? rej(er) : res())
+ })
+ }
+
+ // the old way
+ const dir = process.cwd()
+ this.npm.log.pause()
+ this.npm.log.disableProgress()
+ const initFile = this.npm.config.get('init-module')
+ if (!this.npm.flatOptions.yes && !this.npm.flatOptions.force) {
+ output([
+ 'This utility will walk you through creating a package.json file.',
+ 'It only covers the most common items, and tries to guess sensible defaults.',
+ '',
+ 'See `npm help init` for definitive documentation on these fields',
+ 'and exactly what they do.',
+ '',
+ 'Use `npm install <pkg>` afterwards to install a package and',
+ 'save it as a dependency in the package.json file.',
+ '',
+ 'Press ^C at any time to quit.',
+ ].join('\n'))
+ }
+ // XXX promisify init-package-json
+ await new Promise((res, rej) => {
+ initJson(dir, initFile, this.npm.config, (er, data) => {
+ this.npm.log.resume()
+ this.npm.log.enableProgress()
+ this.npm.log.silly('package data', data)
+ if (er && er.message === 'canceled') {
+ this.npm.log.warn('init', 'canceled')
+ return res()
+ }
+ if (er)
+ rej(er)
+ else {
+ this.npm.log.info('init', 'written successfully')
+ res(data)
+ }
+ })
})
- })
+ }
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Init
diff --git a/deps/npm/lib/install-ci-test.js b/deps/npm/lib/install-ci-test.js
index 52c41c413a..d1740999d4 100644
--- a/deps/npm/lib/install-ci-test.js
+++ b/deps/npm/lib/install-ci-test.js
@@ -1,19 +1,27 @@
// npm install-ci-test
// Runs `npm ci` and then runs `npm test`
-const ci = require('./ci.js')
-const test = require('./test.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'install-ci-test',
- 'npm install-ci-test [args]' +
- '\nSame args as `npm ci`'
-)
+class InstallCITest {
+ constructor (npm) {
+ this.npm = npm
+ }
-const completion = ci.completion
+ get usage () {
+ return usageUtil(
+ 'install-ci-test',
+ 'npm install-ci-test [args]' +
+ '\nSame args as `npm ci`'
+ )
+ }
-const ciTest = (args, cb) =>
- ci(args, er => er ? cb(er) : test([], cb))
-
-module.exports = Object.assign(ciTest, { usage, completion })
+ exec (args, cb) {
+ this.npm.commands.ci(args, (er) => {
+ if (er)
+ return cb(er)
+ this.npm.commands.test([], cb)
+ })
+ }
+}
+module.exports = InstallCITest
diff --git a/deps/npm/lib/install-test.js b/deps/npm/lib/install-test.js
index 9593361e32..487f8da00b 100644
--- a/deps/npm/lib/install-test.js
+++ b/deps/npm/lib/install-test.js
@@ -1,19 +1,31 @@
// npm install-test
// Runs `npm install` and then runs `npm test`
-const install = require('./install.js')
-const test = require('./test.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'install-test',
- 'npm install-test [args]' +
- '\nSame args as `npm install`'
-)
+class InstallTest {
+ constructor (npm) {
+ this.npm = npm
+ }
-const completion = install.completion
+ get usage () {
+ return usageUtil(
+ 'install-test',
+ 'npm install-test [args]' +
+ '\nSame args as `npm install`'
+ )
+ }
-const installTest = (args, cb) =>
- install(args, er => er ? cb(er) : test([], cb))
+ async completion (opts) {
+ return this.npm.commands.install.completion(opts)
+ }
-module.exports = Object.assign(installTest, { usage, completion })
+ exec (args, cb) {
+ this.npm.commands.install(args, (er) => {
+ if (er)
+ return cb(er)
+ this.npm.commands.test([], cb)
+ })
+ }
+}
+module.exports = InstallTest
diff --git a/deps/npm/lib/install.js b/deps/npm/lib/install.js
index 5f0137db1c..d7fd384d5b 100644
--- a/deps/npm/lib/install.js
+++ b/deps/npm/lib/install.js
@@ -3,7 +3,6 @@
const fs = require('fs')
const util = require('util')
const readdir = util.promisify(fs.readdir)
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const reifyFinish = require('./utils/reify-finish.js')
const log = require('npmlog')
@@ -11,133 +10,143 @@ const { resolve, join } = require('path')
const Arborist = require('@npmcli/arborist')
const runScript = require('@npmcli/run-script')
-const cmd = async (args, cb) => install(args).then(() => cb()).catch(cb)
-
-const install = async args => {
- // the /path/to/node_modules/..
- const globalTop = resolve(npm.globalDir, '..')
- const { ignoreScripts, global: isGlobalInstall } = npm.flatOptions
- const where = isGlobalInstall ? globalTop : npm.prefix
-
- // don't try to install the prefix into itself
- args = args.filter(a => resolve(a) !== npm.prefix)
-
- // `npm i -g` => "install this package globally"
- if (where === globalTop && !args.length)
- args = ['.']
-
- // TODO: Add warnings for other deprecated flags? or remove this one?
- if (npm.config.get('dev'))
- log.warn('install', 'Usage of the `--dev` option is deprecated. Use `--include=dev` instead.')
-
- const arb = new Arborist({
- ...npm.flatOptions,
- path: where,
- })
+class Install {
+ constructor (npm) {
+ this.npm = npm
+ }
- await arb.reify({
- ...npm.flatOptions,
- add: args,
- })
- if (!args.length && !isGlobalInstall && !ignoreScripts) {
- const { scriptShell } = npm.flatOptions
- const scripts = [
- 'preinstall',
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
'install',
- 'postinstall',
- 'prepublish', // XXX should we remove this finally??
- 'preprepare',
- 'prepare',
- 'postprepare',
- ]
- for (const event of scripts) {
- await runScript({
- path: where,
- args: [],
- scriptShell,
- stdio: 'inherit',
- stdioString: true,
- banner: log.level !== 'silent',
- event,
- })
- }
+ 'npm install (with no args, in package dir)' +
+ '\nnpm install [<@scope>/]<pkg>' +
+ '\nnpm install [<@scope>/]<pkg>@<tag>' +
+ '\nnpm install [<@scope>/]<pkg>@<version>' +
+ '\nnpm install [<@scope>/]<pkg>@<version range>' +
+ '\nnpm install <alias>@npm:<name>' +
+ '\nnpm install <folder>' +
+ '\nnpm install <tarball file>' +
+ '\nnpm install <tarball url>' +
+ '\nnpm install <git:// url>' +
+ '\nnpm install <github username>/<github project>',
+ '[--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save]'
+ )
}
- await reifyFinish(arb)
-}
-const usage = usageUtil(
- 'install',
- 'npm install (with no args, in package dir)' +
- '\nnpm install [<@scope>/]<pkg>' +
- '\nnpm install [<@scope>/]<pkg>@<tag>' +
- '\nnpm install [<@scope>/]<pkg>@<version>' +
- '\nnpm install [<@scope>/]<pkg>@<version range>' +
- '\nnpm install <alias>@npm:<name>' +
- '\nnpm install <folder>' +
- '\nnpm install <tarball file>' +
- '\nnpm install <tarball url>' +
- '\nnpm install <git:// url>' +
- '\nnpm install <github username>/<github project>',
- '[--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save]'
-)
+ 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
+ // if it starts with https?://, then just give up, because it's a url
+ if (/^https?:\/\//.test(partialWord)) {
+ // do not complete to URLs
+ return []
+ }
-const completion = async (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
- // if it starts with https?://, then just give up, because it's a url
- if (/^https?:\/\//.test(partialWord)) {
- // do not complete to URLs
- return []
- }
+ if (/\//.test(partialWord)) {
+ // Complete fully to folder if there is exactly one match and it
+ // is a folder containing a package.json file. If that is not the
+ // case we return 0 matches, which will trigger the default bash
+ // complete.
+ const lastSlashIdx = partialWord.lastIndexOf('/')
+ const partialName = partialWord.slice(lastSlashIdx + 1)
+ const partialPath = partialWord.slice(0, lastSlashIdx) || '/'
- if (/\//.test(partialWord)) {
- // Complete fully to folder if there is exactly one match and it
- // is a folder containing a package.json file. If that is not the
- // case we return 0 matches, which will trigger the default bash
- // complete.
- const lastSlashIdx = partialWord.lastIndexOf('/')
- const partialName = partialWord.slice(lastSlashIdx + 1)
- const partialPath = partialWord.slice(0, lastSlashIdx) || '/'
+ const annotatePackageDirMatch = async (sibling) => {
+ const fullPath = join(partialPath, sibling)
+ if (sibling.slice(0, partialName.length) !== partialName)
+ return null // not name match
- const annotatePackageDirMatch = async (sibling) => {
- const fullPath = join(partialPath, sibling)
- if (sibling.slice(0, partialName.length) !== partialName)
- return null // not name match
+ try {
+ const contents = await readdir(fullPath)
+ return {
+ fullPath,
+ isPackage: contents.indexOf('package.json') !== -1,
+ }
+ } catch (er) {
+ return { isPackage: false }
+ }
+ }
try {
- const contents = await readdir(fullPath)
- return {
- fullPath,
- isPackage: contents.indexOf('package.json') !== -1,
+ const siblings = await readdir(partialPath)
+ const matches = await Promise.all(
+ siblings.map(async sibling => {
+ return await annotatePackageDirMatch(sibling)
+ })
+ )
+ const match = matches.filter(el => !el || el.isPackage).pop()
+ if (match) {
+ // Success - only one match and it is a package dir
+ return [match.fullPath]
+ } else {
+ // no matches
+ return []
}
} catch (er) {
- return { isPackage: false }
+ return [] // invalid dir: no matching
}
}
+ // Note: there used to be registry completion here,
+ // but it stopped making sense somewhere around
+ // 50,000 packages on the registry
+ }
+
+ exec (args, cb) {
+ this.install(args).then(() => cb()).catch(cb)
+ }
+
+ async install (args) {
+ // the /path/to/node_modules/..
+ const globalTop = resolve(this.npm.globalDir, '..')
+ const { ignoreScripts, global: isGlobalInstall } = this.npm.flatOptions
+ const where = isGlobalInstall ? globalTop : this.npm.prefix
- try {
- const siblings = await readdir(partialPath)
- const matches = await Promise.all(
- siblings.map(async sibling => {
- return await annotatePackageDirMatch(sibling)
+ // don't try to install the prefix into itself
+ args = args.filter(a => resolve(a) !== this.npm.prefix)
+
+ // `npm i -g` => "install this package globally"
+ if (where === globalTop && !args.length)
+ args = ['.']
+
+ // TODO: Add warnings for other deprecated flags? or remove this one?
+ if (this.npm.config.get('dev'))
+ log.warn('install', 'Usage of the `--dev` option is deprecated. Use `--include=dev` instead.')
+
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ path: where,
+ })
+
+ await arb.reify({
+ ...this.npm.flatOptions,
+ add: args,
+ })
+ if (!args.length && !isGlobalInstall && !ignoreScripts) {
+ const { scriptShell } = this.npm.flatOptions
+ const scripts = [
+ 'preinstall',
+ 'install',
+ 'postinstall',
+ 'prepublish', // XXX should we remove this finally??
+ 'preprepare',
+ 'prepare',
+ 'postprepare',
+ ]
+ for (const event of scripts) {
+ await runScript({
+ path: where,
+ args: [],
+ scriptShell,
+ stdio: 'inherit',
+ stdioString: true,
+ banner: log.level !== 'silent',
+ event,
})
- )
- const match = matches.filter(el => !el || el.isPackage).pop()
- if (match) {
- // Success - only one match and it is a package dir
- return [match.fullPath]
- } else {
- // no matches
- return []
}
- } catch (er) {
- return [] // invalid dir: no matching
}
+ await reifyFinish(this.npm, arb)
}
- // Note: there used to be registry completion here,
- // but it stopped making sense somewhere around
- // 50,000 packages on the registry
}
-
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = Install
diff --git a/deps/npm/lib/link.js b/deps/npm/lib/link.js
index 0bb3d87b5e..6d5e207105 100644
--- a/deps/npm/lib/link.js
+++ b/deps/npm/lib/link.js
@@ -8,145 +8,154 @@ const npa = require('npm-package-arg')
const rpj = require('read-package-json-fast')
const semver = require('semver')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const reifyFinish = require('./utils/reify-finish.js')
-const completion = async (opts) => {
- const dir = npm.globalDir
- const files = await readdir(dir)
- return files.filter(f => !/^[._-]/.test(f))
-}
+class Link {
+ constructor (npm) {
+ this.npm = npm
+ }
-const usage = usageUtil(
- 'link',
- 'npm link (in package dir)' +
- '\nnpm link [<@scope>/]<pkg>[@<version>]'
-)
-
-const cmd = (args, cb) => link(args).then(() => cb()).catch(cb)
-
-const link = async args => {
- if (npm.config.get('global')) {
- throw Object.assign(
- new Error(
- 'link should never be --global.\n' +
- 'Please re-run this command with --local'
- ),
- { code: 'ELINKGLOBAL' }
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'link',
+ 'npm link (in package dir)' +
+ '\nnpm link [<@scope>/]<pkg>[@<version>]'
)
}
- // link with no args: symlink the folder to the global location
- // link with package arg: symlink the global to the local
- args = args.filter(a => resolve(a) !== npm.prefix)
- return args.length
- ? linkInstall(args)
- : linkPkg()
-}
+ async completion (opts) {
+ const dir = this.npm.globalDir
+ const files = await readdir(dir)
+ return files.filter(f => !/^[._-]/.test(f))
+ }
-// Returns a list of items that can't be fulfilled by
-// things found in the current arborist inventory
-const missingArgsFromTree = (tree, args) => {
- if (tree.isLink)
- return missingArgsFromTree(tree.target, args)
-
- const foundNodes = []
- const missing = args.filter(a => {
- const arg = npa(a)
- const nodes = tree.children.values()
- const argFound = [...nodes].every(node => {
- // TODO: write tests for unmatching version specs, this is hard to test
- // atm but should be simple once we have a mocked registry again
- if (arg.name !== node.name /* istanbul ignore next */ || (
- arg.version &&
- !semver.satisfies(node.version, arg.version)
- )) {
- foundNodes.push(node)
- return true
- }
- })
- return argFound
- })
+ exec (args, cb) {
+ this.link(args).then(() => cb()).catch(cb)
+ }
- // remote nodes from the loaded tree in order
- // to avoid dropping them later when reifying
- for (const node of foundNodes)
- node.parent = null
+ async link (args) {
+ if (this.npm.config.get('global')) {
+ throw Object.assign(
+ new Error(
+ 'link should never be --global.\n' +
+ 'Please re-run this command with --local'
+ ),
+ { code: 'ELINKGLOBAL' }
+ )
+ }
+
+ // link with no args: symlink the folder to the global location
+ // link with package arg: symlink the global to the local
+ args = args.filter(a => resolve(a) !== this.npm.prefix)
+ return args.length
+ ? this.linkInstall(args)
+ : this.linkPkg()
+ }
- return missing
-}
+ async linkInstall (args) {
+ // load current packages from the global space,
+ // and then add symlinks installs locally
+ const globalTop = resolve(this.npm.globalDir, '..')
+ const globalOpts = {
+ ...this.npm.flatOptions,
+ path: globalTop,
+ global: true,
+ prune: false,
+ }
+ const globalArb = new Arborist(globalOpts)
+
+ // get only current top-level packages from the global space
+ const globals = await globalArb.loadActual({
+ filter: (node, kid) =>
+ !node.isRoot || args.some(a => npa(a).name === kid),
+ })
-const linkInstall = async args => {
- // load current packages from the global space,
- // and then add symlinks installs locally
- const globalTop = resolve(npm.globalDir, '..')
- const globalOpts = {
- ...npm.flatOptions,
- path: globalTop,
- global: true,
- prune: false,
- }
- const globalArb = new Arborist(globalOpts)
-
- // get only current top-level packages from the global space
- const globals = await globalArb.loadActual({
- filter: (node, kid) =>
- !node.isRoot || args.some(a => npa(a).name === kid),
- })
-
- // any extra arg that is missing from the current
- // global space should be reified there first
- const missing = missingArgsFromTree(globals, args)
- if (missing.length) {
- await globalArb.reify({
- ...globalOpts,
- add: missing,
+ // any extra arg that is missing from the current
+ // global space should be reified there first
+ const missing = this.missingArgsFromTree(globals, args)
+ if (missing.length) {
+ await globalArb.reify({
+ ...globalOpts,
+ add: missing,
+ })
+ }
+
+ // get a list of module names that should be linked in the local prefix
+ const names = []
+ for (const a of args) {
+ const arg = npa(a)
+ names.push(
+ arg.type === 'directory'
+ ? (await rpj(resolve(arg.fetchSpec, 'package.json'))).name
+ : arg.name
+ )
+ }
+
+ // npm link should not save=true by default unless you're
+ // using any of --save-dev or other types
+ const save =
+ Boolean(this.npm.config.find('save') !== 'default' || this.npm.flatOptions.saveType)
+
+ // create a new arborist instance for the local prefix and
+ // reify all the pending names as symlinks there
+ const localArb = new Arborist({
+ ...this.npm.flatOptions,
+ path: this.npm.prefix,
+ save,
})
+ await localArb.reify({
+ ...this.npm.flatOptions,
+ path: this.npm.prefix,
+ add: names.map(l => `file:${resolve(globalTop, 'node_modules', l)}`),
+ save,
+ })
+
+ await reifyFinish(this.npm, localArb)
}
- // get a list of module names that should be linked in the local prefix
- const names = []
- for (const a of args) {
- const arg = npa(a)
- names.push(
- arg.type === 'directory'
- ? (await rpj(resolve(arg.fetchSpec, 'package.json'))).name
- : arg.name
- )
+ async linkPkg () {
+ const globalTop = resolve(this.npm.globalDir, '..')
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ path: globalTop,
+ global: true,
+ })
+ await arb.reify({ add: [`file:${this.npm.prefix}`] })
+ await reifyFinish(this.npm, arb)
}
- // npm link should not save=true by default unless you're
- // using any of --save-dev or other types
- const save =
- Boolean(npm.config.find('save') !== 'default' || npm.flatOptions.saveType)
-
- // create a new arborist instance for the local prefix and
- // reify all the pending names as symlinks there
- const localArb = new Arborist({
- ...npm.flatOptions,
- path: npm.prefix,
- save,
- })
- await localArb.reify({
- ...npm.flatOptions,
- path: npm.prefix,
- add: names.map(l => `file:${resolve(globalTop, 'node_modules', l)}`),
- save,
- })
-
- await reifyFinish(localArb)
-}
+ // Returns a list of items that can't be fulfilled by
+ // things found in the current arborist inventory
+ missingArgsFromTree (tree, args) {
+ if (tree.isLink)
+ return this.missingArgsFromTree(tree.target, args)
+
+ const foundNodes = []
+ const missing = args.filter(a => {
+ const arg = npa(a)
+ const nodes = tree.children.values()
+ const argFound = [...nodes].every(node => {
+ // TODO: write tests for unmatching version specs, this is hard to test
+ // atm but should be simple once we have a mocked registry again
+ if (arg.name !== node.name /* istanbul ignore next */ || (
+ arg.version &&
+ !semver.satisfies(node.version, arg.version)
+ )) {
+ foundNodes.push(node)
+ return true
+ }
+ })
+ return argFound
+ })
-const linkPkg = async () => {
- const globalTop = resolve(npm.globalDir, '..')
- const arb = new Arborist({
- ...npm.flatOptions,
- path: globalTop,
- global: true,
- })
- await arb.reify({ add: [`file:${npm.prefix}`] })
- await reifyFinish(arb)
-}
+ // remote nodes from the loaded tree in order
+ // to avoid dropping them later when reifying
+ for (const node of foundNodes)
+ node.parent = null
-module.exports = Object.assign(cmd, { completion, usage })
+ return missing
+ }
+}
+module.exports = Link
diff --git a/deps/npm/lib/ll.js b/deps/npm/lib/ll.js
index 1d5a6217da..7915f5d27c 100644
--- a/deps/npm/lib/ll.js
+++ b/deps/npm/lib/ll.js
@@ -1,9 +1,19 @@
-const { usage, completion } = require('./ls.js')
-const npm = require('./npm.js')
+const LS = require('./ls.js')
+const usageUtil = require('./utils/usage.js')
-const cmd = (args, cb) => {
- npm.config.set('long', true)
- return npm.commands.ls(args, cb)
+class LL extends LS {
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'll',
+ 'npm ll [[<@scope>/]<pkg> ...]'
+ )
+ }
+
+ exec (args, cb) {
+ this.npm.config.set('long', true)
+ super.exec(args, cb)
+ }
}
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = LL
diff --git a/deps/npm/lib/logout.js b/deps/npm/lib/logout.js
index d2762c1ba3..9fb1eab21a 100644
--- a/deps/npm/lib/logout.js
+++ b/deps/npm/lib/logout.js
@@ -1,44 +1,52 @@
-const eu = encodeURIComponent
const log = require('npmlog')
const getAuth = require('npm-registry-fetch/auth.js')
const npmFetch = require('npm-registry-fetch')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'logout',
- 'npm logout [--registry=<url>] [--scope=<@scope>]'
-)
-
-const cmd = (args, cb) => logout(args).then(() => cb()).catch(cb)
-
-const logout = async (args) => {
- const { registry, scope } = npm.flatOptions
- const regRef = scope ? `${scope}:registry` : 'registry'
- const reg = npm.flatOptions[regRef] || registry
-
- const auth = getAuth(reg, npm.flatOptions)
-
- if (auth.token) {
- log.verbose('logout', `clearing token for ${reg}`)
- await npmFetch(`/-/user/token/${eu(auth.token)}`, {
- ...npm.flatOptions,
- method: 'DELETE',
- ignoreBody: true,
- })
- } else if (auth.username || auth.password)
- log.verbose('logout', `clearing user credentials for ${reg}`)
- else {
- const msg = `not logged in to ${reg}, so can't log out!`
- throw Object.assign(new Error(msg), { code: 'ENEEDAUTH' })
+class Logout {
+ constructor (npm) {
+ this.npm = npm
}
- if (scope)
- npm.config.delete(regRef, 'user')
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'logout',
+ 'npm logout [--registry=<url>] [--scope=<@scope>]'
+ )
+ }
- npm.config.clearCredentialsByURI(reg)
+ exec (args, cb) {
+ this.logout(args).then(() => cb()).catch(cb)
+ }
- await npm.config.save('user')
+ async logout (args) {
+ const { registry, scope } = this.npm.flatOptions
+ const regRef = scope ? `${scope}:registry` : 'registry'
+ const reg = this.npm.flatOptions[regRef] || registry
+
+ const auth = getAuth(reg, this.npm.flatOptions)
+
+ if (auth.token) {
+ log.verbose('logout', `clearing token for ${reg}`)
+ await npmFetch(`/-/user/token/${encodeURIComponent(auth.token)}`, {
+ ...this.npm.flatOptions,
+ method: 'DELETE',
+ ignoreBody: true,
+ })
+ } else if (auth.username || auth.password)
+ log.verbose('logout', `clearing user credentials for ${reg}`)
+ else {
+ const msg = `not logged in to ${reg}, so can't log out!`
+ throw Object.assign(new Error(msg), { code: 'ENEEDAUTH' })
+ }
+
+ if (scope)
+ this.npm.config.delete(regRef, 'user')
+
+ this.npm.config.clearCredentialsByURI(reg)
+
+ await this.npm.config.save('user')
+ }
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Logout
diff --git a/deps/npm/lib/ls.js b/deps/npm/lib/ls.js
index 603c3b412d..359fe21e6f 100644
--- a/deps/npm/lib/ls.js
+++ b/deps/npm/lib/ls.js
@@ -7,7 +7,6 @@ const Arborist = require('@npmcli/arborist')
const { breadth } = require('treeverse')
const npa = require('npm-package-arg')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const completion = require('./utils/completion/installed-deep.js')
const output = require('./utils/output.js')
@@ -24,20 +23,166 @@ const _problems = Symbol('problems')
const _required = Symbol('required')
const _type = Symbol('type')
-const usage = usageUtil(
- 'ls',
- 'npm ls [[<@scope>/]<pkg> ...]'
-)
+class LS {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'ls',
+ 'npm ls [[<@scope>/]<pkg> ...]'
+ )
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
+
+ exec (args, cb) {
+ this.ls(args).then(() => cb()).catch(cb)
+ }
-const cmd = (args, cb) => ls(args).then(() => cb()).catch(cb)
+ async ls (args) {
+ const {
+ all,
+ color,
+ depth,
+ json,
+ long,
+ global,
+ parseable,
+ prefix,
+ unicode,
+ } = this.npm.flatOptions
+ const path = global ? resolve(this.npm.globalDir, '..') : prefix
+ const dev = this.npm.config.get('dev')
+ const development = this.npm.config.get('development')
+ const link = this.npm.config.get('link')
+ const only = this.npm.config.get('only')
+ const prod = this.npm.config.get('prod')
+ const production = this.npm.config.get('production')
+
+ const arb = new Arborist({
+ global,
+ ...this.npm.flatOptions,
+ legacyPeerDeps: false,
+ path,
+ })
+ const tree = await this.initTree({arb, args })
+
+ const seenItems = new Set()
+ const seenNodes = new Map()
+ const problems = new Set()
+
+ // defines special handling of printed depth when filtering with args
+ const filterDefaultDepth = depth === null ? Infinity : depth
+ const depthToPrint = (all || args.length)
+ ? filterDefaultDepth
+ : (depth || 0)
+
+ // add root node of tree to list of seenNodes
+ seenNodes.set(tree.path, tree)
+
+ // tree traversal happens here, using treeverse.breadth
+ const result = await breadth({
+ tree,
+ // recursive method, `node` is going to be the current elem (starting from
+ // the `tree` obj) that was just visited in the `visit` method below
+ // `nodeResult` is going to be the returned `item` from `visit`
+ getChildren (node, nodeResult) {
+ const seenPaths = new Set()
+ const shouldSkipChildren =
+ !(node instanceof Arborist.Node) || (node[_depth] > depthToPrint)
+ return (shouldSkipChildren)
+ ? []
+ : [...(node.target || node).edgesOut.values()]
+ .filter(filterByEdgesTypes({
+ dev,
+ development,
+ link,
+ node,
+ prod,
+ production,
+ only,
+ tree,
+ }))
+ .map(mapEdgesToNodes({ seenPaths }))
+ .concat(appendExtraneousChildren({ node, seenPaths }))
+ .sort(sortAlphabetically)
+ .map(augmentNodesWithMetadata({
+ args,
+ currentDepth: node[_depth],
+ nodeResult,
+ seenNodes,
+ }))
+ },
+ // visit each `node` of the `tree`, returning an `item` - these are
+ // the elements that will be used to build the final output
+ visit (node) {
+ node[_problems] = getProblems(node, { global })
+
+ const item = json
+ ? getJsonOutputItem(node, { global, long })
+ : parseable
+ ? null
+ : getHumanOutputItem(node, { args, color, global, long })
+
+ // loop through list of node problems to add them to global list
+ if (node[_include]) {
+ for (const problem of node[_problems])
+ problems.add(problem)
+ }
+
+ seenItems.add(item)
+
+ // return a promise so we don't blow the stack
+ return Promise.resolve(item)
+ },
+ })
+
+ // handle the special case of a broken package.json in the root folder
+ const [rootError] = tree.errors.filter(e =>
+ e.code === 'EJSONPARSE' && e.path === resolve(path, 'package.json'))
+
+ output(
+ json
+ ? jsonOutput({ path, problems, result, rootError, seenItems })
+ : parseable
+ ? parseableOutput({ seenNodes, global, long })
+ : humanOutput({ color, result, seenItems, unicode })
+ )
-const initTree = async ({ arb, args, json }) => {
- const tree = await arb.loadActual()
- tree[_include] = args.length === 0
- tree[_depth] = 0
+ // if filtering items, should exit with error code on no results
+ if (result && !result[_include] && args.length)
+ process.exitCode = 1
- return tree
+ if (rootError) {
+ throw Object.assign(
+ new Error('Failed to parse root package.json'),
+ { code: 'EJSONPARSE' }
+ )
+ }
+
+ if (problems.size) {
+ throw Object.assign(
+ new Error([...problems].join(EOL)),
+ { code: 'ELSPROBLEMS' }
+ )
+ }
+ }
+
+ async initTree ({ arb, args }) {
+ const tree = await arb.loadActual()
+ tree[_include] = args.length === 0
+ tree[_depth] = 0
+
+ return tree
+ }
}
+module.exports = LS
const isGitNode = (node) => {
if (!node.resolved)
@@ -252,7 +397,6 @@ const augmentNodesWithMetadata = ({
args,
currentDepth,
nodeResult,
- parseable,
seenNodes,
}) => (node) => {
// if the original edge was a deduped dep, treeverse will fail to
@@ -285,7 +429,7 @@ const augmentNodesWithMetadata = ({
// _filteredBy is used to apply extra color info to the item that
// was used in args in order to filter
node[_filteredBy] = node[_include] =
- filterByPositionalArgs(args, { node: seenNodes.get(node.path), seenNodes })
+ filterByPositionalArgs(args, { node: seenNodes.get(node.path) })
// _depth keeps track of how many levels deep tree traversal currently is
// so that we can `npm ls --depth=1`
node[_depth] = currentDepth + 1
@@ -359,140 +503,3 @@ const parseableOutput = ({ global, long, seenNodes }) => {
}
return out.trim()
}
-
-const ls = async (args) => {
- const {
- all,
- color,
- depth,
- json,
- long,
- global,
- parseable,
- prefix,
- unicode,
- } = npm.flatOptions
- const path = global ? resolve(npm.globalDir, '..') : prefix
- const dev = npm.config.get('dev')
- const development = npm.config.get('development')
- const link = npm.config.get('link')
- const only = npm.config.get('only')
- const prod = npm.config.get('prod')
- const production = npm.config.get('production')
-
- const arb = new Arborist({
- global,
- ...npm.flatOptions,
- legacyPeerDeps: false,
- path,
- })
- const tree = await initTree({
- arb,
- args,
- global,
- json,
- })
-
- const seenItems = new Set()
- const seenNodes = new Map()
- const problems = new Set()
-
- // defines special handling of printed depth when filtering with args
- const filterDefaultDepth = depth === null ? Infinity : depth
- const depthToPrint = (all || args.length)
- ? filterDefaultDepth
- : (depth || 0)
-
- // add root node of tree to list of seenNodes
- seenNodes.set(tree.path, tree)
-
- // tree traversal happens here, using treeverse.breadth
- const result = await breadth({
- tree,
- // recursive method, `node` is going to be the current elem (starting from
- // the `tree` obj) that was just visited in the `visit` method below
- // `nodeResult` is going to be the returned `item` from `visit`
- getChildren (node, nodeResult) {
- const seenPaths = new Set()
- const shouldSkipChildren =
- !(node instanceof Arborist.Node) || (node[_depth] > depthToPrint)
- return (shouldSkipChildren)
- ? []
- : [...(node.target || node).edgesOut.values()]
- .filter(filterByEdgesTypes({
- dev,
- development,
- link,
- node,
- prod,
- production,
- only,
- tree,
- }))
- .map(mapEdgesToNodes({ seenPaths }))
- .concat(appendExtraneousChildren({ node, seenPaths }))
- .sort(sortAlphabetically)
- .map(augmentNodesWithMetadata({
- args,
- currentDepth: node[_depth],
- nodeResult,
- parseable,
- seenNodes,
- }))
- },
- // visit each `node` of the `tree`, returning an `item` - these are
- // the elements that will be used to build the final output
- visit (node) {
- node[_problems] = getProblems(node, { global })
-
- const item = json
- ? getJsonOutputItem(node, { global, long })
- : parseable
- ? null
- : getHumanOutputItem(node, { args, color, global, long })
-
- // loop through list of node problems to add them to global list
- if (node[_include]) {
- for (const problem of node[_problems])
- problems.add(problem)
- }
-
- seenItems.add(item)
-
- // return a promise so we don't blow the stack
- return Promise.resolve(item)
- },
- })
-
- // handle the special case of a broken package.json in the root folder
- const [rootError] = tree.errors.filter(e =>
- e.code === 'EJSONPARSE' && e.path === resolve(path, 'package.json'))
-
- output(
- json
- ? jsonOutput({ path, problems, result, rootError, seenItems })
- : parseable
- ? parseableOutput({ seenNodes, global, long })
- : humanOutput({ color, result, seenItems, unicode })
- )
-
- // if filtering items, should exit with error code on no results
- if (result && !result[_include] && args.length)
- process.exitCode = 1
-
- if (rootError) {
- throw Object.assign(
- new Error('Failed to parse root package.json'),
- { code: 'EJSONPARSE' }
- )
- }
-
- if (problems.size) {
- throw Object.assign(
- new Error([...problems].join(EOL)),
- { code: 'ELSPROBLEMS' }
- )
- }
-}
-
-module.exports = Object.assign(cmd, { usage, completion })
diff --git a/deps/npm/lib/npm.js b/deps/npm/lib/npm.js
index 85dc67a78a..1f8c785e75 100644
--- a/deps/npm/lib/npm.js
+++ b/deps/npm/lib/npm.js
@@ -13,40 +13,28 @@ require('graceful-fs').gracefulify(require('fs'))
const procLogListener = require('./utils/proc-log-listener.js')
-const hasOwnProperty = (obj, key) =>
- Object.prototype.hasOwnProperty.call(obj, key)
-
-// the first time `npm.commands.xyz` is loaded, it gets added
-// to the cmds object, so we don't have to load it again.
-const proxyCmds = (npm) => {
- const cmds = {}
- return new Proxy(cmds, {
- get: (prop, cmd) => {
- if (hasOwnProperty(cmds, cmd))
- return cmds[cmd]
-
- const actual = deref(cmd)
- if (!actual) {
- cmds[cmd] = undefined
- return cmds[cmd]
- }
- if (cmds[actual]) {
- cmds[cmd] = cmds[actual]
- return cmds[cmd]
- }
- cmds[actual] = makeCmd(actual)
- cmds[cmd] = cmds[actual]
- return cmds[cmd]
- },
- })
-}
-
-const makeCmd = cmd => {
- const impl = require(`./${cmd}.js`)
- const fn = (args, cb) => npm[_runCmd](cmd, impl, args, cb)
- Object.assign(fn, impl)
- return fn
-}
+const proxyCmds = new Proxy({}, {
+ get: (target, cmd) => {
+ const actual = deref(cmd)
+ if (actual && !Reflect.has(target, actual)) {
+ const Impl = require(`./${actual}.js`)
+ const impl = new Impl(npm)
+ // Our existing npm.commands[x] act like a function with attributes, but
+ // our modules have non-inumerable attributes so we can't just assign
+ // them to an anonymous function like we used to. This acts like that
+ // old way of doing things, until we can make breaking changes to the
+ // npm.commands[x] api
+ target[actual] = new Proxy(
+ (args, cb) => npm[_runCmd](cmd, impl, args, cb),
+ {
+ get: (target, attr, receiver) => {
+ return Reflect.get(impl, attr, receiver)
+ },
+ })
+ }
+ return target[actual]
+ },
+})
const { types, defaults, shorthands } = require('./utils/config.js')
const { shellouts } = require('./utils/cmd-list.js')
@@ -68,7 +56,7 @@ const npm = module.exports = new class extends EventEmitter {
}
this.started = Date.now()
this.command = null
- this.commands = proxyCmds(this)
+ this.commands = proxyCmds
procLogListener()
process.emit('time', 'npm')
this.version = require('../package.json').version
@@ -121,7 +109,7 @@ const npm = module.exports = new class extends EventEmitter {
console.log(impl.usage)
cb()
} else {
- impl(args, er => {
+ impl.exec(args, er => {
process.emit('timeEnd', `command:${cmd}`)
cb(er)
})
@@ -193,7 +181,7 @@ const npm = module.exports = new class extends EventEmitter {
this.title = tokrev ? 'npm token revoke' + (this.argv[2] ? ' ***' : '')
: ['npm', ...this.argv].join(' ')
- this.color = setupLog(this.config, this)
+ this.color = setupLog(this.config)
process.env.COLOR = this.color ? '1' : '0'
cleanUpLogFiles(this.cache, this.config.get('logs-max'), log.warn)
diff --git a/deps/npm/lib/org.js b/deps/npm/lib/org.js
index aa9c97d497..054e1833db 100644
--- a/deps/npm/lib/org.js
+++ b/deps/npm/lib/org.js
@@ -1,139 +1,148 @@
const liborg = require('libnpmorg')
-const npm = require('./npm.js')
+const usageUtil = require('./utils/usage.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const Table = require('cli-table3')
-module.exports = org
-
-org.subcommands = ['set', 'rm', 'ls']
-
-org.usage =
- 'npm org set orgname username [developer | admin | owner]\n' +
- 'npm org rm orgname username\n' +
- 'npm org ls orgname [<username>]'
-
-org.completion = async (opts) => {
- var argv = opts.conf.argv.remain
- if (argv.length === 2)
- return org.subcommands
+class Org {
+ constructor (npm) {
+ this.npm = npm
+ }
- switch (argv[2]) {
- case 'ls':
- case 'add':
- case 'rm':
- case 'set':
- return []
- default:
- throw new Error(argv[2] + ' not recognized')
+ get usage () {
+ return usageUtil(
+ 'org',
+ 'npm org set orgname username [developer | admin | owner]\n' +
+ 'npm org rm orgname username\n' +
+ 'npm org ls orgname [<username>]'
+ )
}
-}
-function UsageError () {
- throw Object.assign(new Error(org.usage), { code: 'EUSAGE' })
-}
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv.length === 2)
+ return ['set', 'rm', 'ls']
-function org ([cmd, orgname, username, role], cb) {
- return otplease(npm.flatOptions, opts => {
- switch (cmd) {
+ switch (argv[2]) {
+ case 'ls':
case 'add':
- case 'set':
- return orgSet(orgname, username, role, opts)
case 'rm':
- return orgRm(orgname, username, opts)
- case 'ls':
- return orgList(orgname, username, opts)
+ case 'set':
+ return []
default:
- UsageError()
+ throw new Error(argv[2] + ' not recognized')
}
- }).then(
- x => cb(null, x),
- err => cb(err.code === 'EUSAGE' ? err.message : err)
- )
-}
+ }
-function orgSet (org, user, role, opts) {
- role = role || 'developer'
- if (!org)
- throw new Error('First argument `orgname` is required.')
-
- if (!user)
- throw new Error('Second argument `username` is required.')
-
- if (!['owner', 'admin', 'developer'].find(x => x === role))
- throw new Error('Third argument `role` must be one of `owner`, `admin`, or `developer`, with `developer` being the default value if omitted.')
-
- return liborg.set(org, user, role, opts).then(memDeets => {
- if (opts.json)
- output(JSON.stringify(memDeets, null, 2))
- else if (opts.parseable) {
- output(['org', 'orgsize', 'user', 'role'].join('\t'))
- output([
- memDeets.org.name,
- memDeets.org.size,
- memDeets.user,
- memDeets.role,
- ].join('\t'))
- } else if (!opts.silent && opts.loglevel !== 'silent')
- output(`Added ${memDeets.user} as ${memDeets.role} to ${memDeets.org.name}. You now have ${memDeets.org.size} member${memDeets.org.size === 1 ? '' : 's'} in this org.`)
-
- return memDeets
- })
-}
+ exec (args, cb) {
+ this.org(args)
+ .then(x => cb(null, x))
+ .catch(err => err.code === 'EUSAGE'
+ ? cb(err.message)
+ : cb(err)
+ )
+ }
-function orgRm (org, user, opts) {
- if (!org)
- throw new Error('First argument `orgname` is required.')
-
- if (!user)
- throw new Error('Second argument `username` is required.')
-
- return liborg.rm(org, user, opts).then(() => {
- return liborg.ls(org, opts)
- }).then(roster => {
- user = user.replace(/^[~@]?/, '')
- org = org.replace(/^[~@]?/, '')
- const userCount = Object.keys(roster).length
- if (opts.json) {
- output(JSON.stringify({
- user,
- org,
- userCount,
- deleted: true,
- }))
- } else if (opts.parseable) {
- output(['user', 'org', 'userCount', 'deleted'].join('\t'))
- output([user, org, userCount, true].join('\t'))
- } else if (!opts.silent && opts.loglevel !== 'silent')
- output(`Successfully removed ${user} from ${org}. You now have ${userCount} member${userCount === 1 ? '' : 's'} in this org.`)
- })
-}
+ async org ([cmd, orgname, username, role], cb) {
+ return otplease(this.npm.flatOptions, opts => {
+ switch (cmd) {
+ case 'add':
+ case 'set':
+ return this.set(orgname, username, role, opts)
+ case 'rm':
+ return this.rm(orgname, username, opts)
+ case 'ls':
+ return this.ls(orgname, username, opts)
+ default:
+ throw Object.assign(new Error(this.usage), { code: 'EUSAGE' })
+ }
+ })
+ }
-function orgList (org, user, opts) {
- if (!org)
- throw new Error('First argument `orgname` is required.')
+ set (org, user, role, opts) {
+ role = role || 'developer'
+ if (!org)
+ throw new Error('First argument `orgname` is required.')
+
+ if (!user)
+ throw new Error('Second argument `username` is required.')
+
+ if (!['owner', 'admin', 'developer'].find(x => x === role))
+ throw new Error('Third argument `role` must be one of `owner`, `admin`, or `developer`, with `developer` being the default value if omitted.')
+
+ return liborg.set(org, user, role, opts).then(memDeets => {
+ if (opts.json)
+ output(JSON.stringify(memDeets, null, 2))
+ else if (opts.parseable) {
+ output(['org', 'orgsize', 'user', 'role'].join('\t'))
+ output([
+ memDeets.org.name,
+ memDeets.org.size,
+ memDeets.user,
+ memDeets.role,
+ ].join('\t'))
+ } else if (!opts.silent && opts.loglevel !== 'silent')
+ output(`Added ${memDeets.user} as ${memDeets.role} to ${memDeets.org.name}. You now have ${memDeets.org.size} member${memDeets.org.size === 1 ? '' : 's'} in this org.`)
+
+ return memDeets
+ })
+ }
- return liborg.ls(org, opts).then(roster => {
- if (user) {
- const newRoster = {}
- if (roster[user])
- newRoster[user] = roster[user]
+ rm (org, user, opts) {
+ if (!org)
+ throw new Error('First argument `orgname` is required.')
+
+ if (!user)
+ throw new Error('Second argument `username` is required.')
+
+ return liborg.rm(org, user, opts).then(() => {
+ return liborg.ls(org, opts)
+ }).then(roster => {
+ user = user.replace(/^[~@]?/, '')
+ org = org.replace(/^[~@]?/, '')
+ const userCount = Object.keys(roster).length
+ if (opts.json) {
+ output(JSON.stringify({
+ user,
+ org,
+ userCount,
+ deleted: true,
+ }))
+ } else if (opts.parseable) {
+ output(['user', 'org', 'userCount', 'deleted'].join('\t'))
+ output([user, org, userCount, true].join('\t'))
+ } else if (!opts.silent && opts.loglevel !== 'silent')
+ output(`Successfully removed ${user} from ${org}. You now have ${userCount} member${userCount === 1 ? '' : 's'} in this org.`)
+ })
+ }
- roster = newRoster
- }
- if (opts.json)
- output(JSON.stringify(roster, null, 2))
- else if (opts.parseable) {
- output(['user', 'role'].join('\t'))
- Object.keys(roster).forEach(user => {
- output([user, roster[user]].join('\t'))
- })
- } else if (!opts.silent && opts.loglevel !== 'silent') {
- const table = new Table({ head: ['user', 'role'] })
- Object.keys(roster).sort().forEach(user => {
- table.push([user, roster[user]])
- })
- output(table.toString())
- }
- })
+ ls (org, user, opts) {
+ if (!org)
+ throw new Error('First argument `orgname` is required.')
+
+ return liborg.ls(org, opts).then(roster => {
+ if (user) {
+ const newRoster = {}
+ if (roster[user])
+ newRoster[user] = roster[user]
+
+ roster = newRoster
+ }
+ if (opts.json)
+ output(JSON.stringify(roster, null, 2))
+ else if (opts.parseable) {
+ output(['user', 'role'].join('\t'))
+ Object.keys(roster).forEach(user => {
+ output([user, roster[user]].join('\t'))
+ })
+ } else if (!opts.silent && opts.loglevel !== 'silent') {
+ const table = new Table({ head: ['user', 'role'] })
+ Object.keys(roster).sort().forEach(user => {
+ table.push([user, roster[user]])
+ })
+ output(table.toString())
+ }
+ })
+ }
}
+module.exports = Org
diff --git a/deps/npm/lib/outdated.js b/deps/npm/lib/outdated.js
index c10f63a12e..fc6967faf6 100644
--- a/deps/npm/lib/outdated.js
+++ b/deps/npm/lib/outdated.js
@@ -9,112 +9,135 @@ const pickManifest = require('npm-pick-manifest')
const Arborist = require('@npmcli/arborist')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
const ansiTrim = require('./utils/ansi-trim.js')
-const usage = usageUtil('outdated',
- 'npm outdated [[<@scope>/]<pkg> ...]'
-)
+class Outdated {
+ constructor (npm) {
+ this.npm = npm
+ }
-function cmd (args, cb) {
- outdated(args)
- .then(() => cb())
- .catch(cb)
-}
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('outdated',
+ 'npm outdated [[<@scope>/]<pkg> ...]'
+ )
+ }
-async function outdated (args) {
- const opts = npm.flatOptions
- const global = path.resolve(npm.globalDir, '..')
- const where = opts.global
- ? global
- : npm.prefix
-
- const arb = new Arborist({
- ...opts,
- path: where,
- })
-
- const tree = await arb.loadActual()
- const list = await outdated_(tree, args, opts)
-
- // sorts list alphabetically
- const outdated = list.sort((a, b) => a.name.localeCompare(b.name))
-
- // return if no outdated packages
- if (outdated.length === 0 && !opts.json)
- return
-
- // display results
- if (opts.json)
- output(makeJSON(outdated, opts))
- else if (opts.parseable)
- output(makeParseable(outdated, opts))
- else {
- const outList = outdated.map(x => makePretty(x, opts))
- const outHead = ['Package',
- 'Current',
- 'Wanted',
- 'Latest',
- 'Location',
- 'Depended by',
- ]
-
- if (opts.long)
- outHead.push('Package Type', 'Homepage')
- const outTable = [outHead].concat(outList)
-
- if (opts.color)
- outTable[0] = outTable[0].map(heading => styles.underline(heading))
-
- const tableOpts = {
- align: ['l', 'r', 'r', 'r', 'l'],
- stringLength: s => ansiTrim(s).length,
- }
- output(table(outTable, tableOpts))
+ exec (args, cb) {
+ this.outdated(args).then(() => cb()).catch(cb)
}
-}
-async function outdated_ (tree, deps, opts) {
- const list = []
+ async outdated (args) {
+ this.opts = this.npm.flatOptions
+
+ const global = path.resolve(this.npm.globalDir, '..')
+ const where = this.opts.global
+ ? global
+ : this.npm.prefix
+
+ const arb = new Arborist({
+ ...this.opts,
+ path: where,
+ })
- const edges = new Set()
- function getEdges (nodes, type) {
- const getEdgesIn = (node) => {
- for (const edge of node.edgesIn)
- edges.add(edge)
+ this.edges = new Set()
+ this.list = []
+ this.tree = await arb.loadActual()
+
+ if (args.length !== 0) {
+ // specific deps
+ for (let i = 0; i < args.length; i++) {
+ const nodes = this.tree.inventory.query('name', args[i])
+ this.getEdges(nodes, 'edgesIn')
+ }
+ } else {
+ if (this.opts.all) {
+ // all deps in tree
+ const nodes = this.tree.inventory.values()
+ this.getEdges(nodes, 'edgesOut')
+ }
+ // top-level deps
+ this.getEdges()
}
- const getEdgesOut = (node) => {
- if (opts.global) {
- for (const child of node.children.values())
- edges.add(child)
- } else {
- for (const edge of node.edgesOut.values())
- edges.add(edge)
+ await Promise.all(Array.from(this.edges).map((edge) => {
+ return this.getOutdatedInfo(edge)
+ }))
+
+ // sorts list alphabetically
+ const outdated = this.list.sort((a, b) => a.name.localeCompare(b.name))
+
+ // return if no outdated packages
+ if (outdated.length === 0 && !this.opts.json)
+ return
+
+ // display results
+ if (this.opts.json)
+ output(this.makeJSON(outdated))
+ else if (this.opts.parseable)
+ output(this.makeParseable(outdated))
+ else {
+ const outList = outdated.map(x => this.makePretty(x))
+ const outHead = ['Package',
+ 'Current',
+ 'Wanted',
+ 'Latest',
+ 'Location',
+ 'Depended by',
+ ]
+
+ if (this.opts.long)
+ outHead.push('Package Type', 'Homepage')
+ const outTable = [outHead].concat(outList)
+
+ if (this.opts.color)
+ outTable[0] = outTable[0].map(heading => styles.underline(heading))
+
+ const tableOpts = {
+ align: ['l', 'r', 'r', 'r', 'l'],
+ stringLength: s => ansiTrim(s).length,
}
+ output(table(outTable, tableOpts))
}
+ }
+ getEdges (nodes, type) {
if (!nodes)
- return getEdgesOut(tree)
+ return this.getEdgesOut(this.tree)
for (const node of nodes) {
type === 'edgesOut'
- ? getEdgesOut(node)
- : getEdgesIn(node)
+ ? this.getEdgesOut(node)
+ : this.getEdgesIn(node)
+ }
+ }
+
+ getEdgesIn (node) {
+ for (const edge of node.edgesIn)
+ this.edges.add(edge)
+ }
+
+ getEdgesOut (node) {
+ if (this.opts.global) {
+ for (const child of node.children.values())
+ this.edges.add(child)
+ } else {
+ for (const edge of node.edgesOut.values())
+ this.edges.add(edge)
}
}
- async function getPackument (spec) {
+ async getPackument (spec) {
const packument = await pacote.packument(spec, {
- ...npm.flatOptions,
- fullMetadata: npm.flatOptions.long,
+ ...this.npm.flatOptions,
+ fullMetadata: this.npm.flatOptions.long,
preferOnline: true,
})
return packument
}
- async function getOutdatedInfo (edge) {
+ async getOutdatedInfo (edge) {
const spec = npa(edge.name)
const node = edge.to || edge
const { path, location } = node
@@ -125,7 +148,7 @@ async function outdated_ (tree, deps, opts) {
: edge.dev ? 'devDependencies'
: 'dependencies'
- for (const omitType of opts.omit || []) {
+ for (const omitType of this.opts.omit || []) {
if (node[omitType])
return
}
@@ -136,7 +159,7 @@ async function outdated_ (tree, deps, opts) {
return
try {
- const packument = await getPackument(spec)
+ const packument = await this.getPackument(spec)
const expected = edge.spec
// if it's not a range, version, or tag, skip it
try {
@@ -145,15 +168,15 @@ async function outdated_ (tree, deps, opts) {
} catch (err) {
return null
}
- const wanted = pickManifest(packument, expected, npm.flatOptions)
- const latest = pickManifest(packument, '*', npm.flatOptions)
+ const wanted = pickManifest(packument, expected, this.npm.flatOptions)
+ const latest = pickManifest(packument, '*', this.npm.flatOptions)
if (
!current ||
current !== wanted.version ||
wanted.version !== latest.version
) {
- list.push({
+ this.list.push({
name: edge.name,
path,
type,
@@ -167,7 +190,7 @@ async function outdated_ (tree, deps, opts) {
}
} catch (err) {
// silently catch and ignore ETARGET, E403 &
- // E404 errors, deps are just skipped {
+ // E404 errors, deps are just skipped
if (!(
err.code === 'ETARGET' ||
err.code === 'E403' ||
@@ -177,113 +200,89 @@ async function outdated_ (tree, deps, opts) {
}
}
- const p = []
- if (deps.length !== 0) {
- // specific deps
- for (let i = 0; i < deps.length; i++) {
- const nodes = tree.inventory.query('name', deps[i])
- getEdges(nodes, 'edgesIn')
- }
- } else {
- if (opts.all) {
- // all deps in tree
- const nodes = tree.inventory.values()
- getEdges(nodes, 'edgesOut')
- }
- // top-level deps
- getEdges()
- }
-
- for (const edge of edges)
- p.push(getOutdatedInfo(edge))
-
- await Promise.all(p)
- return list
-}
-
-// formatting functions
-function makePretty (dep, opts) {
- const {
- current = 'MISSING',
- location = '-',
- homepage = '',
- name,
- wanted,
- latest,
- type,
- dependent,
- } = dep
-
- const columns = [name, current, wanted, latest, location, dependent]
-
- if (opts.long) {
- columns[6] = type
- columns[7] = homepage
- }
-
- if (opts.color) {
- columns[0] = color[current === wanted ? 'yellow' : 'red'](columns[0]) // current
- columns[2] = color.green(columns[2]) // wanted
- columns[3] = color.magenta(columns[3]) // latest
- }
-
- return columns
-}
-
-// --parseable creates output like this:
-// <fullpath>:<name@wanted>:<name@installed>:<name@latest>:<dependedby>
-function makeParseable (list, opts) {
- return list.map(dep => {
+ // formatting functions
+ makePretty (dep) {
const {
+ current = 'MISSING',
+ location = '-',
+ homepage = '',
name,
- current,
wanted,
latest,
- path,
- dependent,
type,
- homepage,
- } = dep
- const out = [
- path,
- name + '@' + wanted,
- current ? (name + '@' + current) : 'MISSING',
- name + '@' + latest,
dependent,
- ]
- if (opts.long)
- out.push(type, homepage)
+ } = dep
- return out.join(':')
- }).join(os.EOL)
-}
+ const columns = [name, current, wanted, latest, location, dependent]
-function makeJSON (list, opts) {
- const out = {}
- list.forEach(dep => {
- const {
- name,
- current,
- wanted,
- latest,
- path,
- type,
- dependent,
- homepage,
- } = dep
- out[name] = {
- current,
- wanted,
- latest,
- dependent,
- location: path,
+ if (this.opts.long) {
+ columns[6] = type
+ columns[7] = homepage
}
- if (opts.long) {
- out[name].type = type
- out[name].homepage = homepage
+
+ if (this.opts.color) {
+ columns[0] = color[current === wanted ? 'yellow' : 'red'](columns[0]) // current
+ columns[2] = color.green(columns[2]) // wanted
+ columns[3] = color.magenta(columns[3]) // latest
}
- })
- return JSON.stringify(out, null, 2)
-}
-module.exports = Object.assign(cmd, { usage })
+ return columns
+ }
+
+ // --parseable creates output like this:
+ // <fullpath>:<name@wanted>:<name@installed>:<name@latest>:<dependedby>
+ makeParseable (list) {
+ return list.map(dep => {
+ const {
+ name,
+ current,
+ wanted,
+ latest,
+ path,
+ dependent,
+ type,
+ homepage,
+ } = dep
+ const out = [
+ path,
+ name + '@' + wanted,
+ current ? (name + '@' + current) : 'MISSING',
+ name + '@' + latest,
+ dependent,
+ ]
+ if (this.opts.long)
+ out.push(type, homepage)
+
+ return out.join(':')
+ }).join(os.EOL)
+ }
+
+ makeJSON (list) {
+ const out = {}
+ list.forEach(dep => {
+ const {
+ name,
+ current,
+ wanted,
+ latest,
+ path,
+ type,
+ dependent,
+ homepage,
+ } = dep
+ out[name] = {
+ current,
+ wanted,
+ latest,
+ dependent,
+ location: path,
+ }
+ if (this.opts.long) {
+ out[name].type = type
+ out[name].homepage = homepage
+ }
+ })
+ return JSON.stringify(out, null, 2)
+ }
+}
+module.exports = Outdated
diff --git a/deps/npm/lib/owner.js b/deps/npm/lib/owner.js
index 6dce3ec70f..6cb9904880 100644
--- a/deps/npm/lib/owner.js
+++ b/deps/npm/lib/owner.js
@@ -3,94 +3,138 @@ const npa = require('npm-package-arg')
const npmFetch = require('npm-registry-fetch')
const pacote = require('pacote')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const readLocalPkg = require('./utils/read-local-package.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'owner',
- 'npm owner add <user> [<@scope>/]<pkg>' +
- '\nnpm owner rm <user> [<@scope>/]<pkg>' +
- '\nnpm owner ls [<@scope>/]<pkg>'
-)
+class Owner {
+ constructor (npm) {
+ this.npm = npm
+ }
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
- if (argv.length > 3)
- return []
+ get usage () {
+ return usageUtil(
+ 'owner',
+ 'npm owner add <user> [<@scope>/]<pkg>' +
+ '\nnpm owner rm <user> [<@scope>/]<pkg>' +
+ '\nnpm owner ls [<@scope>/]<pkg>'
+ )
+ }
- if (argv[1] !== 'owner')
- argv.unshift('owner')
+ get usageError () {
+ return Object.assign(new Error(this.usage), { code: 'EUSAGE' })
+ }
- if (argv.length === 2)
- return ['add', 'rm', 'ls']
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv.length > 3)
+ return []
- // reaches registry in order to autocomplete rm
- if (argv[2] === 'rm') {
- const opts = {
- ...npm.flatOptions,
- fullMetadata: true,
+ if (argv[1] !== 'owner')
+ argv.unshift('owner')
+
+ if (argv.length === 2)
+ return ['add', 'rm', 'ls']
+
+ // reaches registry in order to autocomplete rm
+ if (argv[2] === 'rm') {
+ const pkgName = await readLocalPkg(this.npm)
+ if (!pkgName)
+ return []
+
+ const spec = npa(pkgName)
+ const data = await pacote.packument(spec, {
+ ...this.npm.flatOptions,
+ fullMetadata: true,
+ })
+ if (data && data.maintainers && data.maintainers.length)
+ return data.maintainers.map(m => m.name)
}
- const pkgName = await readLocalPkg()
- if (!pkgName)
- return []
+ return []
+ }
- const spec = npa(pkgName)
- const data = await pacote.packument(spec, opts)
- if (data && data.maintainers && data.maintainers.length)
- return data.maintainers.map(m => m.name)
+ exec (args, cb) {
+ this.owner(args).then(() => cb()).catch(cb)
}
- return []
-}
-const UsageError = () =>
- Object.assign(new Error(usage), { code: 'EUSAGE' })
-
-const cmd = (args, cb) => owner(args).then(() => cb()).catch(cb)
-
-const owner = async ([action, ...args]) => {
- const opts = npm.flatOptions
- switch (action) {
- case 'ls':
- case 'list':
- return ls(args[0], opts)
- case 'add':
- return add(args[0], args[1], opts)
- case 'rm':
- case 'remove':
- return rm(args[0], args[1], opts)
- default:
- throw UsageError()
+ async owner ([action, ...args]) {
+ const opts = this.npm.flatOptions
+ switch (action) {
+ case 'ls':
+ case 'list':
+ return this.ls(args[0], opts)
+ case 'add':
+ return this.add(args[0], args[1], opts)
+ case 'rm':
+ case 'remove':
+ return this.rm(args[0], args[1], opts)
+ default:
+ throw this.usageError
+ }
}
-}
-const ls = async (pkg, opts) => {
- if (!pkg) {
- const pkgName = await readLocalPkg()
- if (!pkgName)
- throw UsageError()
+ async ls (pkg, opts) {
+ if (!pkg) {
+ const pkgName = await readLocalPkg(this.npm)
+ if (!pkgName)
+ throw this.usageError
+
+ pkg = pkgName
+ }
+
+ const spec = npa(pkg)
+
+ try {
+ const packumentOpts = { ...opts, fullMetadata: true }
+ const { maintainers } = await pacote.packument(spec, packumentOpts)
+ if (!maintainers || !maintainers.length)
+ output('no admin found')
+ else
+ output(maintainers.map(o => `${o.name} <${o.email}>`).join('\n'))
- pkg = pkgName
+ return maintainers
+ } catch (err) {
+ log.error('owner ls', "Couldn't get owner data", pkg)
+ throw err
+ }
}
- const spec = npa(pkg)
+ async add (user, pkg, opts) {
+ if (!user)
+ throw this.usageError
- try {
- const packumentOpts = { ...opts, fullMetadata: true }
- const { maintainers } = await pacote.packument(spec, packumentOpts)
- if (!maintainers || !maintainers.length)
- output('no admin found')
- else
- output(maintainers.map(o => `${o.name} <${o.email}>`).join('\n'))
+ if (!pkg) {
+ const pkgName = await readLocalPkg(this.npm)
+ if (!pkgName)
+ throw this.usageError
- return maintainers
- } catch (err) {
- log.error('owner ls', "Couldn't get owner data", pkg)
- throw err
+ pkg = pkgName
+ }
+ log.verbose('owner add', '%s to %s', user, pkg)
+
+ const spec = npa(pkg)
+ return putOwners(spec, user, opts, validateAddOwner)
+ }
+
+ async rm (user, pkg, opts) {
+ if (!user)
+ throw this.usageError
+
+ if (!pkg) {
+ const pkgName = await readLocalPkg(this.npm)
+ if (!pkgName)
+ throw this.usageError
+
+ pkg = pkgName
+ }
+ log.verbose('owner rm', '%s from %s', user, pkg)
+
+ const spec = npa(pkg)
+ return putOwners(spec, user, opts, validateRmOwner)
}
}
+module.exports = Owner
const validateAddOwner = (newOwner, owners) => {
owners = owners || []
@@ -109,23 +153,6 @@ const validateAddOwner = (newOwner, owners) => {
]
}
-const add = async (user, pkg, opts) => {
- if (!user)
- throw UsageError()
-
- if (!pkg) {
- const pkgName = await readLocalPkg()
- if (!pkgName)
- throw UsageError()
-
- pkg = pkgName
- }
- log.verbose('owner add', '%s to %s', user, pkg)
-
- const spec = npa(pkg)
- return putOwners(spec, user, opts, validateAddOwner)
-}
-
const validateRmOwner = (rmOwner, owners) => {
let found = false
const m = owners.filter(function (o) {
@@ -151,23 +178,6 @@ const validateRmOwner = (rmOwner, owners) => {
return m
}
-const rm = async (user, pkg, opts) => {
- if (!user)
- throw UsageError()
-
- if (!pkg) {
- const pkgName = await readLocalPkg()
- if (!pkgName)
- throw UsageError()
-
- pkg = pkgName
- }
- log.verbose('owner rm', '%s from %s', user, pkg)
-
- const spec = npa(pkg)
- return putOwners(spec, user, opts, validateRmOwner)
-}
-
const putOwners = async (spec, user, opts, validation) => {
const uri = `/-/user/org.couchdb.user:${encodeURIComponent(user)}`
let u = ''
@@ -227,5 +237,3 @@ const putOwners = async (spec, user, opts, validation) => {
}
return res
}
-
-module.exports = Object.assign(cmd, { usage, completion })
diff --git a/deps/npm/lib/pack.js b/deps/npm/lib/pack.js
index ff906cc2bd..cf1e77f48e 100644
--- a/deps/npm/lib/pack.js
+++ b/deps/npm/lib/pack.js
@@ -4,46 +4,53 @@ const pacote = require('pacote')
const libpack = require('libnpmpack')
const npa = require('npm-package-arg')
-const npm = require('./npm.js')
const { getContents, logTar } = require('./utils/tar.js')
const writeFile = util.promisify(require('fs').writeFile)
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('pack', 'npm pack [[<@scope>/]<pkg>...] [--dry-run]')
-const cmd = (args, cb) => pack(args).then(() => cb()).catch(cb)
+class Pack {
+ constructor (npm) {
+ this.npm = npm
+ }
-const pack = async (args) => {
- if (args.length === 0)
- args = ['.']
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('pack', 'npm pack [[<@scope>/]<pkg>...] [--dry-run]')
+ }
- const { unicode } = npm.flatOptions
+ exec (args, cb) {
+ this.pack(args).then(() => cb()).catch(cb)
+ }
- // clone the opts because pacote mutates it with resolved/integrity
- const tarballs = await Promise.all(args.map((arg) =>
- pack_(arg, { ...npm.flatOptions })))
+ async pack (args) {
+ if (args.length === 0)
+ args = ['.']
- for (const tar of tarballs) {
- logTar(tar, { log, unicode })
- output(tar.filename.replace(/^@/, '').replace(/\//, '-'))
- }
-}
+ const { unicode } = this.npm.flatOptions
-const pack_ = async (arg, opts) => {
- const spec = npa(arg)
- const { dryRun } = opts
- const manifest = await pacote.manifest(spec, opts)
- const filename = `${manifest.name}-${manifest.version}.tgz`
- .replace(/^@/, '').replace(/\//, '-')
- const tarballData = await libpack(arg, opts)
- const pkgContents = await getContents(manifest, tarballData)
+ // clone the opts because pacote mutates it with resolved/integrity
+ const tarballs = await Promise.all(args.map(async (arg) => {
+ const spec = npa(arg)
+ const { dryRun } = this.npm.flatOptions
+ const manifest = await pacote.manifest(spec, this.npm.flatOptions)
+ const filename = `${manifest.name}-${manifest.version}.tgz`
+ .replace(/^@/, '').replace(/\//, '-')
+ const tarballData = await libpack(arg, this.npm.flatOptions)
+ const pkgContents = await getContents(manifest, tarballData)
- if (!dryRun)
- await writeFile(filename, tarballData)
+ if (!dryRun)
+ await writeFile(filename, tarballData)
- return pkgContents
-}
+ return pkgContents
+ }))
-module.exports = Object.assign(cmd, { usage })
+ for (const tar of tarballs) {
+ logTar(tar, { log, unicode })
+ output(tar.filename.replace(/^@/, '').replace(/\//, '-'))
+ }
+ }
+}
+module.exports = Pack
diff --git a/deps/npm/lib/ping.js b/deps/npm/lib/ping.js
index efa2263103..e43f0640f2 100644
--- a/deps/npm/lib/ping.js
+++ b/deps/npm/lib/ping.js
@@ -1,27 +1,36 @@
const log = require('npmlog')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
+const pingUtil = require('./utils/ping.js')
-const usage = usageUtil('ping', 'npm ping\nping registry')
+class Ping {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => ping(args).then(() => cb()).catch(cb)
-const pingUtil = require('./utils/ping.js')
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('ping', 'npm ping\nping registry')
+ }
-const ping = async args => {
- log.notice('PING', npm.flatOptions.registry)
- const start = Date.now()
- const details = await pingUtil(npm.flatOptions)
- const time = Date.now() - start
- log.notice('PONG', `${time / 1000}ms`)
- if (npm.flatOptions.json) {
- output(JSON.stringify({
- registry: npm.flatOptions.registry,
- time,
- details,
- }, null, 2))
- } else if (Object.keys(details).length)
- log.notice('PONG', `${JSON.stringify(details, null, 2)}`)
-}
+ exec (args, cb) {
+ this.ping(args).then(() => cb()).catch(cb)
+ }
-module.exports = Object.assign(cmd, { usage })
+ async ping (args) {
+ log.notice('PING', this.npm.flatOptions.registry)
+ const start = Date.now()
+ const details = await pingUtil(this.npm.flatOptions)
+ const time = Date.now() - start
+ log.notice('PONG', `${time / 1000}ms`)
+ if (this.npm.flatOptions.json) {
+ output(JSON.stringify({
+ registry: this.npm.flatOptions.registry,
+ time,
+ details,
+ }, null, 2))
+ } else if (Object.keys(details).length)
+ log.notice('PONG', `${JSON.stringify(details, null, 2)}`)
+ }
+}
+module.exports = Ping
diff --git a/deps/npm/lib/prefix.js b/deps/npm/lib/prefix.js
index d108b9d423..e46f9c4cdd 100644
--- a/deps/npm/lib/prefix.js
+++ b/deps/npm/lib/prefix.js
@@ -1,7 +1,22 @@
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
-const cmd = (args, cb) => prefix(args).then(() => cb()).catch(cb)
-const usage = usageUtil('prefix', 'npm prefix [-g]')
-const prefix = async (args, cb) => output(npm.prefix)
-module.exports = Object.assign(cmd, { usage })
+
+class Prefix {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('prefix', 'npm prefix [-g]')
+ }
+
+ exec (args, cb) {
+ this.prefix(args).then(() => cb()).catch(cb)
+ }
+
+ async prefix (args) {
+ return output(this.npm.prefix)
+ }
+}
+module.exports = Prefix
diff --git a/deps/npm/lib/profile.js b/deps/npm/lib/profile.js
index 3727ac0c8b..dab99092b0 100644
--- a/deps/npm/lib/profile.js
+++ b/deps/npm/lib/profile.js
@@ -6,71 +6,14 @@ const npmProfile = require('npm-profile')
const qrcodeTerminal = require('qrcode-terminal')
const Table = require('cli-table3')
-const npm = require('./npm.js')
const otplease = require('./utils/otplease.js')
const output = require('./utils/output.js')
const pulseTillDone = require('./utils/pulse-till-done.js')
const readUserInfo = require('./utils/read-user-info.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'npm profile enable-2fa [auth-only|auth-and-writes]\n',
- 'npm profile disable-2fa\n',
- 'npm profile get [<key>]\n',
- 'npm profile set <key> <value>'
-)
-
-const completion = async (opts) => {
- var argv = opts.conf.argv.remain
- const subcommands = ['enable-2fa', 'disable-2fa', 'get', 'set']
-
- if (!argv[2])
- return subcommands
-
- switch (argv[2]) {
- case 'enable-2fa':
- case 'enable-tfa':
- return ['auth-and-writes', 'auth-only']
-
- case 'disable-2fa':
- case 'disable-tfa':
- case 'get':
- case 'set':
- return []
- default:
- throw new Error(argv[2] + ' not recognized')
- }
-}
-
-const cmd = (args, cb) => profile(args).then(() => cb()).catch(cb)
-
-const profile = async (args) => {
- if (args.length === 0)
- throw new Error(usage)
-
- log.gauge.show('profile')
-
- const [subcmd, ...opts] = args
-
- switch (subcmd) {
- case 'enable-2fa':
- case 'enable-tfa':
- case 'enable2fa':
- case 'enabletfa':
- return enable2fa(opts)
- case 'disable-2fa':
- case 'disable-tfa':
- case 'disable2fa':
- case 'disabletfa':
- return disable2fa()
- case 'get':
- return get(opts)
- case 'set':
- return set(opts)
- default:
- throw new Error('Unknown profile command: ' + subcmd)
- }
-}
+const qrcode = url =>
+ new Promise((resolve) => qrcodeTerminal.generate(url, resolve))
const knownProfileKeys = [
'name',
@@ -85,64 +28,6 @@ const knownProfileKeys = [
'updated',
]
-const get = async args => {
- const tfa = 'two-factor auth'
- const conf = { ...npm.flatOptions }
-
- const info = await pulseTillDone.withPromise(npmProfile.get(conf))
-
- if (!info.cidr_whitelist)
- delete info.cidr_whitelist
-
- if (conf.json) {
- output(JSON.stringify(info, null, 2))
- return
- }
-
- // clean up and format key/values for output
- const cleaned = {}
- for (const key of knownProfileKeys)
- cleaned[key] = info[key] || ''
-
- const unknownProfileKeys = Object.keys(info).filter((k) => !(k in cleaned))
- for (const key of unknownProfileKeys)
- cleaned[key] = info[key] || ''
-
- delete cleaned.tfa
- delete cleaned.email_verified
- cleaned.email += info.email_verified ? ' (verified)' : '(unverified)'
-
- if (info.tfa && !info.tfa.pending)
- cleaned[tfa] = info.tfa.mode
- else
- cleaned[tfa] = 'disabled'
-
- if (args.length) {
- const values = args // comma or space separated
- .join(',')
- .split(/,/)
- .filter((arg) => arg.trim() !== '')
- .map((arg) => cleaned[arg])
- .join('\t')
- output(values)
- } else {
- if (conf.parseable) {
- for (const key of Object.keys(info)) {
- if (key === 'tfa')
- output(`${key}\t${cleaned[tfa]}`)
- else
- output(`${key}\t${info[key]}`)
- }
- } else {
- const table = new Table()
- for (const key of Object.keys(cleaned))
- table.push({ [ansistyles.bright(key)]: cleaned[key] })
-
- output(table.toString())
- }
- }
-}
-
const writableProfileKeys = [
'email',
'password',
@@ -153,242 +38,364 @@ const writableProfileKeys = [
'github',
]
-const set = async (args) => {
- const conf = { ...npm.flatOptions }
- const prop = (args[0] || '').toLowerCase().trim()
+class Profile {
+ constructor (npm) {
+ this.npm = npm
+ }
- let value = args.length > 1 ? args.slice(1).join(' ') : null
+ get usage () {
+ return usageUtil(
+ 'profile',
+ 'npm profile enable-2fa [auth-only|auth-and-writes]\n',
+ 'npm profile disable-2fa\n',
+ 'npm profile get [<key>]\n',
+ 'npm profile set <key> <value>'
+ )
+ }
- const readPasswords = async () => {
- const newpassword = await readUserInfo.password('New password: ')
- const confirmedpassword = await readUserInfo.password(' Again: ')
+ async completion (opts) {
+ var argv = opts.conf.argv.remain
- if (newpassword !== confirmedpassword) {
- log.warn('profile', 'Passwords do not match, please try again.')
- return readPasswords()
- }
+ if (!argv[2])
+ return ['enable-2fa', 'disable-2fa', 'get', 'set']
- return newpassword
- }
+ switch (argv[2]) {
+ case 'enable-2fa':
+ case 'enable-tfa':
+ return ['auth-and-writes', 'auth-only']
- if (prop !== 'password' && value === null)
- throw new Error('npm profile set <prop> <value>')
+ case 'disable-2fa':
+ case 'disable-tfa':
+ case 'get':
+ case 'set':
+ return []
+ default:
+ throw new Error(argv[2] + ' not recognized')
+ }
+ }
- if (prop === 'password' && value !== null) {
- throw new Error(
- 'npm profile set password\n' +
- 'Do not include your current or new passwords on the command line.')
+ exec (args, cb) {
+ this.profile(args).then(() => cb()).catch(cb)
}
- if (writableProfileKeys.indexOf(prop) === -1) {
- throw new Error(`"${prop}" is not a property we can set. ` +
- `Valid properties are: ` + writableProfileKeys.join(', '))
+ async profile (args) {
+ if (args.length === 0)
+ throw new Error(this.usage)
+
+ log.gauge.show('profile')
+
+ const [subcmd, ...opts] = args
+
+ switch (subcmd) {
+ case 'enable-2fa':
+ case 'enable-tfa':
+ case 'enable2fa':
+ case 'enabletfa':
+ return this.enable2fa(opts)
+ case 'disable-2fa':
+ case 'disable-tfa':
+ case 'disable2fa':
+ case 'disabletfa':
+ return this.disable2fa()
+ case 'get':
+ return this.get(opts)
+ case 'set':
+ return this.set(opts)
+ default:
+ throw new Error('Unknown profile command: ' + subcmd)
+ }
}
- if (prop === 'password') {
- const current = await readUserInfo.password('Current password: ')
- const newpassword = await readPasswords()
+ async get (args) {
+ const tfa = 'two-factor auth'
+ const conf = { ...this.npm.flatOptions }
+
+ const info = await pulseTillDone.withPromise(npmProfile.get(conf))
+
+ if (!info.cidr_whitelist)
+ delete info.cidr_whitelist
+
+ if (conf.json) {
+ output(JSON.stringify(info, null, 2))
+ return
+ }
- value = { old: current, new: newpassword }
+ // clean up and format key/values for output
+ const cleaned = {}
+ for (const key of knownProfileKeys)
+ cleaned[key] = info[key] || ''
+
+ const unknownProfileKeys = Object.keys(info).filter((k) => !(k in cleaned))
+ for (const key of unknownProfileKeys)
+ cleaned[key] = info[key] || ''
+
+ delete cleaned.tfa
+ delete cleaned.email_verified
+ cleaned.email += info.email_verified ? ' (verified)' : '(unverified)'
+
+ if (info.tfa && !info.tfa.pending)
+ cleaned[tfa] = info.tfa.mode
+ else
+ cleaned[tfa] = 'disabled'
+
+ if (args.length) {
+ const values = args // comma or space separated
+ .join(',')
+ .split(/,/)
+ .filter((arg) => arg.trim() !== '')
+ .map((arg) => cleaned[arg])
+ .join('\t')
+ output(values)
+ } else {
+ if (conf.parseable) {
+ for (const key of Object.keys(info)) {
+ if (key === 'tfa')
+ output(`${key}\t${cleaned[tfa]}`)
+ else
+ output(`${key}\t${info[key]}`)
+ }
+ } else {
+ const table = new Table()
+ for (const key of Object.keys(cleaned))
+ table.push({ [ansistyles.bright(key)]: cleaned[key] })
+
+ output(table.toString())
+ }
+ }
}
- // FIXME: Work around to not clear everything other than what we're setting
- const user = await pulseTillDone.withPromise(npmProfile.get(conf))
- const newUser = {}
+ async set (args) {
+ const conf = { ...this.npm.flatOptions }
+ const prop = (args[0] || '').toLowerCase().trim()
- for (const key of writableProfileKeys)
- newUser[key] = user[key]
+ let value = args.length > 1 ? args.slice(1).join(' ') : null
- newUser[prop] = value
+ const readPasswords = async () => {
+ const newpassword = await readUserInfo.password('New password: ')
+ const confirmedpassword = await readUserInfo.password(' Again: ')
- const result = await otplease(conf, conf => npmProfile.set(newUser, conf))
+ if (newpassword !== confirmedpassword) {
+ log.warn('profile', 'Passwords do not match, please try again.')
+ return readPasswords()
+ }
- if (conf.json)
- output(JSON.stringify({ [prop]: result[prop] }, null, 2))
- else if (conf.parseable)
- output(prop + '\t' + result[prop])
- else if (result[prop] != null)
- output('Set', prop, 'to', result[prop])
- else
- output('Set', prop)
-}
+ return newpassword
+ }
-const enable2fa = async (args) => {
- if (args.length > 1)
- throw new Error('npm profile enable-2fa [auth-and-writes|auth-only]')
-
- const mode = args[0] || 'auth-and-writes'
- if (mode !== 'auth-only' && mode !== 'auth-and-writes') {
- throw new Error(
- `Invalid two-factor authentication mode "${mode}".\n` +
- 'Valid modes are:\n' +
- ' auth-only - Require two-factor authentication only when logging in\n' +
- ' auth-and-writes - Require two-factor authentication when logging in ' +
- 'AND when publishing'
- )
- }
+ if (prop !== 'password' && value === null)
+ throw new Error('npm profile set <prop> <value>')
- const conf = { ...npm.flatOptions }
- if (conf.json || conf.parseable) {
- throw new Error(
- 'Enabling two-factor authentication is an interactive operation and ' +
- (conf.json ? 'JSON' : 'parseable') + ' output mode is not available'
- )
- }
+ if (prop === 'password' && value !== null) {
+ throw new Error(
+ 'npm profile set password\n' +
+ 'Do not include your current or new passwords on the command line.')
+ }
- const info = {
- tfa: {
- mode: mode,
- },
- }
+ if (writableProfileKeys.indexOf(prop) === -1) {
+ throw new Error(`"${prop}" is not a property we can set. ` +
+ `Valid properties are: ` + writableProfileKeys.join(', '))
+ }
- // if they're using legacy auth currently then we have to
- // update them to a bearer token before continuing.
- const auth = getAuth(conf)
+ if (prop === 'password') {
+ const current = await readUserInfo.password('Current password: ')
+ const newpassword = await readPasswords()
- if (!auth.basic && !auth.token) {
- throw new Error(
- 'You need to be logged in to registry ' +
- `${conf.registry} in order to enable 2fa`
- )
+ value = { old: current, new: newpassword }
+ }
+
+ // FIXME: Work around to not clear everything other than what we're setting
+ const user = await pulseTillDone.withPromise(npmProfile.get(conf))
+ const newUser = {}
+
+ for (const key of writableProfileKeys)
+ newUser[key] = user[key]
+
+ newUser[prop] = value
+
+ const result = await otplease(conf, conf => npmProfile.set(newUser, conf))
+
+ if (conf.json)
+ output(JSON.stringify({ [prop]: result[prop] }, null, 2))
+ else if (conf.parseable)
+ output(prop + '\t' + result[prop])
+ else if (result[prop] != null)
+ output('Set', prop, 'to', result[prop])
+ else
+ output('Set', prop)
}
- if (auth.basic) {
- log.info('profile', 'Updating authentication to bearer token')
- const result = await npmProfile.createToken(
- auth.basic.password, false, [], conf
- )
+ async enable2fa (args) {
+ if (args.length > 1)
+ throw new Error('npm profile enable-2fa [auth-and-writes|auth-only]')
- if (!result.token) {
+ const mode = args[0] || 'auth-and-writes'
+ if (mode !== 'auth-only' && mode !== 'auth-and-writes') {
throw new Error(
- `Your registry ${conf.registry} does not seem to ` +
- 'support bearer tokens. Bearer tokens are required for ' +
- 'two-factor authentication'
+ `Invalid two-factor authentication mode "${mode}".\n` +
+ 'Valid modes are:\n' +
+ ' auth-only - Require two-factor authentication only when logging in\n' +
+ ' auth-and-writes - Require two-factor authentication when logging in ' +
+ 'AND when publishing'
)
}
- npm.config.setCredentialsByURI(conf.registry, { token: result.token })
- await npm.config.save('user')
- }
+ const conf = { ...this.npm.flatOptions }
+ if (conf.json || conf.parseable) {
+ throw new Error(
+ 'Enabling two-factor authentication is an interactive operation and ' +
+ (conf.json ? 'JSON' : 'parseable') + ' output mode is not available'
+ )
+ }
- log.notice('profile', 'Enabling two factor authentication for ' + mode)
- const password = await readUserInfo.password()
- info.tfa.password = password
+ const info = {
+ tfa: {
+ mode: mode,
+ },
+ }
- log.info('profile', 'Determine if tfa is pending')
- const userInfo = await pulseTillDone.withPromise(npmProfile.get(conf))
+ // if they're using legacy auth currently then we have to
+ // update them to a bearer token before continuing.
+ const creds = this.npm.config.getCredentialsByURI(conf.registry)
+ const auth = {}
+
+ if (creds.token)
+ auth.token = creds.token
+ else if (creds.username)
+ auth.basic = { username: creds.username, password: creds.password }
+ else if (creds.auth) {
+ const basic = Buffer.from(creds.auth, 'base64').toString().split(':', 2)
+ auth.basic = { username: basic[0], password: basic[1] }
+ }
- if (userInfo && userInfo.tfa && userInfo.tfa.pending) {
- log.info('profile', 'Resetting two-factor authentication')
- await pulseTillDone.withPromise(
- npmProfile.set({ tfa: { password, mode: 'disable' } }, conf)
- )
- } else if (userInfo && userInfo.tfa) {
if (conf.otp)
- conf.otp = conf.otp
- else {
- const otp = await readUserInfo.otp(
- 'Enter one-time password from your authenticator app: '
+ auth.otp = conf.otp
+
+ if (!auth.basic && !auth.token) {
+ throw new Error(
+ 'You need to be logged in to registry ' +
+ `${conf.registry} in order to enable 2fa`
)
- conf.otp = otp
}
- }
- log.info('profile', 'Setting two-factor authentication to ' + mode)
- const challenge = await pulseTillDone.withPromise(npmProfile.set(info, conf))
+ if (auth.basic) {
+ log.info('profile', 'Updating authentication to bearer token')
+ const result = await npmProfile.createToken(
+ auth.basic.password, false, [], conf
+ )
- if (challenge.tfa === null) {
- output('Two factor authentication mode changed to: ' + mode)
- return
- }
+ if (!result.token) {
+ throw new Error(
+ `Your registry ${conf.registry} does not seem to ` +
+ 'support bearer tokens. Bearer tokens are required for ' +
+ 'two-factor authentication'
+ )
+ }
- const badResponse = typeof challenge.tfa !== 'string'
- || !/^otpauth:[/][/]/.test(challenge.tfa)
- if (badResponse) {
- throw new Error(
- 'Unknown error enabling two-factor authentication. Expected otpauth URL' +
- ', got: ' + inspect(challenge.tfa)
- )
- }
+ this.npm.config.setCredentialsByURI(
+ conf.registry,
+ { token: result.token }
+ )
+ await this.npm.config.save('user')
+ }
- const otpauth = new URL(challenge.tfa)
- const secret = otpauth.searchParams.get('secret')
- const code = await qrcode(challenge.tfa)
+ log.notice('profile', 'Enabling two factor authentication for ' + mode)
+ const password = await readUserInfo.password()
+ info.tfa.password = password
- output(
- 'Scan into your authenticator app:\n' + code + '\n Or enter code:', secret
- )
+ log.info('profile', 'Determine if tfa is pending')
+ const userInfo = await pulseTillDone.withPromise(npmProfile.get(conf))
- const interactiveOTP =
- await readUserInfo.otp('And an OTP code from your authenticator: ')
+ if (userInfo && userInfo.tfa && userInfo.tfa.pending) {
+ log.info('profile', 'Resetting two-factor authentication')
+ await pulseTillDone.withPromise(
+ npmProfile.set({ tfa: { password, mode: 'disable' } }, conf)
+ )
+ } else if (userInfo && userInfo.tfa) {
+ if (conf.otp)
+ conf.otp = conf.otp
+ else {
+ const otp = await readUserInfo.otp(
+ 'Enter one-time password from your authenticator app: '
+ )
+ conf.otp = otp
+ }
+ }
- log.info('profile', 'Finalizing two-factor authentication')
+ log.info('profile', 'Setting two-factor authentication to ' + mode)
+ const challenge = await pulseTillDone.withPromise(
+ npmProfile.set(info, conf)
+ )
- const result = await npmProfile.set({ tfa: [interactiveOTP] }, conf)
+ if (challenge.tfa === null) {
+ output('Two factor authentication mode changed to: ' + mode)
+ return
+ }
- output(
- '2FA successfully enabled. Below are your recovery codes, ' +
- 'please print these out.'
- )
- output(
- 'You will need these to recover access to your account ' +
- 'if you lose your authentication device.'
- )
+ const badResponse = typeof challenge.tfa !== 'string'
+ || !/^otpauth:[/][/]/.test(challenge.tfa)
+ if (badResponse) {
+ throw new Error(
+ 'Unknown error enabling two-factor authentication. Expected otpauth URL' +
+ ', got: ' + inspect(challenge.tfa)
+ )
+ }
- for (const tfaCode of result.tfa)
- output('\t' + tfaCode)
-}
+ const otpauth = new URL(challenge.tfa)
+ const secret = otpauth.searchParams.get('secret')
+ const code = await qrcode(challenge.tfa)
-const getAuth = conf => {
- const creds = npm.config.getCredentialsByURI(conf.registry)
- const auth = {}
-
- if (creds.token)
- auth.token = creds.token
- else if (creds.username)
- auth.basic = { username: creds.username, password: creds.password }
- else if (creds.auth) {
- const basic = Buffer.from(creds.auth, 'base64').toString().split(':', 2)
- auth.basic = { username: basic[0], password: basic[1] }
- }
+ output(
+ 'Scan into your authenticator app:\n' + code + '\n Or enter code:', secret
+ )
- if (conf.otp)
- auth.otp = conf.otp
+ const interactiveOTP =
+ await readUserInfo.otp('And an OTP code from your authenticator: ')
- return auth
-}
+ log.info('profile', 'Finalizing two-factor authentication')
-const disable2fa = async args => {
- const conf = { ...npm.flatOptions }
- const info = await pulseTillDone.withPromise(npmProfile.get(conf))
+ const result = await npmProfile.set({ tfa: [interactiveOTP] }, conf)
- if (!info.tfa || info.tfa.pending) {
- output('Two factor authentication not enabled.')
- return
+ output(
+ '2FA successfully enabled. Below are your recovery codes, ' +
+ 'please print these out.'
+ )
+ output(
+ 'You will need these to recover access to your account ' +
+ 'if you lose your authentication device.'
+ )
+
+ for (const tfaCode of result.tfa)
+ output('\t' + tfaCode)
}
- const password = await readUserInfo.password()
+ async disable2fa (args) {
+ const conf = { ...this.npm.flatOptions }
+ const info = await pulseTillDone.withPromise(npmProfile.get(conf))
- if (!conf.otp) {
- const msg = 'Enter one-time password from your authenticator app: '
- conf.otp = await readUserInfo.otp(msg)
- }
+ if (!info.tfa || info.tfa.pending) {
+ output('Two factor authentication not enabled.')
+ return
+ }
- log.info('profile', 'disabling tfa')
+ const password = await readUserInfo.password()
- await pulseTillDone.withPromise(npmProfile.set({
- tfa: { password: password, mode: 'disable' },
- }, conf))
+ if (!conf.otp) {
+ const msg = 'Enter one-time password from your authenticator app: '
+ conf.otp = await readUserInfo.otp(msg)
+ }
- if (conf.json)
- output(JSON.stringify({ tfa: false }, null, 2))
- else if (conf.parseable)
- output('tfa\tfalse')
- else
- output('Two factor authentication disabled.')
-}
+ log.info('profile', 'disabling tfa')
-const qrcode = url =>
- new Promise((resolve) => qrcodeTerminal.generate(url, resolve))
+ await pulseTillDone.withPromise(npmProfile.set({
+ tfa: { password: password, mode: 'disable' },
+ }, conf))
-module.exports = Object.assign(cmd, { usage, completion })
+ if (conf.json)
+ output(JSON.stringify({ tfa: false }, null, 2))
+ else if (conf.parseable)
+ output('tfa\tfalse')
+ else
+ output('Two factor authentication disabled.')
+ }
+}
+module.exports = Profile
diff --git a/deps/npm/lib/prune.js b/deps/npm/lib/prune.js
index 228fd3eebb..b839301d51 100644
--- a/deps/npm/lib/prune.js
+++ b/deps/npm/lib/prune.js
@@ -1,24 +1,32 @@
// prune extraneous packages
-const npm = require('./npm.js')
const Arborist = require('@npmcli/arborist')
const usageUtil = require('./utils/usage.js')
-
const reifyFinish = require('./utils/reify-finish.js')
-const usage = usageUtil('prune',
- 'npm prune [[<@scope>/]<pkg>...] [--production]'
-)
+class Prune {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => prune().then(() => cb()).catch(cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('prune',
+ 'npm prune [[<@scope>/]<pkg>...] [--production]'
+ )
+ }
-const prune = async () => {
- const where = npm.prefix
- const arb = new Arborist({
- ...npm.flatOptions,
- path: where,
- })
- await arb.prune(npm.flatOptions)
- await reifyFinish(arb)
-}
+ exec (args, cb) {
+ this.prune().then(() => cb()).catch(cb)
+ }
-module.exports = Object.assign(cmd, { usage })
+ async prune () {
+ const where = this.npm.prefix
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ path: where,
+ })
+ await arb.prune(this.npm.flatOptions)
+ await reifyFinish(this.npm, arb)
+ }
+}
+module.exports = Prune
diff --git a/deps/npm/lib/publish.js b/deps/npm/lib/publish.js
index 5ec66d42fa..c8e82c44c5 100644
--- a/deps/npm/lib/publish.js
+++ b/deps/npm/lib/publish.js
@@ -8,9 +8,10 @@ const pacote = require('pacote')
const npa = require('npm-package-arg')
const npmFetch = require('npm-registry-fetch')
-const npm = require('./npm.js')
+const { flatten } = require('./utils/flat-options.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
+const usageUtil = require('./utils/usage.js')
const { getContents, logTar } = require('./utils/tar.js')
// this is the only case in the CLI where we use the old full slow
@@ -18,122 +19,125 @@ const { getContents, logTar } = require('./utils/tar.js')
// defaults and metadata, like git sha's and default scripts and all that.
const readJson = util.promisify(require('read-package-json'))
-const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('publish',
- 'npm publish [<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]' +
- '\n\nPublishes \'.\' if no argument supplied' +
- '\nSets tag `latest` if no --tag specified')
+class Publish {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => publish(args).then(() => cb()).catch(cb)
+ get usage () {
+ return usageUtil('publish',
+ 'npm publish [<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]' +
+ '\n\nPublishes \'.\' if no argument supplied' +
+ '\nSets tag `latest` if no --tag specified')
+ }
-const publish = async args => {
- if (args.length === 0)
- args = ['.']
- if (args.length !== 1)
- throw usage
+ exec (args, cb) {
+ this.publish(args).then(() => cb()).catch(cb)
+ }
- log.verbose('publish', args)
+ async publish (args) {
+ if (args.length === 0)
+ args = ['.']
+ if (args.length !== 1)
+ throw this.usage
- const opts = { ...npm.flatOptions }
- const { json, defaultTag } = opts
+ log.verbose('publish', args)
- if (semver.validRange(defaultTag))
- throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())
+ const opts = { ...this.npm.flatOptions }
+ const { unicode, dryRun, json, defaultTag } = opts
- const tarball = await publish_(args[0], opts)
- const silent = log.level === 'silent'
- if (!silent && json)
- output(JSON.stringify(tarball, null, 2))
- else if (!silent)
- output(`+ ${tarball.id}`)
+ if (semver.validRange(defaultTag))
+ throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())
- return tarball
-}
+ // you can publish name@version, ./foo.tgz, etc.
+ // even though the default is the 'file:.' cwd.
+ const spec = npa(args[0])
+ let manifest = await this.getManifest(spec, opts)
-// if it's a directory, read it from the file system
-// otherwise, get the full metadata from whatever it is
-const getManifest = (spec, opts) =>
- spec.type === 'directory' ? readJson(`${spec.fetchSpec}/package.json`)
- : pacote.manifest(spec, { ...opts, fullMetadata: true })
+ if (manifest.publishConfig)
+ Object.assign(opts, this.publishConfigToOpts(manifest.publishConfig))
-// for historical reasons, publishConfig in package.json can contain
-// ANY config keys that npm supports in .npmrc files and elsewhere.
-// We *may* want to revisit this at some point, and have a minimal set
-// that's a SemVer-major change that ought to get a RFC written on it.
-const { flatten } = require('./utils/flat-options.js')
-const publishConfigToOpts = publishConfig =>
- // create a new object that inherits from the config stack
- // then squash the css-case into camelCase opts, like we do
- flatten(Object.assign(Object.create(npm.config.list[0]), publishConfig))
-
-const publish_ = async (arg, opts) => {
- const { unicode, dryRun, json } = opts
- // you can publish name@version, ./foo.tgz, etc.
- // even though the default is the 'file:.' cwd.
- const spec = npa(arg)
-
- let manifest = await getManifest(spec, opts)
-
- if (manifest.publishConfig)
- Object.assign(opts, publishConfigToOpts(manifest.publishConfig))
-
- // only run scripts for directory type publishes
- if (spec.type === 'directory') {
- await runScript({
- event: 'prepublishOnly',
- path: spec.fetchSpec,
- stdio: 'inherit',
- pkg: manifest,
- banner: log.level !== 'silent',
- })
- }
+ // only run scripts for directory type publishes
+ if (spec.type === 'directory') {
+ await runScript({
+ event: 'prepublishOnly',
+ path: spec.fetchSpec,
+ stdio: 'inherit',
+ pkg: manifest,
+ banner: log.level !== 'silent',
+ })
+ }
+
+ const tarballData = await pack(spec, opts)
+ const pkgContents = await getContents(manifest, tarballData)
+
+ // The purpose of re-reading the manifest is in case it changed,
+ // so that we send the latest and greatest thing to the registry
+ // note that publishConfig might have changed as well!
+ manifest = await this.getManifest(spec, opts)
+ if (manifest.publishConfig)
+ Object.assign(opts, this.publishConfigToOpts(manifest.publishConfig))
+
+ // note that logTar calls npmlog.notice(), so if we ARE in silent mode,
+ // this will do nothing, but we still want it in the debuglog if it fails.
+ if (!json)
+ logTar(pkgContents, { log, unicode })
+
+ if (!dryRun) {
+ const resolved = npa.resolve(manifest.name, manifest.version)
+ const registry = npmFetch.pickRegistry(resolved, opts)
+ const creds = this.npm.config.getCredentialsByURI(registry)
+ if (!creds.token && !creds.username) {
+ throw Object.assign(new Error('This command requires you to be logged in.'), {
+ code: 'ENEEDAUTH',
+ })
+ }
+ await otplease(opts, opts => libpub(manifest, tarballData, opts))
+ }
+
+ if (spec.type === 'directory') {
+ await runScript({
+ event: 'publish',
+ path: spec.fetchSpec,
+ stdio: 'inherit',
+ pkg: manifest,
+ banner: log.level !== 'silent',
+ })
- const tarballData = await pack(spec, opts)
- const pkgContents = await getContents(manifest, tarballData)
-
- // The purpose of re-reading the manifest is in case it changed,
- // so that we send the latest and greatest thing to the registry
- // note that publishConfig might have changed as well!
- manifest = await getManifest(spec, opts)
- if (manifest.publishConfig)
- Object.assign(opts, publishConfigToOpts(manifest.publishConfig))
-
- // note that logTar calls npmlog.notice(), so if we ARE in silent mode,
- // this will do nothing, but we still want it in the debuglog if it fails.
- if (!json)
- logTar(pkgContents, { log, unicode })
-
- if (!dryRun) {
- const resolved = npa.resolve(manifest.name, manifest.version)
- const registry = npmFetch.pickRegistry(resolved, opts)
- const creds = npm.config.getCredentialsByURI(registry)
- if (!creds.token && !creds.username) {
- throw Object.assign(new Error('This command requires you to be logged in.'), {
- code: 'ENEEDAUTH',
+ await runScript({
+ event: 'postpublish',
+ path: spec.fetchSpec,
+ stdio: 'inherit',
+ pkg: manifest,
+ banner: log.level !== 'silent',
})
}
- await otplease(opts, opts => libpub(manifest, tarballData, opts))
+
+ const silent = log.level === 'silent'
+ if (!silent && json)
+ output(JSON.stringify(pkgContents, null, 2))
+ else if (!silent)
+ output(`+ ${pkgContents.id}`)
+
+ return pkgContents
}
- if (spec.type === 'directory') {
- await runScript({
- event: 'publish',
- path: spec.fetchSpec,
- stdio: 'inherit',
- pkg: manifest,
- banner: log.level !== 'silent',
- })
-
- await runScript({
- event: 'postpublish',
- path: spec.fetchSpec,
- stdio: 'inherit',
- pkg: manifest,
- banner: log.level !== 'silent',
- })
+ // if it's a directory, read it from the file system
+ // otherwise, get the full metadata from whatever it is
+ getManifest (spec, opts) {
+ if (spec.type === 'directory')
+ return readJson(`${spec.fetchSpec}/package.json`)
+ return pacote.manifest(spec, { ...opts, fullMetadata: true })
}
- return pkgContents
+ // for historical reasons, publishConfig in package.json can contain
+ // ANY config keys that npm supports in .npmrc files and elsewhere.
+ // We *may* want to revisit this at some point, and have a minimal set
+ // that's a SemVer-major change that ought to get a RFC written on it.
+ publishConfigToOpts (publishConfig) {
+ // create a new object that inherits from the config stack
+ // then squash the css-case into camelCase opts, like we do
+ return flatten({...this.npm.config.list[0], ...publishConfig})
+ }
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Publish
diff --git a/deps/npm/lib/rebuild.js b/deps/npm/lib/rebuild.js
index ab34b7f3df..1091b01589 100644
--- a/deps/npm/lib/rebuild.js
+++ b/deps/npm/lib/rebuild.js
@@ -2,64 +2,74 @@ const { resolve } = require('path')
const Arborist = require('@npmcli/arborist')
const npa = require('npm-package-arg')
const semver = require('semver')
-
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const output = require('./utils/output.js')
+const completion = require('./utils/completion/installed-deep.js')
-const cmd = (args, cb) => rebuild(args).then(() => cb()).catch(cb)
+class Rebuild {
+ constructor (npm) {
+ this.npm = npm
+ }
-const usage = usageUtil('rebuild', 'npm rebuild [[<@scope>/]<name>[@<version>] ...]')
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('rebuild', 'npm rebuild [[<@scope>/]<name>[@<version>] ...]')
+ }
-const completion = require('./utils/completion/installed-deep.js')
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
-const rebuild = async args => {
- const globalTop = resolve(npm.globalDir, '..')
- const where = npm.flatOptions.global ? globalTop : npm.prefix
- const arb = new Arborist({
- ...npm.flatOptions,
- path: where,
- })
+ exec (args, cb) {
+ this.rebuild(args).then(() => cb()).catch(cb)
+ }
- if (args.length) {
- // get the set of nodes matching the name that we want rebuilt
- const tree = await arb.loadActual()
- const filter = getFilterFn(args)
- await arb.rebuild({
- nodes: tree.inventory.filter(filter),
+ async rebuild (args) {
+ const globalTop = resolve(this.npm.globalDir, '..')
+ const where = this.npm.flatOptions.global ? globalTop : this.npm.prefix
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ path: where,
})
- } else
- await arb.rebuild()
- output('rebuilt dependencies successfully')
-}
+ if (args.length) {
+ // get the set of nodes matching the name that we want rebuilt
+ const tree = await arb.loadActual()
+ const specs = args.map(arg => {
+ const spec = npa(arg)
+ if (spec.type === 'tag' && spec.rawSpec === '')
+ return spec
-const getFilterFn = args => {
- const specs = args.map(arg => {
- const spec = npa(arg)
- if (spec.type === 'tag' && spec.rawSpec === '')
- return spec
+ if (spec.type !== 'range' && spec.type !== 'version' && spec.type !== 'directory')
+ throw new Error('`npm rebuild` only supports SemVer version/range specifiers')
- if (spec.type !== 'range' && spec.type !== 'version' && spec.type !== 'directory')
- throw new Error('`npm rebuild` only supports SemVer version/range specifiers')
+ return spec
+ })
+ const nodes = tree.inventory.filter(node => this.isNode(specs, node))
- return spec
- })
+ await arb.rebuild({ nodes })
+ } else
+ await arb.rebuild()
- return node => specs.some(spec => {
- if (spec.type === 'directory')
- return node.path === spec.fetchSpec
+ output('rebuilt dependencies successfully')
+ }
- if (spec.name !== node.name)
- return false
+ isNode (specs, node) {
+ return specs.some(spec => {
+ if (spec.type === 'directory')
+ return node.path === spec.fetchSpec
- if (spec.rawSpec === '' || spec.rawSpec === '*')
- return true
+ if (spec.name !== node.name)
+ return false
- const { version } = node.package
- // TODO: add tests for a package with missing version
- return semver.satisfies(version, spec.fetchSpec)
- })
-}
+ if (spec.rawSpec === '' || spec.rawSpec === '*')
+ return true
-module.exports = Object.assign(cmd, { usage, completion })
+ const { version } = node.package
+ // TODO: add tests for a package with missing version
+ return semver.satisfies(version, spec.fetchSpec)
+ })
+ }
+}
+module.exports = Rebuild
diff --git a/deps/npm/lib/repo.js b/deps/npm/lib/repo.js
index e9074dca68..f0be99d4d6 100644
--- a/deps/npm/lib/repo.js
+++ b/deps/npm/lib/repo.js
@@ -1,52 +1,63 @@
const log = require('npmlog')
const pacote = require('pacote')
-const { promisify } = require('util')
-const openUrl = promisify(require('./utils/open-url.js'))
-const usageUtil = require('./utils/usage.js')
-const npm = require('./npm.js')
-const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js')
const { URL } = require('url')
-const usage = usageUtil('repo', 'npm repo [<pkgname> [<pkgname> ...]]')
-
-const cmd = (args, cb) => repo(args).then(() => cb()).catch(cb)
+const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js')
+const openUrl = require('./utils/open-url.js')
+const usageUtil = require('./utils/usage.js')
-const repo = async args => {
- if (!args || !args.length)
- args = ['.']
+class Repo {
+ constructor (npm) {
+ this.npm = npm
+ }
- await Promise.all(args.map(pkg => getRepo(pkg)))
-}
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('repo', 'npm repo [<pkgname> [<pkgname> ...]]')
+ }
-const getRepo = async pkg => {
- const opts = { ...npm.flatOptions, fullMetadata: true }
- const mani = await pacote.manifest(pkg, opts)
+ exec (args, cb) {
+ this.repo(args).then(() => cb()).catch(cb)
+ }
- const r = mani.repository
- const rurl = !r ? null
- : typeof r === 'string' ? r
- : typeof r === 'object' && typeof r.url === 'string' ? r.url
- : null
+ async repo (args) {
+ if (!args || !args.length)
+ args = ['.']
- if (!rurl) {
- throw Object.assign(new Error('no repository'), {
- pkgid: pkg,
- })
+ await Promise.all(args.map(pkg => this.get(pkg)))
}
- const info = hostedFromMani(mani)
- const url = info ?
- info.browse(mani.repository.directory) : unknownHostedUrl(rurl)
+ async get (pkg) {
+ const opts = { ...this.npm.flatOptions, fullMetadata: true }
+ const mani = await pacote.manifest(pkg, opts)
- if (!url) {
- throw Object.assign(new Error('no repository: could not get url'), {
- pkgid: pkg,
- })
- }
+ const r = mani.repository
+ const rurl = !r ? null
+ : typeof r === 'string' ? r
+ : typeof r === 'object' && typeof r.url === 'string' ? r.url
+ : null
+
+ if (!rurl) {
+ throw Object.assign(new Error('no repository'), {
+ pkgid: pkg,
+ })
+ }
+
+ const info = hostedFromMani(mani)
+ const url = info ?
+ info.browse(mani.repository.directory) : unknownHostedUrl(rurl)
- log.silly('docs', 'url', url)
- await openUrl(url, `${mani.name} repo available at the following URL`)
+ if (!url) {
+ throw Object.assign(new Error('no repository: could not get url'), {
+ pkgid: pkg,
+ })
+ }
+
+ log.silly('docs', 'url', url)
+ await openUrl(this.npm, url, `${mani.name} repo available at the following URL`)
+ }
}
+module.exports = Repo
const unknownHostedUrl = url => {
try {
@@ -67,5 +78,3 @@ const unknownHostedUrl = url => {
return null
}
}
-
-module.exports = Object.assign(cmd, { usage })
diff --git a/deps/npm/lib/restart.js b/deps/npm/lib/restart.js
index 1462cf6051..d5a7789ca9 100644
--- a/deps/npm/lib/restart.js
+++ b/deps/npm/lib/restart.js
@@ -1,2 +1,9 @@
-const npm = require('./npm.js')
-module.exports = require('./utils/lifecycle-cmd.js')(npm, 'restart')
+const LifecycleCmd = require('./utils/lifecycle-cmd.js')
+
+// This ends up calling run-script(['restart', ...args])
+class Restart extends LifecycleCmd {
+ constructor (npm) {
+ super(npm, 'restart')
+ }
+}
+module.exports = Restart
diff --git a/deps/npm/lib/root.js b/deps/npm/lib/root.js
index 631aef8386..8e5ac63d7b 100644
--- a/deps/npm/lib/root.js
+++ b/deps/npm/lib/root.js
@@ -1,7 +1,22 @@
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
-const cmd = (args, cb) => root(args).then(() => cb()).catch(cb)
-const usage = usageUtil('root', 'npm root [-g]')
-const root = async (args, cb) => output(npm.dir)
-module.exports = Object.assign(cmd, { usage })
+
+class Root {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('root', 'npm root [-g]')
+ }
+
+ exec (args, cb) {
+ this.root(args).then(() => cb()).catch(cb)
+ }
+
+ async root () {
+ output(this.npm.dir)
+ }
+}
+module.exports = Root
diff --git a/deps/npm/lib/run-script.js b/deps/npm/lib/run-script.js
index 4dfb854cad..cdfd88f10f 100644
--- a/deps/npm/lib/run-script.js
+++ b/deps/npm/lib/run-script.js
@@ -1,144 +1,157 @@
const runScript = require('@npmcli/run-script')
const { isServerPackage } = runScript
-const npm = require('./npm.js')
const readJson = require('read-package-json-fast')
const { resolve } = require('path')
const output = require('./utils/output.js')
const log = require('npmlog')
-const usageUtil = require('./utils/usage')
-const didYouMean = require('./utils/did-you-mean')
+const usageUtil = require('./utils/usage.js')
+const didYouMean = require('./utils/did-you-mean.js')
const isWindowsShell = require('./utils/is-windows-shell.js')
-const usage = usageUtil(
- 'run-script',
- 'npm run-script <command> [-- <args>]'
-)
-
-const completion = async (opts) => {
- const argv = opts.conf.argv.remain
- if (argv.length === 2) {
- // find the script name
- const json = resolve(npm.localPrefix, 'package.json')
- const { scripts = {} } = await readJson(json).catch(er => ({}))
- return Object.keys(scripts)
+const cmdList = [
+ 'publish',
+ 'install',
+ 'uninstall',
+ 'test',
+ 'stop',
+ 'start',
+ 'restart',
+ 'version',
+].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]), [])
+
+class RunScript {
+ constructor (npm) {
+ this.npm = npm
}
-}
-const cmd = (args, cb) => {
- const fn = args.length ? doRun : list
- return fn(args).then(() => cb()).catch(cb)
-}
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'run-script',
+ 'npm run-script <command> [-- <args>]'
+ )
+ }
-const doRun = async (args) => {
- const path = npm.localPrefix
- const event = args.shift()
- const { scriptShell } = npm.flatOptions
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ if (argv.length === 2) {
+ // find the script name
+ const json = resolve(this.npm.localPrefix, 'package.json')
+ const { scripts = {} } = await readJson(json).catch(er => ({}))
+ return Object.keys(scripts)
+ }
+ }
- const pkg = await readJson(`${path}/package.json`)
- const { scripts = {} } = pkg
+ exec (args, cb) {
+ if (args.length)
+ this.run(args).then(() => cb()).catch(cb)
+ else
+ this.list(args).then(() => cb()).catch(cb)
+ }
- if (event === 'restart' && !scripts.restart)
- scripts.restart = 'npm stop --if-present && npm start'
- else if (event === 'env' && !scripts.env)
- scripts.env = isWindowsShell ? 'SET' : 'env'
+ async run (args) {
+ const path = this.npm.localPrefix
+ const event = args.shift()
+ const { scriptShell } = this.npm.flatOptions
- pkg.scripts = scripts
+ const pkg = await readJson(`${path}/package.json`)
+ const { scripts = {} } = pkg
- if (!Object.prototype.hasOwnProperty.call(scripts, event) && !(event === 'start' && await isServerPackage(path))) {
- if (npm.config.get('if-present'))
- return
+ if (event === 'restart' && !scripts.restart)
+ scripts.restart = 'npm stop --if-present && npm start'
+ else if (event === 'env' && !scripts.env)
+ scripts.env = isWindowsShell ? 'SET' : 'env'
- const suggestions = didYouMean(event, Object.keys(scripts))
- throw new Error(`missing script: ${event}${
- suggestions ? `\n${suggestions}` : ''}`)
- }
+ pkg.scripts = scripts
- // positional args only added to the main event, not pre/post
- const events = [[event, args]]
- if (!npm.flatOptions.ignoreScripts) {
- if (scripts[`pre${event}`])
- events.unshift([`pre${event}`, []])
+ if (
+ !Object.prototype.hasOwnProperty.call(scripts, event) &&
+ !(event === 'start' && await isServerPackage(path))
+ ) {
+ if (this.npm.config.get('if-present'))
+ return
- if (scripts[`post${event}`])
- events.push([`post${event}`, []])
- }
+ const suggestions = didYouMean(event, Object.keys(scripts))
+ throw new Error(`missing script: ${event}${
+ suggestions ? `\n${suggestions}` : ''}`)
+ }
- const opts = {
- path,
- args,
- scriptShell,
- stdio: 'inherit',
- stdioString: true,
- pkg,
- banner: log.level !== 'silent',
- }
+ // positional args only added to the main event, not pre/post
+ const events = [[event, args]]
+ if (!this.npm.flatOptions.ignoreScripts) {
+ if (scripts[`pre${event}`])
+ events.unshift([`pre${event}`, []])
+
+ if (scripts[`post${event}`])
+ events.push([`post${event}`, []])
+ }
- for (const [event, args] of events) {
- await runScript({
- ...opts,
- event,
+ const opts = {
+ path,
args,
- })
+ scriptShell,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg,
+ banner: log.level !== 'silent',
+ }
+
+ for (const [event, args] of events) {
+ await runScript({
+ ...opts,
+ event,
+ args,
+ })
+ }
}
-}
-const list = async () => {
- const path = npm.localPrefix
- const { scripts, name } = await readJson(`${path}/package.json`)
- const cmdList = [
- 'publish',
- 'install',
- 'uninstall',
- 'test',
- 'stop',
- 'start',
- 'restart',
- 'version',
- ].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]), [])
-
- if (!scripts)
- return []
-
- const allScripts = Object.keys(scripts)
- if (log.level === 'silent')
- return allScripts
+ async list () {
+ const path = this.npm.localPrefix
+ const { scripts, name } = await readJson(`${path}/package.json`)
- if (npm.flatOptions.json) {
- output(JSON.stringify(scripts, null, 2))
- return allScripts
- }
+ if (!scripts)
+ return []
- if (npm.flatOptions.parseable) {
- for (const [script, cmd] of Object.entries(scripts))
- output(`${script}:${cmd}`)
+ const allScripts = Object.keys(scripts)
+ if (log.level === 'silent')
+ return allScripts
- return allScripts
- }
+ if (this.npm.flatOptions.json) {
+ output(JSON.stringify(scripts, null, 2))
+ return allScripts
+ }
- const indent = '\n '
- const prefix = ' '
- const cmds = []
- const runScripts = []
- for (const script of allScripts) {
- const list = cmdList.includes(script) ? cmds : runScripts
- list.push(script)
- }
+ if (this.npm.flatOptions.parseable) {
+ for (const [script, cmd] of Object.entries(scripts))
+ output(`${script}:${cmd}`)
- if (cmds.length)
- output(`Lifecycle scripts included in ${name}:`)
+ return allScripts
+ }
- for (const script of cmds)
- output(prefix + script + indent + scripts[script])
+ const indent = '\n '
+ const prefix = ' '
+ const cmds = []
+ const runScripts = []
+ for (const script of allScripts) {
+ const list = cmdList.includes(script) ? cmds : runScripts
+ list.push(script)
+ }
- if (!cmds.length && runScripts.length)
- output(`Scripts available in ${name} via \`npm run-script\`:`)
- else if (runScripts.length)
- output('\navailable via `npm run-script`:')
+ if (cmds.length)
+ output(`Lifecycle scripts included in ${name}:`)
- for (const script of runScripts)
- output(prefix + script + indent + scripts[script])
+ for (const script of cmds)
+ output(prefix + script + indent + scripts[script])
- return allScripts
-}
+ if (!cmds.length && runScripts.length)
+ output(`Scripts available in ${name} via \`npm run-script\`:`)
+ else if (runScripts.length)
+ output('\navailable via `npm run-script`:')
+
+ for (const script of runScripts)
+ output(prefix + script + indent + scripts[script])
-module.exports = Object.assign(cmd, { completion, usage })
+ return allScripts
+ }
+}
+module.exports = RunScript
diff --git a/deps/npm/lib/search.js b/deps/npm/lib/search.js
index 3f8fd99fb8..e0922b9846 100644
--- a/deps/npm/lib/search.js
+++ b/deps/npm/lib/search.js
@@ -5,69 +5,10 @@ const log = require('npmlog')
const formatPackageStream = require('./search/format-package-stream.js')
const packageFilter = require('./search/package-filter.js')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil(
- 'search',
- 'npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms ...]'
-)
-
-const cmd = (args, cb) => search(args).then(() => cb()).catch(cb)
-
-const search = async (args) => {
- const opts = {
- ...npm.flatOptions,
- ...npm.flatOptions.search,
- include: prepareIncludes(args, npm.flatOptions.search.opts),
- exclude: prepareExcludes(npm.flatOptions.search.exclude),
- }
-
- if (opts.include.length === 0)
- throw new Error('search must be called with arguments')
-
- // Used later to figure out whether we had any packages go out
- let anyOutput = false
-
- class FilterStream extends Minipass {
- write (pkg) {
- if (packageFilter(pkg, opts.include, opts.exclude))
- super.write(pkg)
- }
- }
-
- const filterStream = new FilterStream()
-
- // Grab a configured output stream that will spit out packages in the
- // desired format.
- const outputStream = formatPackageStream({
- args, // --searchinclude options are not highlighted
- ...opts,
- })
-
- log.silly('search', 'searching packages')
- const p = new Pipeline(
- libSearch.stream(opts.include, opts),
- filterStream,
- outputStream
- )
-
- p.on('data', chunk => {
- if (!anyOutput)
- anyOutput = true
- output(chunk.toString('utf8'))
- })
-
- await p.promise()
- if (!anyOutput && !opts.json && !opts.parseable)
- output('No matches found for ' + (args.map(JSON.stringify).join(' ')))
-
- log.silly('search', 'search completed')
- log.clearProgress()
-}
-
-function prepareIncludes (args, searchopts) {
+function prepareIncludes (args) {
return args
.map(s => s.toLowerCase())
.filter(s => s)
@@ -85,4 +26,72 @@ function prepareExcludes (searchexclude) {
.filter(s => s)
}
-module.exports = Object.assign(cmd, { usage })
+class Search {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'search',
+ 'npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms ...]'
+ )
+ }
+
+ exec (args, cb) {
+ this.search(args).then(() => cb()).catch(cb)
+ }
+
+ async search (args) {
+ const opts = {
+ ...this.npm.flatOptions,
+ ...this.npm.flatOptions.search,
+ include: prepareIncludes(args),
+ exclude: prepareExcludes(this.npm.flatOptions.search.exclude),
+ }
+
+ if (opts.include.length === 0)
+ throw new Error('search must be called with arguments')
+
+ // Used later to figure out whether we had any packages go out
+ let anyOutput = false
+
+ class FilterStream extends Minipass {
+ write (pkg) {
+ if (packageFilter(pkg, opts.include, opts.exclude))
+ super.write(pkg)
+ }
+ }
+
+ const filterStream = new FilterStream()
+
+ // Grab a configured output stream that will spit out packages in the
+ // desired format.
+ const outputStream = formatPackageStream({
+ args, // --searchinclude options are not highlighted
+ ...opts,
+ })
+
+ log.silly('search', 'searching packages')
+ const p = new Pipeline(
+ libSearch.stream(opts.include, opts),
+ filterStream,
+ outputStream
+ )
+
+ p.on('data', chunk => {
+ if (!anyOutput)
+ anyOutput = true
+ output(chunk.toString('utf8'))
+ })
+
+ await p.promise()
+ if (!anyOutput && !opts.json && !opts.parseable)
+ output('No matches found for ' + (args.map(JSON.stringify).join(' ')))
+
+ log.silly('search', 'search completed')
+ log.clearProgress()
+ }
+}
+module.exports = Search
diff --git a/deps/npm/lib/set-script.js b/deps/npm/lib/set-script.js
index 7bac6eca50..25545898e1 100644
--- a/deps/npm/lib/set-script.js
+++ b/deps/npm/lib/set-script.js
@@ -1,52 +1,62 @@
const log = require('npmlog')
const usageUtil = require('./utils/usage.js')
-const { localPrefix } = require('./npm.js')
const fs = require('fs')
-const usage = usageUtil('set-script', 'npm set-script [<script>] [<command>]')
const parseJSON = require('json-parse-even-better-errors')
const rpj = require('read-package-json-fast')
-const cmd = (args, cb) => set(args).then(() => cb()).catch(cb)
-
-const set = async function (args) {
- if (process.env.npm_lifecycle_event === 'postinstall')
- throw new Error('Scripts can’t set from the postinstall script')
+class SetScript {
+ constructor (npm) {
+ this.npm = npm
+ }
- // Parse arguments
- if (args.length !== 2)
- throw new Error(`Expected 2 arguments: got ${args.length}`)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('set-script', 'npm set-script [<script>] [<command>]')
+ }
- // Set the script
- let manifest
- let warn = false
- try {
- manifest = fs.readFileSync(localPrefix + '/package.json', 'utf-8')
- } catch (error) {
- throw new Error('package.json not found')
+ exec (args, cb) {
+ this.set(args).then(() => cb()).catch(cb)
}
- try {
- manifest = parseJSON(manifest)
- } catch (error) {
- throw new Error(`Invalid package.json: ${error}`)
+
+ async set (args) {
+ if (process.env.npm_lifecycle_event === 'postinstall')
+ throw new Error('Scripts can’t set from the postinstall script')
+
+ // Parse arguments
+ if (args.length !== 2)
+ throw new Error(`Expected 2 arguments: got ${args.length}`)
+
+ // Set the script
+ let manifest
+ let warn = false
+ try {
+ manifest = fs.readFileSync(this.npm.localPrefix + '/package.json', 'utf-8')
+ } catch (error) {
+ throw new Error('package.json not found')
+ }
+ try {
+ manifest = parseJSON(manifest)
+ } catch (error) {
+ throw new Error(`Invalid package.json: ${error}`)
+ }
+ if (!manifest.scripts)
+ manifest.scripts = {}
+ if (manifest.scripts[args[0]] && manifest.scripts[args[0]] !== args[1])
+ warn = true
+ manifest.scripts[args[0]] = args[1]
+ // format content
+ const packageJsonInfo = await rpj(this.npm.localPrefix + '/package.json')
+ const {
+ [Symbol.for('indent')]: indent,
+ [Symbol.for('newline')]: newline,
+ } = packageJsonInfo
+ const format = indent === undefined ? ' ' : indent
+ const eol = newline === undefined ? '\n' : newline
+ const content = (JSON.stringify(manifest, null, format) + '\n')
+ .replace(/\n/g, eol)
+ fs.writeFileSync(this.npm.localPrefix + '/package.json', content)
+ if (warn)
+ log.warn('set-script', `Script "${args[0]}" was overwritten`)
}
- if (!manifest.scripts)
- manifest.scripts = {}
- if (manifest.scripts[args[0]] && manifest.scripts[args[0]] !== args[1])
- warn = true
- manifest.scripts[args[0]] = args[1]
- // format content
- const packageJsonInfo = await rpj(localPrefix + '/package.json')
- const {
- [Symbol.for('indent')]: indent,
- [Symbol.for('newline')]: newline,
- } = packageJsonInfo
- const format = indent === undefined ? ' ' : indent
- const eol = newline === undefined ? '\n' : newline
- const content = (JSON.stringify(manifest, null, format) + '\n')
- .replace(/\n/g, eol)
- fs.writeFileSync(localPrefix + '/package.json', content)
- if (warn)
- log.warn('set-script', `Script "${args[0]}" was overwritten`)
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = SetScript
diff --git a/deps/npm/lib/set.js b/deps/npm/lib/set.js
index 3d61c1e93e..cbce1547e8 100644
--- a/deps/npm/lib/set.js
+++ b/deps/npm/lib/set.js
@@ -1,14 +1,26 @@
-const npm = require('./npm.js')
-const config = require('./config.js')
+const usageUtil = require('./utils/usage.js')
-const usage = 'npm set <key>=<value> [<key>=<value> ...] (See `npm config`)'
+class Set {
+ constructor (npm) {
+ this.npm = npm
+ }
-const completion = config.completion
+ get usage () {
+ return usageUtil(
+ 'set',
+ 'npm set <key>=<value> [<key>=<value> ...] (See `npm config`)'
+ )
+ }
-const cmd = (args, cb) => {
- if (!args.length)
- return cb(usage)
- npm.commands.config(['set'].concat(args), cb)
-}
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return this.npm.commands.config.completion(opts)
+ }
-module.exports = Object.assign(cmd, { usage, completion })
+ exec (args, cb) {
+ if (!args.length)
+ return cb(this.usage)
+ this.npm.commands.config(['set'].concat(args), cb)
+ }
+}
+module.exports = Set
diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js
index 8768f35b5f..a7516131d2 100644
--- a/deps/npm/lib/shrinkwrap.js
+++ b/deps/npm/lib/shrinkwrap.js
@@ -5,48 +5,58 @@ const { unlink } = fs.promises || { unlink: util.promisify(fs.unlink) }
const Arborist = require('@npmcli/arborist')
const log = require('npmlog')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('shrinkwrap', 'npm shrinkwrap')
-
-const cmd = (args, cb) => shrinkwrap().then(() => cb()).catch(cb)
-
-const shrinkwrap = async () => {
- // if has a npm-shrinkwrap.json, nothing to do
- // if has a package-lock.json, rename to npm-shrinkwrap.json
- // if has neither, load the actual tree and save that as npm-shrinkwrap.json
- // in all cases, re-cast to current lockfile version
- //
- // loadVirtual, fall back to loadActual
- // rename shrinkwrap file type, and tree.meta.save()
- if (npm.flatOptions.global) {
- const er = new Error('`npm shrinkwrap` does not work for global packages')
- er.code = 'ESHRINKWRAPGLOBAL'
- throw er
+
+class Shrinkwrap {
+ constructor (npm) {
+ this.npm = npm
}
- const path = npm.prefix
- const sw = resolve(path, 'npm-shrinkwrap.json')
- const arb = new Arborist({ ...npm.flatOptions, path })
- const tree = await arb.loadVirtual().catch(() => arb.loadActual())
- const { meta } = tree
- const newFile = meta.hiddenLockfile || !meta.loadedFromDisk
- const oldFilename = meta.filename
- const notSW = !newFile && basename(oldFilename) !== 'npm-shrinkwrap.json'
-
- meta.hiddenLockfile = false
- meta.filename = sw
- await meta.save()
-
- if (newFile)
- log.notice('', 'created a lockfile as npm-shrinkwrap.json')
- else if (notSW) {
- await unlink(oldFilename)
- log.notice('', 'package-lock.json has been renamed to npm-shrinkwrap.json')
- } else if (meta.originalLockfileVersion !== npm.lockfileVersion)
- log.notice('', `npm-shrinkwrap.json updated to version ${npm.lockfileVersion}`)
- else
- log.notice('', 'npm-shrinkwrap.json up to date')
-}
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('shrinkwrap', 'npm shrinkwrap')
+ }
+
+ exec (args, cb) {
+ this.shrinkwrap().then(() => cb()).catch(cb)
+ }
-module.exports = Object.assign(cmd, { usage })
+ async shrinkwrap () {
+ // if has a npm-shrinkwrap.json, nothing to do
+ // if has a package-lock.json, rename to npm-shrinkwrap.json
+ // if has neither, load the actual tree and save that as npm-shrinkwrap.json
+ // in all cases, re-cast to current lockfile version
+ //
+ // loadVirtual, fall back to loadActual
+ // rename shrinkwrap file type, and tree.meta.save()
+ if (this.npm.flatOptions.global) {
+ const er = new Error('`npm shrinkwrap` does not work for global packages')
+ er.code = 'ESHRINKWRAPGLOBAL'
+ throw er
+ }
+
+ const path = this.npm.prefix
+ const sw = resolve(path, 'npm-shrinkwrap.json')
+ const arb = new Arborist({ ...this.npm.flatOptions, path })
+ const tree = await arb.loadVirtual().catch(() => arb.loadActual())
+ const { meta } = tree
+ const newFile = meta.hiddenLockfile || !meta.loadedFromDisk
+ const oldFilename = meta.filename
+ const notSW = !newFile && basename(oldFilename) !== 'npm-shrinkwrap.json'
+
+ meta.hiddenLockfile = false
+ meta.filename = sw
+ await meta.save()
+
+ if (newFile)
+ log.notice('', 'created a lockfile as npm-shrinkwrap.json')
+ else if (notSW) {
+ await unlink(oldFilename)
+ log.notice('', 'package-lock.json has been renamed to npm-shrinkwrap.json')
+ } else if (meta.originalLockfileVersion !== this.npm.lockfileVersion)
+ log.notice('', `npm-shrinkwrap.json updated to version ${this.npm.lockfileVersion}`)
+ else
+ log.notice('', 'npm-shrinkwrap.json up to date')
+ }
+}
+module.exports = Shrinkwrap
diff --git a/deps/npm/lib/star.js b/deps/npm/lib/star.js
index c02fdeed89..b39d23b2c1 100644
--- a/deps/npm/lib/star.js
+++ b/deps/npm/lib/star.js
@@ -2,73 +2,81 @@ const fetch = require('npm-registry-fetch')
const log = require('npmlog')
const npa = require('npm-package-arg')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
const getIdentity = require('./utils/get-identity')
-const usage = usageUtil(
- 'star',
- 'npm star [<pkg>...]\n' +
- 'npm unstar [<pkg>...]'
-)
+class Star {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => star(args).then(() => cb()).catch(cb)
+ get usage () {
+ return usageUtil(
+ 'star',
+ 'npm star [<pkg>...]\n' +
+ 'npm unstar [<pkg>...]'
+ )
+ }
-const star = async args => {
- if (!args.length)
- throw new Error(usage)
+ exec (args, cb) {
+ this.star(args).then(() => cb()).catch(cb)
+ }
- // if we're unstarring, then show an empty star image
- // otherwise, show the full star image
- const { unicode } = npm.flatOptions
- const unstar = npm.config.get('star.unstar')
- const full = unicode ? '\u2605 ' : '(*)'
- const empty = unicode ? '\u2606 ' : '( )'
- const show = unstar ? empty : full
+ async star (args) {
+ if (!args.length)
+ throw new Error(this.usage)
- const pkgs = args.map(npa)
- for (const pkg of pkgs) {
- const [username, fullData] = await Promise.all([
- getIdentity(npm.flatOptions),
- fetch.json(pkg.escapedName, {
- ...npm.flatOptions,
- spec: pkg,
- query: { write: true },
- preferOnline: true,
- }),
- ])
+ // if we're unstarring, then show an empty star image
+ // otherwise, show the full star image
+ const { unicode } = this.npm.flatOptions
+ const unstar = this.npm.config.get('star.unstar')
+ const full = unicode ? '\u2605 ' : '(*)'
+ const empty = unicode ? '\u2606 ' : '( )'
+ const show = unstar ? empty : full
- if (!username)
- throw new Error('You need to be logged in!')
+ const pkgs = args.map(npa)
+ for (const pkg of pkgs) {
+ const [username, fullData] = await Promise.all([
+ getIdentity(this.npm, this.npm.flatOptions),
+ fetch.json(pkg.escapedName, {
+ ...this.npm.flatOptions,
+ spec: pkg,
+ query: { write: true },
+ preferOnline: true,
+ }),
+ ])
- const body = {
- _id: fullData._id,
- _rev: fullData._rev,
- users: fullData.users || {},
- }
+ if (!username)
+ throw new Error('You need to be logged in!')
- if (!unstar) {
- log.info('star', 'starring', body._id)
- body.users[username] = true
- log.verbose('star', 'starring', body)
- } else {
- delete body.users[username]
- log.info('unstar', 'unstarring', body._id)
- log.verbose('unstar', 'unstarring', body)
- }
+ const body = {
+ _id: fullData._id,
+ _rev: fullData._rev,
+ users: fullData.users || {},
+ }
+
+ if (!unstar) {
+ log.info('star', 'starring', body._id)
+ body.users[username] = true
+ log.verbose('star', 'starring', body)
+ } else {
+ delete body.users[username]
+ log.info('unstar', 'unstarring', body._id)
+ log.verbose('unstar', 'unstarring', body)
+ }
- const data = await fetch.json(pkg.escapedName, {
- ...npm.flatOptions,
- spec: pkg,
- method: 'PUT',
- body,
- })
+ const data = await fetch.json(pkg.escapedName, {
+ ...this.npm.flatOptions,
+ spec: pkg,
+ method: 'PUT',
+ body,
+ })
- output(show + ' ' + pkg.name)
- log.verbose('star', data)
- return data
+ output(show + ' ' + pkg.name)
+ log.verbose('star', data)
+ return data
+ }
}
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Star
diff --git a/deps/npm/lib/stars.js b/deps/npm/lib/stars.js
index d1c2b73fb1..fe280705b4 100644
--- a/deps/npm/lib/stars.js
+++ b/deps/npm/lib/stars.js
@@ -1,35 +1,42 @@
const log = require('npmlog')
const fetch = require('npm-registry-fetch')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const getIdentity = require('./utils/get-identity.js')
const usageUtil = require('./utils/usage.js')
-const usage = usageUtil('stars', 'npm stars [<user>]')
-
-const cmd = (args, cb) => stars(args).then(() => cb()).catch(cb)
-
-const stars = (args) => {
- return stars_(args).catch(er => {
- if (er.code === 'ENEEDAUTH')
- log.warn('stars', 'auth is required to look up your username')
-
- throw er
- })
-}
-
-const stars_ = async ([user = getIdentity(npm.flatOptions)]) => {
- const { rows } = await fetch.json('/-/_view/starredByUser', {
- ...npm.flatOptions,
- query: { key: `"${await user}"` },
- })
-
- if (rows.length === 0)
- log.warn('stars', 'user has not starred any packages')
-
- for (const row of rows)
- output(row.value)
+class Stars {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('stars', 'npm stars [<user>]')
+ }
+
+ exec (args, cb) {
+ this.stars(args).then(() => cb()).catch(er => {
+ if (er.code === 'ENEEDAUTH')
+ log.warn('stars', 'auth is required to look up your username')
+ cb(er)
+ })
+ }
+
+ async stars ([user]) {
+ if (!user)
+ user = await getIdentity(this.npm, this.npm.flatOptions)
+
+ const { rows } = await fetch.json('/-/_view/starredByUser', {
+ ...this.npm.flatOptions,
+ query: { key: `"${user}"` },
+ })
+
+ if (rows.length === 0)
+ log.warn('stars', 'user has not starred any packages')
+
+ for (const row of rows)
+ output(row.value)
+ }
}
-
-module.exports = Object.assign(cmd, { usage })
+module.exports = Stars
diff --git a/deps/npm/lib/start.js b/deps/npm/lib/start.js
index 9fa076d5e3..3d46a3a7ba 100644
--- a/deps/npm/lib/start.js
+++ b/deps/npm/lib/start.js
@@ -1,2 +1,9 @@
-const npm = require('./npm.js')
-module.exports = require('./utils/lifecycle-cmd.js')(npm, 'start')
+const LifecycleCmd = require('./utils/lifecycle-cmd.js')
+
+// This ends up calling run-script(['start', ...args])
+class Start extends LifecycleCmd {
+ constructor (npm) {
+ super(npm, 'start')
+ }
+}
+module.exports = Start
diff --git a/deps/npm/lib/stop.js b/deps/npm/lib/stop.js
index 827d414d13..d7df5887e7 100644
--- a/deps/npm/lib/stop.js
+++ b/deps/npm/lib/stop.js
@@ -1,2 +1,9 @@
-const npm = require('./npm.js')
-module.exports = require('./utils/lifecycle-cmd.js')(npm, 'stop')
+const LifecycleCmd = require('./utils/lifecycle-cmd.js')
+
+// This ends up calling run-script(['stop', ...args])
+class Stop extends LifecycleCmd {
+ constructor (npm) {
+ super(npm, 'stop')
+ }
+}
+module.exports = Stop
diff --git a/deps/npm/lib/team.js b/deps/npm/lib/team.js
index 24e8360b64..4947739a04 100644
--- a/deps/npm/lib/team.js
+++ b/deps/npm/lib/team.js
@@ -1,146 +1,148 @@
const columns = require('cli-columns')
const libteam = require('libnpmteam')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
-const usageUtil = require('./utils/usage')
+const usageUtil = require('./utils/usage.js')
-const subcommands = ['create', 'destroy', 'add', 'rm', 'ls']
+class Team {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ get usage () {
+ return usageUtil(
+ 'team',
+ 'npm team create <scope:team> [--otp <otpcode>]\n' +
+ 'npm team destroy <scope:team> [--otp <otpcode>]\n' +
+ 'npm team add <scope:team> <user> [--otp <otpcode>]\n' +
+ 'npm team rm <scope:team> <user> [--otp <otpcode>]\n' +
+ 'npm team ls <scope>|<scope:team>\n'
+ )
+ }
-const usage = usageUtil(
- 'team',
- 'npm team create <scope:team> [--otp <otpcode>]\n' +
- 'npm team destroy <scope:team> [--otp <otpcode>]\n' +
- 'npm team add <scope:team> <user> [--otp <otpcode>]\n' +
- 'npm team rm <scope:team> <user> [--otp <otpcode>]\n' +
- 'npm team ls <scope>|<scope:team>\n'
-)
+ async completion (opts) {
+ const { conf: { argv: { remain: argv } } } = opts
+ const subcommands = ['create', 'destroy', 'add', 'rm', 'ls']
-const completion = async (opts) => {
- const { conf: { argv: { remain: argv } } } = opts
- if (argv.length === 2)
- return subcommands
+ if (argv.length === 2)
+ return subcommands
- switch (argv[2]) {
- case 'ls':
- case 'create':
- case 'destroy':
- case 'add':
- case 'rm':
+ if (subcommands.includes(argv[2]))
return []
- default:
- throw new Error(argv[2] + ' not recognized')
+
+ throw new Error(argv[2] + ' not recognized')
}
-}
-const cmd = (args, cb) => team(args).then(() => cb()).catch(cb)
+ exec (args, cb) {
+ this.team(args).then(() => cb()).catch(cb)
+ }
-const team = async ([cmd, entity = '', user = '']) => {
- // Entities are in the format <scope>:<team>
- // XXX: "description" option to libnpmteam is used as a description of the
- // team, but in npm's options, this is a boolean meaning "show the
- // description in npm search output". Hence its being set to null here.
- await otplease(npm.flatOptions, opts => {
- entity = entity.replace(/^@/, '')
- switch (cmd) {
- case 'create': return teamCreate(entity, opts)
- case 'destroy': return teamDestroy(entity, opts)
- case 'add': return teamAdd(entity, user, opts)
- case 'rm': return teamRm(entity, user, opts)
- case 'ls': {
- const match = entity.match(/[^:]+:.+/)
- if (match)
- return teamListUsers(entity, opts)
- else
- return teamListTeams(entity, opts)
+ async team ([cmd, entity = '', user = '']) {
+ // Entities are in the format <scope>:<team>
+ // XXX: "description" option to libnpmteam is used as a description of the
+ // team, but in npm's options, this is a boolean meaning "show the
+ // description in npm search output". Hence its being set to null here.
+ await otplease(this.npm.flatOptions, opts => {
+ entity = entity.replace(/^@/, '')
+ switch (cmd) {
+ case 'create': return this.create(entity, opts)
+ case 'destroy': return this.destroy(entity, opts)
+ case 'add': return this.add(entity, user, opts)
+ case 'rm': return this.rm(entity, user, opts)
+ case 'ls': {
+ const match = entity.match(/[^:]+:.+/)
+ if (match)
+ return this.listUsers(entity, opts)
+ else
+ return this.listTeams(entity, opts)
+ }
+ default:
+ throw this.usage
}
- default:
- throw usage
- }
- })
-}
+ })
+ }
-const teamCreate = async (entity, opts) => {
- await libteam.create(entity, opts)
- if (opts.json) {
- output(JSON.stringify({
- created: true,
- team: entity,
- }))
- } else if (opts.parseable)
- output(`${entity}\tcreated`)
- else if (!opts.silent && opts.loglevel !== 'silent')
- output(`+@${entity}`)
-}
+ async create (entity, opts) {
+ await libteam.create(entity, opts)
+ if (opts.json) {
+ output(JSON.stringify({
+ created: true,
+ team: entity,
+ }))
+ } else if (opts.parseable)
+ output(`${entity}\tcreated`)
+ else if (!opts.silent && opts.loglevel !== 'silent')
+ output(`+@${entity}`)
+ }
-const teamDestroy = async (entity, opts) => {
- await libteam.destroy(entity, opts)
- if (opts.json) {
- output(JSON.stringify({
- deleted: true,
- team: entity,
- }))
- } else if (opts.parseable)
- output(`${entity}\tdeleted`)
- else if (!opts.silent && opts.loglevel !== 'silent')
- output(`-@${entity}`)
-}
+ async destroy (entity, opts) {
+ await libteam.destroy(entity, opts)
+ if (opts.json) {
+ output(JSON.stringify({
+ deleted: true,
+ team: entity,
+ }))
+ } else if (opts.parseable)
+ output(`${entity}\tdeleted`)
+ else if (!opts.silent && opts.loglevel !== 'silent')
+ output(`-@${entity}`)
+ }
-const teamAdd = async (entity, user, opts) => {
- await libteam.add(user, entity, opts)
- if (opts.json) {
- output(JSON.stringify({
- added: true,
- team: entity,
- user,
- }))
- } else if (opts.parseable)
- output(`${user}\t${entity}\tadded`)
- else if (!opts.silent && opts.loglevel !== 'silent')
- output(`${user} added to @${entity}`)
-}
+ async add (entity, user, opts) {
+ await libteam.add(user, entity, opts)
+ if (opts.json) {
+ output(JSON.stringify({
+ added: true,
+ team: entity,
+ user,
+ }))
+ } else if (opts.parseable)
+ output(`${user}\t${entity}\tadded`)
+ else if (!opts.silent && opts.loglevel !== 'silent')
+ output(`${user} added to @${entity}`)
+ }
-const teamRm = async (entity, user, opts) => {
- await libteam.rm(user, entity, opts)
- if (opts.json) {
- output(JSON.stringify({
- removed: true,
- team: entity,
- user,
- }))
- } else if (opts.parseable)
- output(`${user}\t${entity}\tremoved`)
- else if (!opts.silent && opts.loglevel !== 'silent')
- output(`${user} removed from @${entity}`)
-}
+ async rm (entity, user, opts) {
+ await libteam.rm(user, entity, opts)
+ if (opts.json) {
+ output(JSON.stringify({
+ removed: true,
+ team: entity,
+ user,
+ }))
+ } else if (opts.parseable)
+ output(`${user}\t${entity}\tremoved`)
+ else if (!opts.silent && opts.loglevel !== 'silent')
+ output(`${user} removed from @${entity}`)
+ }
-const teamListUsers = async (entity, opts) => {
- const users = (await libteam.lsUsers(entity, opts)).sort()
- if (opts.json)
- output(JSON.stringify(users, null, 2))
- else if (opts.parseable)
- output(users.join('\n'))
- else if (!opts.silent && opts.loglevel !== 'silent') {
- const plural = users.length === 1 ? '' : 's'
- const more = users.length === 0 ? '' : ':\n'
- output(`\n@${entity} has ${users.length} user${plural}${more}`)
- output(columns(users, { padding: 1 }))
+ async listUsers (entity, opts) {
+ const users = (await libteam.lsUsers(entity, opts)).sort()
+ if (opts.json)
+ output(JSON.stringify(users, null, 2))
+ else if (opts.parseable)
+ output(users.join('\n'))
+ else if (!opts.silent && opts.loglevel !== 'silent') {
+ const plural = users.length === 1 ? '' : 's'
+ const more = users.length === 0 ? '' : ':\n'
+ output(`\n@${entity} has ${users.length} user${plural}${more}`)
+ output(columns(users, { padding: 1 }))
+ }
}
-}
-const teamListTeams = async (entity, opts) => {
- const teams = (await libteam.lsTeams(entity, opts)).sort()
- if (opts.json)
- output(JSON.stringify(teams, null, 2))
- else if (opts.parseable)
- output(teams.join('\n'))
- else if (!opts.silent && opts.loglevel !== 'silent') {
- const plural = teams.length === 1 ? '' : 's'
- const more = teams.length === 0 ? '' : ':\n'
- output(`\n@${entity} has ${teams.length} team${plural}${more}`)
- output(columns(teams.map(t => `@${t}`), { padding: 1 }))
+ async listTeams (entity, opts) {
+ const teams = (await libteam.lsTeams(entity, opts)).sort()
+ if (opts.json)
+ output(JSON.stringify(teams, null, 2))
+ else if (opts.parseable)
+ output(teams.join('\n'))
+ else if (!opts.silent && opts.loglevel !== 'silent') {
+ const plural = teams.length === 1 ? '' : 's'
+ const more = teams.length === 0 ? '' : ':\n'
+ output(`\n@${entity} has ${teams.length} team${plural}${more}`)
+ output(columns(teams.map(t => `@${t}`), { padding: 1 }))
+ }
}
}
-
-module.exports = Object.assign(cmd, { completion, usage })
+module.exports = Team
diff --git a/deps/npm/lib/test.js b/deps/npm/lib/test.js
index ea5914ea38..12292d2924 100644
--- a/deps/npm/lib/test.js
+++ b/deps/npm/lib/test.js
@@ -1,12 +1,19 @@
-const npm = require('./npm.js')
-const testCmd = require('./utils/lifecycle-cmd.js')(npm, 'test')
-const { completion, usage } = testCmd
-const cmd = (args, cb) => testCmd(args, er => {
- if (er && er.code === 'ELIFECYCLE') {
- /* eslint-disable standard/no-callback-literal */
- cb('Test failed. See above for more details.')
- } else
- cb(er)
-})
+const LifecycleCmd = require('./utils/lifecycle-cmd.js')
-module.exports = Object.assign(cmd, { completion, usage })
+// This ends up calling run-script(['test', ...args])
+class Test extends LifecycleCmd {
+ constructor (npm) {
+ super(npm, 'test')
+ }
+
+ exec (args, cb) {
+ super.exec(args, er => {
+ if (er && er.code === 'ELIFECYCLE') {
+ /* eslint-disable standard/no-callback-literal */
+ cb('Test failed. See above for more details.')
+ } else
+ cb(er)
+ })
+ }
+}
+module.exports = Test
diff --git a/deps/npm/lib/token.js b/deps/npm/lib/token.js
index 1e78584c77..ad6d5c6fcb 100644
--- a/deps/npm/lib/token.js
+++ b/deps/npm/lib/token.js
@@ -1,127 +1,68 @@
-const profile = require('npm-profile')
-const npm = require('./npm.js')
-const output = require('./utils/output.js')
-const otplease = require('./utils/otplease.js')
const Table = require('cli-table3')
-const isCidrV4 = require('is-cidr').v4
-const isCidrV6 = require('is-cidr').v6
-const readUserInfo = require('./utils/read-user-info.js')
const ansistyles = require('ansistyles')
+const { v4: isCidrV4, v6: isCidrV6 } = require('is-cidr')
const log = require('npmlog')
-const pulseTillDone = require('./utils/pulse-till-done.js')
+const profile = require('npm-profile')
-module.exports = token
+const otplease = require('./utils/otplease.js')
+const output = require('./utils/output.js')
+const pulseTillDone = require('./utils/pulse-till-done.js')
+const readUserInfo = require('./utils/read-user-info.js')
+const usageUtil = require('./utils/usage.js')
-token._validateCIDRList = validateCIDRList
+class Token {
+ constructor (npm) {
+ this.npm = npm
+ }
-const usageUtil = require('./utils/usage.js')
-token.usage = usageUtil('token',
- 'npm token list\n' +
- 'npm token revoke <id|token>\n' +
- 'npm token create [--read-only] [--cidr=list]')
-
-const UsageError = (msg) =>
- Object.assign(new Error(`\nUsage: ${msg}\n\n` + token.usage), {
- code: 'EUSAGE',
- })
-
-const InvalidCIDRError = (msg) =>
- Object.assign(new Error(msg), { code: 'EINVALIDCIDR' })
-
-token.subcommands = ['list', 'revoke', 'create']
-
-token.completion = async (opts) => {
- var argv = opts.conf.argv.remain
- if (argv.length === 2)
- return token.subcommands
-
- switch (argv[2]) {
- case 'list':
- case 'revoke':
- case 'create':
- return []
- default:
- throw new Error(argv[2] + ' not recognized')
+ get usage () {
+ return usageUtil('token',
+ 'npm token list\n' +
+ 'npm token revoke <id|token>\n' +
+ 'npm token create [--read-only] [--cidr=list]'
+ )
}
-}
-function withCb (prom, cb) {
- prom.then((value) => cb(null, value), cb)
-}
+ async completion (opts) {
+ const argv = opts.conf.argv.remain
+ const subcommands = ['list', 'revoke', 'create']
+ if (argv.length === 2)
+ return subcommands
+
+ if (subcommands.includes(argv[2]))
+ return []
-function token (args, cb) {
- log.gauge.show('token')
- if (args.length === 0)
- return withCb(list([]), cb)
- switch (args[0]) {
- case 'list':
- case 'ls':
- withCb(list(), cb)
- break
- case 'delete':
- case 'revoke':
- case 'remove':
- case 'rm':
- withCb(rm(args.slice(1)), cb)
- break
- case 'create':
- withCb(create(args.slice(1)), cb)
- break
- default:
- cb(UsageError(`${args[0]} is not a recognized subcommand.`))
+ throw new Error(argv[2] + ' not recognized')
}
-}
-function generateTokenIds (tokens, minLength) {
- const byId = {}
- for (const token of tokens) {
- token.id = token.key
- for (let ii = minLength; ii < token.key.length; ++ii) {
- const match = tokens.some(ot =>
- ot !== token &&
- ot.key.slice(0, ii) === token.key.slice(0, ii))
- if (!match) {
- token.id = token.key.slice(0, ii)
- break
- }
- }
- byId[token.id] = token
+ exec (args, cb) {
+ this.token(args).then(() => cb()).catch(cb)
}
- return byId
-}
-function config () {
- const conf = { ...npm.flatOptions }
- const creds = npm.config.getCredentialsByURI(conf.registry)
- if (creds.token)
- conf.auth = { token: creds.token }
- else if (creds.username) {
- conf.auth = {
- basic: {
- username: creds.username,
- password: creds.password,
- },
- }
- } else if (creds.auth) {
- const auth = Buffer.from(creds.auth, 'base64').toString().split(':', 2)
- conf.auth = {
- basic: {
- username: auth[0],
- password: auth[1],
- },
+ async token (args, cb) {
+ log.gauge.show('token')
+ if (args.length === 0)
+ return this.list()
+ switch (args[0]) {
+ case 'list':
+ case 'ls':
+ return this.list()
+ case 'delete':
+ case 'revoke':
+ case 'remove':
+ case 'rm':
+ return this.rm(args.slice(1))
+ case 'create':
+ return this.create(args.slice(1))
+ default:
+ throw this.usageError(`${args[0]} is not a recognized subcommand.`)
}
- } else
- conf.auth = {}
-
- if (conf.otp)
- conf.auth.otp = conf.otp
- return conf
-}
+ }
-function list (args) {
- const conf = config()
- log.info('token', 'getting list')
- return pulseTillDone.withPromise(profile.listTokens(conf)).then((tokens) => {
+ async list () {
+ const conf = this.config()
+ log.info('token', 'getting list')
+ const tokens = await pulseTillDone.withPromise(profile.listTokens(conf))
if (conf.json) {
output(JSON.stringify(tokens, null, 2))
return
@@ -138,7 +79,7 @@ function list (args) {
})
return
}
- generateTokenIds(tokens, 6)
+ this.generateTokenIds(tokens, 6)
const idWidth = tokens.reduce((acc, token) =>
Math.max(acc, token.id.length), 0)
const table = new Table({
@@ -155,18 +96,17 @@ function list (args) {
])
})
output(table.toString())
- })
-}
+ }
-function rm (args) {
- if (args.length === 0)
- return Promise.reject(UsageError('`<tokenKey>` argument is required.'))
+ async rm (args) {
+ if (args.length === 0)
+ throw this.usageError('`<tokenKey>` argument is required.')
- const conf = config()
- const toRemove = []
- const progress = log.newItem('removing tokens', toRemove.length)
- progress.info('token', 'getting existing list')
- return pulseTillDone.withPromise(profile.listTokens(conf).then((tokens) => {
+ const conf = this.config()
+ const toRemove = []
+ const progress = log.newItem('removing tokens', toRemove.length)
+ progress.info('token', 'getting existing list')
+ const tokens = await pulseTillDone.withPromise(profile.listTokens(conf))
args.forEach((id) => {
const matches = tokens.filter((token) => token.key.indexOf(id) === 0)
if (matches.length === 1)
@@ -181,59 +121,113 @@ function rm (args) {
toRemove.push(id)
}
})
- return Promise.all(toRemove.map(key => {
+ await Promise.all(toRemove.map(key => {
return otplease(conf, conf => {
return profile.removeToken(key, conf)
})
}))
- })).then(() => {
if (conf.json)
output(JSON.stringify(toRemove))
else if (conf.parseable)
output(toRemove.join('\t'))
else
output('Removed ' + toRemove.length + ' token' + (toRemove.length !== 1 ? 's' : ''))
- })
-}
+ }
-function create (args) {
- const conf = config()
- const cidr = conf.cidr
- const readonly = conf.readOnly
+ async create (args) {
+ const conf = this.config()
+ const cidr = conf.cidr
+ const readonly = conf.readOnly
+
+ return readUserInfo.password().then((password) => {
+ const validCIDR = this.validateCIDRList(cidr)
+ log.info('token', 'creating')
+ return pulseTillDone.withPromise(otplease(conf, conf => {
+ return profile.createToken(password, readonly, validCIDR, conf)
+ }))
+ }).then((result) => {
+ delete result.key
+ delete result.updated
+ if (conf.json)
+ output(JSON.stringify(result))
+ else if (conf.parseable)
+ Object.keys(result).forEach((k) => output(k + '\t' + result[k]))
+ else {
+ const table = new Table()
+ for (const k of Object.keys(result))
+ table.push({ [ansistyles.bright(k)]: String(result[k]) })
+ output(table.toString())
+ }
+ })
+ }
- return readUserInfo.password().then((password) => {
- const validCIDR = validateCIDRList(cidr)
- log.info('token', 'creating')
- return pulseTillDone.withPromise(otplease(conf, conf => {
- return profile.createToken(password, readonly, validCIDR, conf)
- }))
- }).then((result) => {
- delete result.key
- delete result.updated
- if (conf.json)
- output(JSON.stringify(result))
- else if (conf.parseable)
- Object.keys(result).forEach((k) => output(k + '\t' + result[k]))
- else {
- const table = new Table()
- for (const k of Object.keys(result))
- table.push({ [ansistyles.bright(k)]: String(result[k]) })
- output(table.toString())
- }
- })
-}
+ config () {
+ const conf = { ...this.npm.flatOptions }
+ const creds = this.npm.config.getCredentialsByURI(conf.registry)
+ if (creds.token)
+ conf.auth = { token: creds.token }
+ else if (creds.username) {
+ conf.auth = {
+ basic: {
+ username: creds.username,
+ password: creds.password,
+ },
+ }
+ } else if (creds.auth) {
+ const auth = Buffer.from(creds.auth, 'base64').toString().split(':', 2)
+ conf.auth = {
+ basic: {
+ username: auth[0],
+ password: auth[1],
+ },
+ }
+ } else
+ conf.auth = {}
-function validateCIDR (cidr) {
- if (isCidrV6(cidr))
- throw InvalidCIDRError('CIDR whitelist can only contain IPv4 addresses, ' + cidr + ' is IPv6')
+ if (conf.otp)
+ conf.auth.otp = conf.otp
+ return conf
+ }
- if (!isCidrV4(cidr))
- throw InvalidCIDRError('CIDR whitelist contains invalid CIDR entry: ' + cidr)
-}
+ usageError (msg) {
+ return Object.assign(new Error(`\nUsage: ${msg}\n\n` + this.usage), {
+ code: 'EUSAGE',
+ })
+ }
-function validateCIDRList (cidrs) {
- const maybeList = cidrs ? (Array.isArray(cidrs) ? cidrs : [cidrs]) : []
- const list = maybeList.length === 1 ? maybeList[0].split(/,\s*/) : maybeList
- list.forEach(validateCIDR)
- return list
+ invalidCIDRError (msg) {
+ return Object.assign(new Error(msg), { code: 'EINVALIDCIDR' })
+ }
+
+ generateTokenIds (tokens, minLength) {
+ const byId = {}
+ for (const token of tokens) {
+ token.id = token.key
+ for (let ii = minLength; ii < token.key.length; ++ii) {
+ const match = tokens.some(ot =>
+ ot !== token &&
+ ot.key.slice(0, ii) === token.key.slice(0, ii))
+ if (!match) {
+ token.id = token.key.slice(0, ii)
+ break
+ }
+ }
+ byId[token.id] = token
+ }
+ return byId
+ }
+
+ validateCIDRList (cidrs) {
+ const maybeList = cidrs ? (Array.isArray(cidrs) ? cidrs : [cidrs]) : []
+ const list = maybeList.length === 1 ? maybeList[0].split(/,\s*/) : maybeList
+ for (const cidr of list) {
+ if (isCidrV6(cidr))
+ throw this.invalidCIDRError('CIDR whitelist can only contain IPv4 addresses, ' + cidr + ' is IPv6')
+
+ if (!isCidrV4(cidr))
+ throw this.invalidCIDRError('CIDR whitelist contains invalid CIDR entry: ' + cidr)
+ }
+ return list
+ }
}
+module.exports = Token
diff --git a/deps/npm/lib/uninstall.js b/deps/npm/lib/uninstall.js
index 15995c0b3c..d7116e4c2d 100644
--- a/deps/npm/lib/uninstall.js
+++ b/deps/npm/lib/uninstall.js
@@ -2,49 +2,62 @@ const { resolve } = require('path')
const Arborist = require('@npmcli/arborist')
const rpj = require('read-package-json-fast')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const reifyFinish = require('./utils/reify-finish.js')
const completion = require('./utils/completion/installed-shallow.js')
-const usage = usageUtil(
- 'uninstall',
- 'npm uninstall [<@scope>/]<pkg>[@<version>]... [-S|--save|--no-save]'
-)
-
-const cmd = (args, cb) => rm(args).then(() => cb()).catch(cb)
-
-const rm = async args => {
- // the /path/to/node_modules/..
- const { global, prefix } = npm.flatOptions
- const path = global ? resolve(npm.globalDir, '..') : prefix
-
- if (!args.length) {
- if (!global)
- throw new Error('Must provide a package name to remove')
- else {
- let pkg
-
- try {
- pkg = await rpj(resolve(npm.localPrefix, 'package.json'))
- } catch (er) {
- if (er.code !== 'ENOENT' && er.code !== 'ENOTDIR')
- throw er
- else
- throw usage
- }
+class Uninstall {
+ constructor (npm) {
+ this.npm = npm
+ }
- args.push(pkg.name)
- }
+ get usage () {
+ return usageUtil(
+ 'uninstall',
+ 'npm uninstall [<@scope>/]<pkg>[@<version>]... [-S|--save|--no-save]'
+ )
}
- const arb = new Arborist({ ...npm.flatOptions, path })
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
+ }
- await arb.reify({
- ...npm.flatOptions,
- rm: args,
- })
- await reifyFinish(arb)
-}
+ exec (args, cb) {
+ this.uninstall(args).then(() => cb()).catch(cb)
+ }
-module.exports = Object.assign(cmd, { usage, completion })
+ async uninstall (args) {
+ // the /path/to/node_modules/..
+ const { global, prefix } = this.npm.flatOptions
+ const path = global ? resolve(this.npm.globalDir, '..') : prefix
+
+ if (!args.length) {
+ if (!global)
+ throw new Error('Must provide a package name to remove')
+ else {
+ let pkg
+
+ try {
+ pkg = await rpj(resolve(this.npm.localPrefix, 'package.json'))
+ } catch (er) {
+ if (er.code !== 'ENOENT' && er.code !== 'ENOTDIR')
+ throw er
+ else
+ throw this.usage
+ }
+
+ args.push(pkg.name)
+ }
+ }
+
+ const arb = new Arborist({ ...this.npm.flatOptions, path })
+
+ await arb.reify({
+ ...this.npm.flatOptions,
+ rm: args,
+ })
+ await reifyFinish(this.npm, arb)
+ }
+}
+module.exports = Uninstall
diff --git a/deps/npm/lib/unpublish.js b/deps/npm/lib/unpublish.js
index bb931682b4..34751da4a5 100644
--- a/deps/npm/lib/unpublish.js
+++ b/deps/npm/lib/unpublish.js
@@ -7,102 +7,107 @@ const npmFetch = require('npm-registry-fetch')
const libunpub = require('libnpmpublish').unpublish
const readJson = util.promisify(require('read-package-json'))
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const getIdentity = require('./utils/get-identity.js')
-const usage = usageUtil('unpublish', 'npm unpublish [<@scope>/]<pkg>[@<version>]')
-
-const cmd = (args, cb) => unpublish(args).then(() => cb()).catch(cb)
-
-const completion = async (args) => {
- const { partialWord, conf } = args
-
- if (conf.argv.remain.length >= 3)
- return []
-
- const opts = npm.flatOptions
- const username = await getIdentity({ ...opts }).catch(() => null)
- if (!username)
- return []
-
- const access = await libaccess.lsPackages(username, opts)
- // do a bit of filtering at this point, so that we don't need
- // to fetch versions for more than one thing, but also don't
- // accidentally unpublish a whole project
- let pkgs = Object.keys(access || {})
- if (!partialWord || !pkgs.length)
- return pkgs
+class Unpublish {
+ constructor (npm) {
+ this.npm = npm
+ }
- const pp = npa(partialWord).name
- pkgs = pkgs.filter(p => !p.indexOf(pp))
- if (pkgs.length > 1)
- return pkgs
+ get usage () {
+ return usageUtil('unpublish', 'npm unpublish [<@scope>/]<pkg>[@<version>]')
+ }
- const json = await npmFetch.json(npa(pkgs[0]).escapedName, opts)
- const versions = Object.keys(json.versions)
- if (!versions.length)
- return pkgs
- else
- return versions.map(v => `${pkgs[0]}@${v}`)
-}
+ async completion (args) {
+ const { partialWord, conf } = args
+
+ if (conf.argv.remain.length >= 3)
+ return []
+
+ const opts = this.npm.flatOptions
+ const username = await getIdentity(this.npm, { ...opts }).catch(() => null)
+ if (!username)
+ return []
+
+ const access = await libaccess.lsPackages(username, opts)
+ // do a bit of filtering at this point, so that we don't need
+ // to fetch versions for more than one thing, but also don't
+ // accidentally unpublish a whole project
+ let pkgs = Object.keys(access || {})
+ if (!partialWord || !pkgs.length)
+ return pkgs
+
+ const pp = npa(partialWord).name
+ pkgs = pkgs.filter(p => !p.indexOf(pp))
+ if (pkgs.length > 1)
+ return pkgs
+
+ const json = await npmFetch.json(npa(pkgs[0]).escapedName, opts)
+ const versions = Object.keys(json.versions)
+ if (!versions.length)
+ return pkgs
+ else
+ return versions.map(v => `${pkgs[0]}@${v}`)
+ }
-async function unpublish (args) {
- if (args.length > 1)
- throw new Error(usage)
-
- const spec = args.length && npa(args[0])
- const opts = npm.flatOptions
- const { force, silent, loglevel } = opts
- let res
- let pkgName
- let pkgVersion
-
- log.silly('unpublish', 'args[0]', args[0])
- log.silly('unpublish', 'spec', spec)
-
- if (!spec.rawSpec && !force) {
- throw new Error(
- 'Refusing to delete entire project.\n' +
- 'Run with --force to do this.\n' +
- usage
- )
+ exec (args, cb) {
+ this.unpublish(args).then(() => cb()).catch(cb)
}
- if (!spec || path.resolve(spec.name) === npm.localPrefix) {
- // if there's a package.json in the current folder, then
- // read the package name and version out of that.
- const pkgJson = path.join(npm.localPrefix, 'package.json')
- let manifest
- try {
- manifest = await readJson(pkgJson)
- } catch (err) {
- if (err && err.code !== 'ENOENT' && err.code !== 'ENOTDIR')
- throw err
- else
- throw new Error(`Usage: ${usage}`)
+ async unpublish (args) {
+ if (args.length > 1)
+ throw new Error(this.usage)
+
+ const spec = args.length && npa(args[0])
+ const opts = this.npm.flatOptions
+ const { force, silent, loglevel } = opts
+ let pkgName
+ let pkgVersion
+
+ log.silly('unpublish', 'args[0]', args[0])
+ log.silly('unpublish', 'spec', spec)
+
+ if (!spec.rawSpec && !force) {
+ throw new Error(
+ 'Refusing to delete entire project.\n' +
+ 'Run with --force to do this.\n' +
+ this.usage
+ )
}
- log.verbose('unpublish', manifest)
-
- const { name, version, publishConfig } = manifest
- const pkgJsonSpec = npa.resolve(name, version)
- const optsWithPub = { ...opts, publishConfig }
- res = await otplease(opts, opts => libunpub(pkgJsonSpec, optsWithPub))
- pkgName = name
- pkgVersion = version ? `@${version}` : ''
- } else {
- res = await otplease(opts, opts => libunpub(spec, opts))
- pkgName = spec.name
- pkgVersion = spec.type === 'version' ? `@${spec.rawSpec}` : ''
- }
-
- if (!silent && loglevel !== 'silent')
- output(`- ${pkgName}${pkgVersion}`)
+ if (!spec || path.resolve(spec.name) === this.npm.localPrefix) {
+ // if there's a package.json in the current folder, then
+ // read the package name and version out of that.
+ const pkgJson = path.join(this.npm.localPrefix, 'package.json')
+ let manifest
+ try {
+ manifest = await readJson(pkgJson)
+ } catch (err) {
+ if (err && err.code !== 'ENOENT' && err.code !== 'ENOTDIR')
+ throw err
+ else
+ throw new Error(`Usage: ${this.usage}`)
+ }
+
+ log.verbose('unpublish', manifest)
+
+ const { name, version, publishConfig } = manifest
+ const pkgJsonSpec = npa.resolve(name, version)
+ const optsWithPub = { ...opts, publishConfig }
+ await otplease(opts, opts => libunpub(pkgJsonSpec, optsWithPub))
+ pkgName = name
+ pkgVersion = version ? `@${version}` : ''
+ } else {
+ await otplease(opts, opts => libunpub(spec, opts))
+ pkgName = spec.name
+ pkgVersion = spec.type === 'version' ? `@${spec.rawSpec}` : ''
+ }
- return res
+ if (!silent && loglevel !== 'silent')
+ output(`- ${pkgName}${pkgVersion}`)
+ }
}
-
-module.exports = Object.assign(cmd, { completion, usage })
+module.exports = Unpublish
diff --git a/deps/npm/lib/unstar.js b/deps/npm/lib/unstar.js
index 554bd60bc9..c814ef2b66 100644
--- a/deps/npm/lib/unstar.js
+++ b/deps/npm/lib/unstar.js
@@ -1,9 +1,9 @@
-const { usage } = require('./star.js')
-const npm = require('./npm.js')
+const Star = require('./star.js')
-const unstar = (args, cb) => {
- npm.config.set('star.unstar', true)
- return npm.commands.star(args, cb)
+class Unstar extends Star {
+ exec (args, cb) {
+ this.npm.config.set('star.unstar', true)
+ super.exec(args, cb)
+ }
}
-
-module.exports = Object.assign(unstar, { usage })
+module.exports = Unstar
diff --git a/deps/npm/lib/update.js b/deps/npm/lib/update.js
index 6eaa085b87..98043e09c7 100644
--- a/deps/npm/lib/update.js
+++ b/deps/npm/lib/update.js
@@ -3,37 +3,51 @@ const path = require('path')
const Arborist = require('@npmcli/arborist')
const log = require('npmlog')
-const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const reifyFinish = require('./utils/reify-finish.js')
const completion = require('./utils/completion/installed-deep.js')
-const usage = usageUtil(
- 'update',
- 'npm update [-g] [<pkg>...]'
-)
-
-const cmd = (args, cb) => update(args).then(() => cb()).catch(cb)
+class Update {
+ constructor (npm) {
+ this.npm = npm
+ }
-const update = async args => {
- const update = args.length === 0 ? true : args
- const global = path.resolve(npm.globalDir, '..')
- const where = npm.flatOptions.global
- ? global
- : npm.prefix
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'update',
+ 'npm update [-g] [<pkg>...]'
+ )
+ }
- if (npm.flatOptions.depth) {
- log.warn('update', 'The --depth option no longer has any effect. See RFC0019.\n' +
- 'https://github.com/npm/rfcs/blob/latest/implemented/0019-remove-update-depth-option.md')
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ async completion (opts) {
+ return completion(this.npm, opts)
}
- const arb = new Arborist({
- ...npm.flatOptions,
- path: where,
- })
+ exec (args, cb) {
+ this.update(args).then(() => cb()).catch(cb)
+ }
- await arb.reify({ update })
- await reifyFinish(arb)
+ async update (args) {
+ const update = args.length === 0 ? true : args
+ const global = path.resolve(this.npm.globalDir, '..')
+ const where = this.npm.flatOptions.global
+ ? global
+ : this.npm.prefix
+
+ if (this.npm.flatOptions.depth) {
+ log.warn('update', 'The --depth option no longer has any effect. See RFC0019.\n' +
+ 'https://github.com/npm/rfcs/blob/latest/implemented/0019-remove-update-depth-option.md')
+ }
+
+ const arb = new Arborist({
+ ...this.npm.flatOptions,
+ path: where,
+ })
+
+ await arb.reify({ update })
+ await reifyFinish(this.npm, arb)
+ }
}
-
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = Update
diff --git a/deps/npm/lib/utils/audit-error.js b/deps/npm/lib/utils/audit-error.js
index c7423c4472..ae0749ff6f 100644
--- a/deps/npm/lib/utils/audit-error.js
+++ b/deps/npm/lib/utils/audit-error.js
@@ -4,8 +4,7 @@
// returns 'true' if there was an error, false otherwise
const output = require('./output.js')
-const npm = require('../npm.js')
-const auditError = (report) => {
+const auditError = (npm, report) => {
if (!report || !report.error)
return false
diff --git a/deps/npm/lib/utils/completion/installed-deep.js b/deps/npm/lib/utils/completion/installed-deep.js
index f464bb9a9d..b65c17e41d 100644
--- a/deps/npm/lib/utils/completion/installed-deep.js
+++ b/deps/npm/lib/utils/completion/installed-deep.js
@@ -1,8 +1,7 @@
const { resolve } = require('path')
const Arborist = require('@npmcli/arborist')
-const npm = require('../../npm.js')
-const installedDeep = async () => {
+const installedDeep = async (npm) => {
const {
depth,
global,
diff --git a/deps/npm/lib/utils/completion/installed-shallow.js b/deps/npm/lib/utils/completion/installed-shallow.js
index c9c680e7dd..1c9b8ef5ac 100644
--- a/deps/npm/lib/utils/completion/installed-shallow.js
+++ b/deps/npm/lib/utils/completion/installed-shallow.js
@@ -1,10 +1,8 @@
-const npm = require('../../npm.js')
const { promisify } = require('util')
const readdir = promisify(require('readdir-scoped-modules'))
-const names = global => readdir(global ? npm.globalDir : npm.localDir)
-
-const installedShallow = async (opts) => {
+const installedShallow = async (npm, opts) => {
+ const names = global => readdir(global ? npm.globalDir : npm.localDir)
const { conf: { argv: { remain } } } = opts
if (remain.length > 3)
return null
diff --git a/deps/npm/lib/utils/explain-dep.js b/deps/npm/lib/utils/explain-dep.js
index 213493c654..c01bc780bf 100644
--- a/deps/npm/lib/utils/explain-dep.js
+++ b/deps/npm/lib/utils/explain-dep.js
@@ -86,9 +86,10 @@ const explainDependents = ({ name, dependents }, depth, color) => {
return str.split('\n').join('\n ')
}
-const explainEdge = ({ name, type, from, spec }, depth, color) => {
+const explainEdge = ({ name, type, bundled, from, spec }, depth, color) => {
const { bold } = color ? chalk : nocolor
return (type === 'prod' ? '' : `${colorType(type, color)} `) +
+ (bundled ? `${colorType('bundled', color)} ` : '') +
`${bold(name)}@"${bold(spec)}" from ` +
explainFrom(from, depth, color)
}
diff --git a/deps/npm/lib/utils/get-identity.js b/deps/npm/lib/utils/get-identity.js
index d5c560161d..e92a2c524e 100644
--- a/deps/npm/lib/utils/get-identity.js
+++ b/deps/npm/lib/utils/get-identity.js
@@ -1,10 +1,9 @@
const npmFetch = require('npm-registry-fetch')
-const npm = require('../npm')
const needsAuthError = (msg) =>
Object.assign(new Error(msg), { code: 'ENEEDAUTH' })
-module.exports = async (opts = {}) => {
+module.exports = async (npm, opts = {}) => {
const { registry } = opts
if (!registry)
throw Object.assign(new Error('No registry specified.'), { code: 'ENOREGISTRY' })
diff --git a/deps/npm/lib/utils/lifecycle-cmd.js b/deps/npm/lib/utils/lifecycle-cmd.js
index 94b109942a..8be9b5a12f 100644
--- a/deps/npm/lib/utils/lifecycle-cmd.js
+++ b/deps/npm/lib/utils/lifecycle-cmd.js
@@ -1,10 +1,19 @@
// The implementation of commands that are just "run a script"
-// test, start, stop, restart
-
+// restart, start, stop, test
const usageUtil = require('./usage.js')
-module.exports = (npm, stage) => {
- const cmd = (args, cb) => npm.commands['run-script']([stage, ...args], cb)
- const usage = usageUtil(stage, `npm ${stage} [-- <args>]`)
- return Object.assign(cmd, { usage })
+class LifecycleCmd {
+ constructor (npm, stage) {
+ this.npm = npm
+ this.stage = stage
+ }
+
+ get usage () {
+ return usageUtil(this.stage, `npm ${this.stage} [-- <args>]`)
+ }
+
+ exec (args, cb) {
+ this.npm.commands['run-script']([this.stage, ...args], cb)
+ }
}
+module.exports = LifecycleCmd
diff --git a/deps/npm/lib/utils/npm-usage.js b/deps/npm/lib/utils/npm-usage.js
index d4261f79dc..220f8037f1 100644
--- a/deps/npm/lib/utils/npm-usage.js
+++ b/deps/npm/lib/utils/npm-usage.js
@@ -1,10 +1,9 @@
-const npm = require('../npm.js')
const didYouMean = require('./did-you-mean.js')
const { dirname } = require('path')
const output = require('./output.js')
const { cmdList } = require('./cmd-list')
-module.exports = (valid = true) => {
+module.exports = (npm, valid = true) => {
npm.config.set('loglevel', 'silent')
const usesBrowser = npm.config.get('viewer') === 'browser'
? ' (in a browser)' : ''
@@ -22,7 +21,7 @@ npm help <term> search for help on <term>${usesBrowser}
npm help npm more involved overview${usesBrowser}
All commands:
-${npm.config.get('long') ? usages() : ('\n ' + wrap(cmdList))}
+${npm.config.get('long') ? usages(npm) : ('\n ' + wrap(cmdList))}
Specify configs in the ini-formatted file:
${npm.config.get('userconfig')}
@@ -59,12 +58,11 @@ const wrap = (arr) => {
return out.join('\n ').substr(2)
}
-const usages = () => {
+const usages = (npm) => {
// return a string of <command>: <usage>
let maxLen = 0
return cmdList.reduce((set, c) => {
- set.push([c, require(`../${npm.deref(c)}.js`).usage ||
- /* istanbul ignore next - all commands should have usage */ ''])
+ set.push([c, npm.commands[c].usage])
maxLen = Math.max(maxLen, c.length)
return set
}, [])
diff --git a/deps/npm/lib/utils/open-url.js b/deps/npm/lib/utils/open-url.js
index 28c2d038a4..1fe456bd05 100644
--- a/deps/npm/lib/utils/open-url.js
+++ b/deps/npm/lib/utils/open-url.js
@@ -1,19 +1,12 @@
-const npm = require('../npm.js')
const output = require('./output.js')
const opener = require('opener')
const { URL } = require('url')
-const isUrlValid = url => {
- try {
- return /^(https?|file):$/.test(new URL(url).protocol)
- } catch (_) {
- return false
- }
-}
-
// attempt to open URL in web-browser, print address otherwise:
-module.exports = function open (url, errMsg, cb, browser = npm.config.get('browser')) {
+const open = async (npm, url, errMsg) => {
+ const browser = npm.config.get('browser')
+
function printAlternateMsg () {
const json = npm.config.get('json')
const alternateMsg = json
@@ -28,18 +21,28 @@ module.exports = function open (url, errMsg, cb, browser = npm.config.get('brows
if (browser === false) {
printAlternateMsg()
- return cb()
+ return
}
- if (!isUrlValid(url))
- return cb(new Error('Invalid URL: ' + url))
+ try {
+ if (!/^(https?|file):$/.test(new URL(url).protocol))
+ throw new Error()
+ } catch (_) {
+ throw new Error('Invalid URL: ' + url)
+ }
const command = browser === true ? null : browser
- opener(url, { command }, (er) => {
- if (er && er.code === 'ENOENT') {
- printAlternateMsg()
- return cb()
- } else
- return cb(er)
+ await new Promise((resolve, reject) => {
+ opener(url, { command }, (err) => {
+ if (err) {
+ if (err.code === 'ENOENT')
+ printAlternateMsg()
+ else
+ return reject(err)
+ }
+ return resolve()
+ })
})
}
+
+module.exports = open
diff --git a/deps/npm/lib/utils/read-local-package.js b/deps/npm/lib/utils/read-local-package.js
index 7ab130c1f3..c31bca9947 100644
--- a/deps/npm/lib/utils/read-local-package.js
+++ b/deps/npm/lib/utils/read-local-package.js
@@ -1,8 +1,6 @@
const { resolve } = require('path')
const readJson = require('read-package-json-fast')
-const npm = require('../npm.js')
-
-async function readLocalPackageName (cb) {
+async function readLocalPackageName (npm) {
if (npm.flatOptions.global)
return
diff --git a/deps/npm/lib/utils/reify-finish.js b/deps/npm/lib/utils/reify-finish.js
index 9c95e9fcff..1c02b93a41 100644
--- a/deps/npm/lib/utils/reify-finish.js
+++ b/deps/npm/lib/utils/reify-finish.js
@@ -1,17 +1,16 @@
const reifyOutput = require('./reify-output.js')
-const npm = require('../npm.js')
const ini = require('ini')
const util = require('util')
const fs = require('fs')
const { writeFile } = fs.promises || { writeFile: util.promisify(fs.writeFile) }
const {resolve} = require('path')
-const reifyFinish = async arb => {
- await saveBuiltinConfig(arb)
- reifyOutput(arb)
+const reifyFinish = async (npm, arb) => {
+ await saveBuiltinConfig(npm, arb)
+ reifyOutput(npm, arb)
}
-const saveBuiltinConfig = async arb => {
+const saveBuiltinConfig = async (npm, arb) => {
const { options: { global }, actualTree } = arb
if (!global)
return
diff --git a/deps/npm/lib/utils/reify-output.js b/deps/npm/lib/utils/reify-output.js
index 4abaadc2ec..216f0e902e 100644
--- a/deps/npm/lib/utils/reify-output.js
+++ b/deps/npm/lib/utils/reify-output.js
@@ -9,7 +9,6 @@
// found 37 vulnerabilities (5 low, 7 moderate, 25 high)
// run `npm audit fix` to fix them, or `npm audit` for details
-const npm = require('../npm.js')
const log = require('npmlog')
const output = require('./output.js')
const { depth } = require('treeverse')
@@ -19,7 +18,7 @@ const { readTree: getFundingInfo } = require('libnpmfund')
const auditError = require('./audit-error.js')
// TODO: output JSON if flatOptions.json is true
-const reifyOutput = arb => {
+const reifyOutput = (npm, arb) => {
// don't print any info in --silent mode
if (log.levels[log.level] > log.levels.error)
return
@@ -29,7 +28,7 @@ const reifyOutput = arb => {
// note: fails and crashes if we're running audit fix and there was an error
// which is a good thing, because there's no point printing all this other
// stuff in that case!
- const auditReport = auditError(arb.auditReport) ? null : arb.auditReport
+ const auditReport = auditError(npm, arb.auditReport) ? null : arb.auditReport
const summary = {
added: 0,
@@ -75,9 +74,9 @@ const reifyOutput = arb => {
}
output(JSON.stringify(summary, 0, 2))
} else {
- packagesChangedMessage(summary)
+ packagesChangedMessage(npm, summary)
packagesFundingMessage(summary)
- printAuditReport(auditReport)
+ printAuditReport(npm, auditReport)
}
}
@@ -85,7 +84,7 @@ const reifyOutput = arb => {
// at the end if there's still stuff, because it's silly for `npm audit`
// to tell you to run `npm audit` for details. otherwise, use the summary
// report. if we get here, we know it's not quiet or json.
-const printAuditReport = report => {
+const printAuditReport = (npm, report) => {
if (!report)
return
@@ -102,7 +101,7 @@ const printAuditReport = report => {
output('\n' + res.report)
}
-const packagesChangedMessage = ({ added, removed, changed, audited }) => {
+const packagesChangedMessage = (npm, { added, removed, changed, audited }) => {
const msg = ['\n']
if (added === 0 && removed === 0 && changed === 0) {
msg.push('up to date')
diff --git a/deps/npm/lib/utils/usage.js b/deps/npm/lib/utils/usage.js
index ddcbd708b7..5f4eca73ea 100644
--- a/deps/npm/lib/utils/usage.js
+++ b/deps/npm/lib/utils/usage.js
@@ -1,7 +1,7 @@
-var aliases = require('../utils/cmd-list').aliases
+const aliases = require('../utils/cmd-list').aliases
module.exports = function usage (cmd, txt, opt) {
- var post = Object.keys(aliases).reduce(function (p, c) {
+ const post = Object.keys(aliases).reduce(function (p, c) {
var val = aliases[c]
if (val !== cmd)
return p
diff --git a/deps/npm/lib/version.js b/deps/npm/lib/version.js
index 9d87b2f2c9..1ba834f5d7 100644
--- a/deps/npm/lib/version.js
+++ b/deps/npm/lib/version.js
@@ -1,75 +1,86 @@
const libversion = require('libnpmversion')
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
-const completion = async (opts) => {
- const { conf: { argv: { remain } } } = opts
- if (remain.length > 2)
- return []
+class Version {
+ constructor (npm) {
+ this.npm = npm
+ }
- return [
- 'major',
- 'minor',
- 'patch',
- 'premajor',
- 'preminor',
- 'prepatch',
- 'prerelease',
- 'from-git',
- ]
-}
+ get usage () {
+ return usageUtil(
+ 'version',
+ 'npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]\n' +
+ '(run in package dir)\n\n' +
+ `'npm -v' or 'npm --version' to print npm version (${this.npm.version})\n` +
+ `'npm view <pkg> version' to view a package's published version\n` +
+ `'npm ls' to inspect current package/dependency versions\n`
+ )
+ }
-const usage = usageUtil('version',
-`npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]
-(run in package dir)
+ async completion (opts) {
+ const { conf: { argv: { remain } } } = opts
+ if (remain.length > 2)
+ return []
-'npm -v' or 'npm --version' to print npm version (${npm.version})
-'npm view <pkg> version' to view a package's published version
-'npm ls' to inspect current package/dependency versions`
-)
+ return [
+ 'major',
+ 'minor',
+ 'patch',
+ 'premajor',
+ 'preminor',
+ 'prepatch',
+ 'prerelease',
+ 'from-git',
+ ]
+ }
-const cmd = (args, cb) => version(args).then(() => cb()).catch(cb)
+ exec (args, cb) {
+ return this.version(args).then(() => cb()).catch(cb)
+ }
-const version = async args => {
- switch (args.length) {
- case 0:
- return list()
- case 1:
- return version_(args)
- default:
- throw usage
+ async version (args) {
+ switch (args.length) {
+ case 0:
+ return this.list()
+ case 1:
+ return this.change(args)
+ default:
+ throw this.usage
+ }
}
-}
-const version_ = async (args) => {
- const prefix = npm.flatOptions.tagVersionPrefix
- const version = await libversion(args[0], {
- ...npm.flatOptions,
- path: npm.prefix,
- })
- return output(`${prefix}${version}`)
-}
+ async change (args) {
+ const prefix = this.npm.flatOptions.tagVersionPrefix
+ const version = await libversion(args[0], {
+ ...this.npm.flatOptions,
+ path: this.npm.prefix,
+ })
+ return output(`${prefix}${version}`)
+ }
-const list = async () => {
- const results = {}
- const { promisify } = require('util')
- const { resolve } = require('path')
- const readFile = promisify(require('fs').readFile)
- const pj = resolve(npm.prefix, 'package.json')
+ async list () {
+ const results = {}
+ const { promisify } = require('util')
+ const { resolve } = require('path')
+ const readFile = promisify(require('fs').readFile)
+ const pj = resolve(this.npm.prefix, 'package.json')
- const pkg = await readFile(pj, 'utf8')
- .then(data => JSON.parse(data))
- .catch(() => ({}))
+ const pkg = await readFile(pj, 'utf8')
+ .then(data => JSON.parse(data))
+ .catch(() => ({}))
- if (pkg.name && pkg.version)
- results[pkg.name] = pkg.version
+ if (pkg.name && pkg.version)
+ results[pkg.name] = pkg.version
- results.npm = npm.version
- for (const [key, version] of Object.entries(process.versions))
- results[key] = version
+ results.npm = this.npm.version
+ for (const [key, version] of Object.entries(process.versions))
+ results[key] = version
- output(npm.flatOptions.json ? JSON.stringify(results, null, 2) : results)
+ if (this.npm.flatOptions.json)
+ output(JSON.stringify(results, null, 2))
+ else
+ output(results)
+ }
}
-
-module.exports = Object.assign(cmd, { usage, completion })
+module.exports = Version
diff --git a/deps/npm/lib/view.js b/deps/npm/lib/view.js
index d6d79d35c5..d0d5fa59d4 100644
--- a/deps/npm/lib/view.js
+++ b/deps/npm/lib/view.js
@@ -3,293 +3,370 @@
const byteSize = require('byte-size')
const color = require('ansicolors')
const columns = require('cli-columns')
+const fs = require('fs')
+const jsonParse = require('json-parse-even-better-errors')
const log = require('npmlog')
const npa = require('npm-package-arg')
-const npm = require('./npm.js')
-const { packument } = require('pacote')
const path = require('path')
-const { inspect, promisify } = require('util')
const relativeDate = require('tiny-relative-date')
const semver = require('semver')
const style = require('ansistyles')
-const usageUtil = require('./utils/usage')
+const { inspect, promisify } = require('util')
+const { packument } = require('pacote')
+
+const usageUtil = require('./utils/usage.js')
-const fs = require('fs')
const readFile = promisify(fs.readFile)
-const jsonParse = require('json-parse-even-better-errors')
const readJson = async file => jsonParse(await readFile(file, 'utf8'))
-const usage = usageUtil(
- 'view',
- 'npm view [<@scope>/]<pkg>[@<version>] [<field>[.subfield]...]'
-)
-
-const cmd = (args, cb) => view(args).then(() => cb()).catch(cb)
+class View {
+ constructor (npm) {
+ this.npm = npm
+ }
-const completion = async (opts) => {
- if (opts.conf.argv.remain.length <= 2) {
- // There used to be registry completion here, but it stopped
- // making sense somewhere around 50,000 packages on the registry
- return
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'view',
+ 'npm view [<@scope>/]<pkg>[@<version>] [<field>[.subfield]...]'
+ )
}
- // have the package, get the fields
- const config = { ...npm.flatOptions, fullMetadata: true, preferOnline: true }
- const { defaultTag } = config
- const spec = npa(opts.conf.argv.remain[2])
- const pckmnt = await packument(spec, config)
- const dv = pckmnt.versions[pckmnt['dist-tags'][defaultTag]]
- pckmnt.versions = Object.keys(pckmnt.versions).sort(semver.compareLoose)
-
- return getFields(pckmnt).concat(getFields(dv))
-
- function getFields (d, f, pref) {
- f = f || []
- if (!d)
+
+ async completion (opts) {
+ if (opts.conf.argv.remain.length <= 2) {
+ // There used to be registry completion here, but it stopped
+ // making sense somewhere around 50,000 packages on the registry
+ return
+ }
+ // have the package, get the fields
+ const config = {
+ ...this.npm.flatOptions,
+ fullMetadata: true,
+ preferOnline: true,
+ }
+ const { defaultTag } = config
+ const spec = npa(opts.conf.argv.remain[2])
+ const pckmnt = await packument(spec, config)
+ const dv = pckmnt.versions[pckmnt['dist-tags'][defaultTag]]
+ pckmnt.versions = Object.keys(pckmnt.versions).sort(semver.compareLoose)
+
+ return getFields(pckmnt).concat(getFields(dv))
+
+ function getFields (d, f, pref) {
+ f = f || []
+ if (!d)
+ return f
+ pref = pref || []
+ Object.keys(d).forEach((k) => {
+ if (k.charAt(0) === '_' || k.indexOf('.') !== -1)
+ return
+ const p = pref.concat(k).join('.')
+ f.push(p)
+ if (Array.isArray(d[k])) {
+ d[k].forEach((val, i) => {
+ const pi = p + '[' + i + ']'
+ if (val && typeof val === 'object')
+ getFields(val, f, [p])
+ else
+ f.push(pi)
+ })
+ return
+ }
+ if (typeof d[k] === 'object')
+ getFields(d[k], f, [p])
+ })
return f
- pref = pref || []
- Object.keys(d).forEach(function (k) {
- if (k.charAt(0) === '_' || k.indexOf('.') !== -1)
- return
- const p = pref.concat(k).join('.')
- f.push(p)
- if (Array.isArray(d[k])) {
- d[k].forEach(function (val, i) {
- const pi = p + '[' + i + ']'
- if (val && typeof val === 'object')
- getFields(val, f, [p])
- else
- f.push(pi)
- })
- return
- }
- if (typeof d[k] === 'object')
- getFields(d[k], f, [p])
- })
- return f
+ }
}
-}
-const view = async args => {
- if (!args.length)
- args = ['.']
-
- const opts = { ...npm.flatOptions, preferOnline: true, fullMetadata: true }
- const pkg = args.shift()
- let nv
- if (/^[.]@/.test(pkg))
- nv = npa.resolve(null, pkg.slice(2))
- else
- nv = npa(pkg)
-
- const name = nv.name
- const local = (name === '.' || !name)
-
- if (opts.global && local)
- throw new Error('Cannot use view command in global mode.')
-
- if (local) {
- const dir = npm.prefix
- const manifest = await readJson(path.resolve(dir, 'package.json'))
- if (!manifest.name)
- throw new Error('Invalid package.json, no "name" field')
- const p = manifest.name
- nv = npa(p)
- if (pkg && ~pkg.indexOf('@'))
- nv.rawSpec = pkg.split('@')[pkg.indexOf('@')]
-
- await fetchAndRead(nv, args, opts)
- } else
- await fetchAndRead(nv, args, opts)
-}
+ exec (args, cb) {
+ this.view(args).then(() => cb()).catch(cb)
+ }
-const fetchAndRead = async (nv, args, opts) => {
- // get the data about this package
- let version = nv.rawSpec || npm.flatOptions.defaultTag
+ async view (args) {
+ if (!args.length)
+ args = ['.']
- const pckmnt = await packument(nv, opts)
+ const opts = {
+ ...this.npm.flatOptions,
+ preferOnline: true,
+ fullMetadata: true,
+ }
+ const pkg = args.shift()
+ let nv
+ if (/^[.]@/.test(pkg))
+ nv = npa.resolve(null, pkg.slice(2))
+ else
+ nv = npa(pkg)
+
+ const name = nv.name
+ const local = (name === '.' || !name)
+
+ if (opts.global && local)
+ throw new Error('Cannot use view command in global mode.')
+
+ if (local) {
+ const dir = this.npm.prefix
+ const manifest = await readJson(path.resolve(dir, 'package.json'))
+ if (!manifest.name)
+ throw new Error('Invalid package.json, no "name" field')
+ const p = manifest.name
+ nv = npa(p)
+ if (pkg && ~pkg.indexOf('@'))
+ nv.rawSpec = pkg.split('@')[pkg.indexOf('@')]
+ }
- if (pckmnt['dist-tags'] && pckmnt['dist-tags'][version])
- version = pckmnt['dist-tags'][version]
+ // get the data about this package
+ let version = nv.rawSpec || this.npm.flatOptions.defaultTag
- if (pckmnt.time && pckmnt.time.unpublished) {
- const u = pckmnt.time.unpublished
- const er = new Error('Unpublished by ' + u.name + ' on ' + u.time)
- er.statusCode = 404
- er.code = 'E404'
- er.pkgid = pckmnt._id
- throw er
- }
+ const pckmnt = await packument(nv, opts)
- const results = []
- const versions = pckmnt.versions || {}
- pckmnt.versions = Object.keys(versions).sort(semver.compareLoose)
- if (!args.length)
- args = ['']
+ if (pckmnt['dist-tags'] && pckmnt['dist-tags'][version])
+ version = pckmnt['dist-tags'][version]
- // remove readme unless we asked for it
- if (args.indexOf('readme') === -1)
- delete pckmnt.readme
+ if (pckmnt.time && pckmnt.time.unpublished) {
+ const u = pckmnt.time.unpublished
+ const er = new Error('Unpublished by ' + u.name + ' on ' + u.time)
+ er.statusCode = 404
+ er.code = 'E404'
+ er.pkgid = pckmnt._id
+ throw er
+ }
- Object.keys(versions).forEach(function (v) {
- if (semver.satisfies(v, version, true)) {
- args.forEach(arg => {
- // remove readme unless we asked for it
- if (args.indexOf('readme') !== -1)
- delete versions[v].readme
+ const results = []
+ const versions = pckmnt.versions || {}
+ pckmnt.versions = Object.keys(versions).sort(semver.compareLoose)
+ if (!args.length)
+ args = ['']
- results.push(showFields(pckmnt, versions[v], arg))
- })
+ // remove readme unless we asked for it
+ if (args.indexOf('readme') === -1)
+ delete pckmnt.readme
+
+ Object.keys(versions).forEach((v) => {
+ if (semver.satisfies(v, version, true)) {
+ args.forEach(arg => {
+ // remove readme unless we asked for it
+ if (args.indexOf('readme') !== -1)
+ delete versions[v].readme
+
+ results.push(showFields(pckmnt, versions[v], arg))
+ })
+ }
+ })
+ let retval = results.reduce(reducer, {})
+
+ if (args.length === 1 && args[0] === '') {
+ retval = cleanBlanks(retval)
+ log.silly('view', retval)
}
- })
- let retval = results.reduce(reducer, {})
- if (args.length === 1 && args[0] === '') {
- retval = cleanBlanks(retval)
- log.silly('view', retval)
+ if (
+ !opts.json &&
+ args.length === 1 &&
+ args[0] === ''
+ ) {
+ // general view
+ pckmnt.version = version
+ await Promise.all(
+ results.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]][''], opts))
+ )
+ return retval
+ } else {
+ // view by field name
+ await this.printData(retval, pckmnt._id, opts)
+ return retval
+ }
}
- if (
- !opts.json &&
- args.length === 1 &&
- args[0] === ''
- ) {
- // general view
- pckmnt.version = version
- await Promise.all(
- results.map((v) => prettyView(pckmnt, v[Object.keys(v)[0]][''], opts))
- )
- return retval
- } else {
- // view by field name
- await printData(retval, pckmnt._id, opts)
- return retval
- }
-}
+ async printData (data, name, opts) {
+ const versions = Object.keys(data)
+ let msg = ''
+ let msgJson = []
+ const includeVersions = versions.length > 1
+ let includeFields
+
+ versions.forEach((v) => {
+ const fields = Object.keys(data[v])
+ includeFields = includeFields || (fields.length > 1)
+ if (opts.json)
+ msgJson.push({})
+ fields.forEach((f) => {
+ let d = cleanup(data[v][f])
+ if (fields.length === 1 && opts.json)
+ msgJson[msgJson.length - 1][f] = d
-const prettyView = async (packument, manifest, opts) => {
- // More modern, pretty printing of default view
- const unicode = opts.unicode
- const tags = []
+ if (includeVersions || includeFields || typeof d !== 'string') {
+ if (opts.json)
+ msgJson[msgJson.length - 1][f] = d
+ else {
+ d = inspect(d, {
+ showHidden: false,
+ depth: 5,
+ colors: this.npm.color,
+ maxArrayLength: null,
+ })
+ }
+ } else if (typeof d === 'string' && opts.json)
+ d = JSON.stringify(d)
+
+ if (!opts.json) {
+ if (f && includeFields)
+ f += ' = '
+ msg += (includeVersions ? name + '@' + v + ' ' : '') +
+ (includeFields ? f : '') + d + '\n'
+ }
+ })
+ })
- Object.keys(packument['dist-tags']).forEach((t) => {
- const version = packument['dist-tags'][t]
- tags.push(`${style.bright(color.green(t))}: ${version}`)
- })
- const unpackedSize = manifest.dist.unpackedSize &&
- byteSize(manifest.dist.unpackedSize)
- const licenseField = manifest.license || 'Proprietary'
- const info = {
- name: color.green(manifest.name),
- version: color.green(manifest.version),
- bins: Object.keys(manifest.bin || {}).map(color.yellow),
- versions: color.yellow(packument.versions.length + ''),
- description: manifest.description,
- deprecated: manifest.deprecated,
- keywords: (packument.keywords || []).map(color.yellow),
- license: typeof licenseField === 'string'
- ? licenseField
- : (licenseField.type || 'Proprietary'),
- deps: Object.keys(manifest.dependencies || {}).map((dep) => {
- return `${color.yellow(dep)}: ${manifest.dependencies[dep]}`
- }),
- publisher: manifest._npmUser && unparsePerson({
- name: color.yellow(manifest._npmUser.name),
- email: color.cyan(manifest._npmUser.email),
- }),
- modified: !packument.time ? undefined
- : color.yellow(relativeDate(packument.time[packument.version])),
- maintainers: (packument.maintainers || []).map((u) => unparsePerson({
- name: color.yellow(u.name),
- email: color.cyan(u.email),
- })),
- repo: (
- manifest.bugs && (manifest.bugs.url || manifest.bugs)
- ) || (
- manifest.repository && (manifest.repository.url || manifest.repository)
- ),
- site: (
- manifest.homepage && (manifest.homepage.url || manifest.homepage)
- ),
- tags,
- tarball: color.cyan(manifest.dist.tarball),
- shasum: color.yellow(manifest.dist.shasum),
- integrity: manifest.dist.integrity && color.yellow(manifest.dist.integrity),
- fileCount: manifest.dist.fileCount && color.yellow(manifest.dist.fileCount),
- unpackedSize: unpackedSize && color.yellow(unpackedSize.value) + ' ' + unpackedSize.unit,
- }
- if (info.license.toLowerCase().trim() === 'proprietary')
- info.license = style.bright(color.red(info.license))
- else
- info.license = color.green(info.license)
-
- console.log('')
- console.log(
- style.underline(style.bright(`${info.name}@${info.version}`)) +
- ' | ' + info.license +
- ' | deps: ' + (info.deps.length ? color.cyan(info.deps.length) : color.green('none')) +
- ' | versions: ' + info.versions
- )
- info.description && console.log(info.description)
- if (info.repo || info.site)
- info.site && console.log(color.cyan(info.site))
-
- const warningSign = unicode ? ' ⚠️ ' : '!!'
- info.deprecated && console.log(
- `\n${style.bright(color.red('DEPRECATED'))}${
- warningSign
- } - ${info.deprecated}`
- )
+ if (opts.json) {
+ if (msgJson.length && Object.keys(msgJson[0]).length === 1) {
+ const k = Object.keys(msgJson[0])[0]
+ msgJson = msgJson.map(m => m[k])
+ }
+ if (msgJson.length === 1)
+ msg = JSON.stringify(msgJson[0], null, 2) + '\n'
+ else if (msgJson.length > 1)
+ msg = JSON.stringify(msgJson, null, 2) + '\n'
+ }
- if (info.keywords.length) {
- console.log('')
- console.log('keywords:', info.keywords.join(', '))
- }
+ // disable the progress bar entirely, as we can't meaningfully update it if
+ // we may have partial lines printed.
+ log.disableProgress()
- if (info.bins.length) {
- console.log('')
- console.log('bin:', info.bins.join(', '))
+ // only log if there is something to log
+ if (msg !== '')
+ console.log(msg.trim())
}
- console.log('')
- console.log('dist')
- console.log('.tarball:', info.tarball)
- console.log('.shasum:', info.shasum)
- info.integrity && console.log('.integrity:', info.integrity)
- info.unpackedSize && console.log('.unpackedSize:', info.unpackedSize)
+ async prettyView (packument, manifest, opts) {
+ // More modern, pretty printing of default view
+ const unicode = opts.unicode
+ const tags = []
+
+ Object.keys(packument['dist-tags']).forEach((t) => {
+ const version = packument['dist-tags'][t]
+ tags.push(`${style.bright(color.green(t))}: ${version}`)
+ })
+ const unpackedSize = manifest.dist.unpackedSize &&
+ byteSize(manifest.dist.unpackedSize)
+ const licenseField = manifest.license || 'Proprietary'
+ const info = {
+ name: color.green(manifest.name),
+ version: color.green(manifest.version),
+ bins: Object.keys(manifest.bin || {}).map(color.yellow),
+ versions: color.yellow(packument.versions.length + ''),
+ description: manifest.description,
+ deprecated: manifest.deprecated,
+ keywords: (packument.keywords || []).map(color.yellow),
+ license: typeof licenseField === 'string'
+ ? licenseField
+ : (licenseField.type || 'Proprietary'),
+ deps: Object.keys(manifest.dependencies || {}).map((dep) => {
+ return `${color.yellow(dep)}: ${manifest.dependencies[dep]}`
+ }),
+ publisher: manifest._npmUser && unparsePerson({
+ name: color.yellow(manifest._npmUser.name),
+ email: color.cyan(manifest._npmUser.email),
+ }),
+ modified: !packument.time ? undefined
+ : color.yellow(relativeDate(packument.time[packument.version])),
+ maintainers: (packument.maintainers || []).map((u) => unparsePerson({
+ name: color.yellow(u.name),
+ email: color.cyan(u.email),
+ })),
+ repo: (
+ manifest.bugs && (manifest.bugs.url || manifest.bugs)
+ ) || (
+ manifest.repository && (manifest.repository.url || manifest.repository)
+ ),
+ site: (
+ manifest.homepage && (manifest.homepage.url || manifest.homepage)
+ ),
+ tags,
+ tarball: color.cyan(manifest.dist.tarball),
+ shasum: color.yellow(manifest.dist.shasum),
+ integrity:
+ manifest.dist.integrity && color.yellow(manifest.dist.integrity),
+ fileCount:
+ manifest.dist.fileCount && color.yellow(manifest.dist.fileCount),
+ unpackedSize: unpackedSize && color.yellow(unpackedSize.value) + ' ' + unpackedSize.unit,
+ }
+ if (info.license.toLowerCase().trim() === 'proprietary')
+ info.license = style.bright(color.red(info.license))
+ else
+ info.license = color.green(info.license)
- const maxDeps = 24
- if (info.deps.length) {
console.log('')
- console.log('dependencies:')
- console.log(columns(info.deps.slice(0, maxDeps), { padding: 1 }))
- if (info.deps.length > maxDeps)
- console.log(`(...and ${info.deps.length - maxDeps} more.)`)
- }
+ console.log(
+ style.underline(style.bright(`${info.name}@${info.version}`)) +
+ ' | ' + info.license +
+ ' | deps: ' + (info.deps.length ? color.cyan(info.deps.length) : color.green('none')) +
+ ' | versions: ' + info.versions
+ )
+ info.description && console.log(info.description)
+ if (info.repo || info.site)
+ info.site && console.log(color.cyan(info.site))
+
+ const warningSign = unicode ? ' ⚠️ ' : '!!'
+ info.deprecated && console.log(
+ `\n${style.bright(color.red('DEPRECATED'))}${
+ warningSign
+ } - ${info.deprecated}`
+ )
+
+ if (info.keywords.length) {
+ console.log('')
+ console.log('keywords:', info.keywords.join(', '))
+ }
+
+ if (info.bins.length) {
+ console.log('')
+ console.log('bin:', info.bins.join(', '))
+ }
- if (info.maintainers && info.maintainers.length) {
console.log('')
- console.log('maintainers:')
- info.maintainers.forEach((u) => console.log('-', u))
- }
+ console.log('dist')
+ console.log('.tarball:', info.tarball)
+ console.log('.shasum:', info.shasum)
+ info.integrity && console.log('.integrity:', info.integrity)
+ info.unpackedSize && console.log('.unpackedSize:', info.unpackedSize)
+
+ const maxDeps = 24
+ if (info.deps.length) {
+ console.log('')
+ console.log('dependencies:')
+ console.log(columns(info.deps.slice(0, maxDeps), { padding: 1 }))
+ if (info.deps.length > maxDeps)
+ console.log(`(...and ${info.deps.length - maxDeps} more.)`)
+ }
- console.log('')
- console.log('dist-tags:')
- console.log(columns(info.tags))
+ if (info.maintainers && info.maintainers.length) {
+ console.log('')
+ console.log('maintainers:')
+ info.maintainers.forEach((u) => console.log('-', u))
+ }
- if (info.publisher || info.modified) {
- let publishInfo = 'published'
- if (info.modified)
- publishInfo += ` ${info.modified}`
- if (info.publisher)
- publishInfo += ` by ${info.publisher}`
console.log('')
- console.log(publishInfo)
+ console.log('dist-tags:')
+ console.log(columns(info.tags))
+
+ if (info.publisher || info.modified) {
+ let publishInfo = 'published'
+ if (info.modified)
+ publishInfo += ` ${info.modified}`
+ if (info.publisher)
+ publishInfo += ` by ${info.publisher}`
+ console.log('')
+ console.log(publishInfo)
+ }
}
}
+module.exports = View
function cleanBlanks (obj) {
const clean = {}
- Object.keys(obj).forEach(function (version) {
+ Object.keys(obj).forEach((version) => {
clean[version] = obj[version]['']
})
return clean
@@ -297,9 +374,9 @@ function cleanBlanks (obj) {
function reducer (l, r) {
if (r) {
- Object.keys(r).forEach(function (v) {
+ Object.keys(r).forEach((v) => {
l[v] = l[v] || {}
- Object.keys(r[v]).forEach(function (t) {
+ Object.keys(r[v]).forEach((t) => {
l[v][t] = r[v][t]
})
})
@@ -311,8 +388,8 @@ function reducer (l, r) {
// return whatever was printed
function showFields (data, version, fields) {
const o = {}
- ;[data, version].forEach(function (s) {
- Object.keys(s).forEach(function (k) {
+ ;[data, version].forEach((s) => {
+ Object.keys(s).forEach((k) => {
o[k] = s[k]
})
})
@@ -346,7 +423,7 @@ function search (data, fields, version, title) {
return search(data[0], fields, version, title)
let results = []
- data.forEach(function (data, i) {
+ data.forEach((data, i) => {
const tl = title.length
const newt = title.substr(0, tl - fields.join('.').length - 1) +
'[' + i + ']' + [''].concat(fields).join('.')
@@ -368,68 +445,6 @@ function search (data, fields, version, title) {
return o
}
-async function printData (data, name, opts) {
- const versions = Object.keys(data)
- let msg = ''
- let msgJson = []
- const includeVersions = versions.length > 1
- let includeFields
-
- versions.forEach(function (v) {
- const fields = Object.keys(data[v])
- includeFields = includeFields || (fields.length > 1)
- if (opts.json)
- msgJson.push({})
- fields.forEach(function (f) {
- let d = cleanup(data[v][f])
- if (fields.length === 1 && opts.json)
- msgJson[msgJson.length - 1][f] = d
-
- if (includeVersions || includeFields || typeof d !== 'string') {
- if (opts.json)
- msgJson[msgJson.length - 1][f] = d
- else {
- d = inspect(d, {
- showHidden: false,
- depth: 5,
- colors: npm.color,
- maxArrayLength: null,
- })
- }
- } else if (typeof d === 'string' && opts.json)
- d = JSON.stringify(d)
-
- if (!opts.json) {
- if (f && includeFields)
- f += ' = '
- msg += (includeVersions ? name + '@' + v + ' ' : '') +
- (includeFields ? f : '') + d + '\n'
- }
- })
- })
-
- if (opts.json) {
- if (msgJson.length && Object.keys(msgJson[0]).length === 1) {
- const k = Object.keys(msgJson[0])[0]
- msgJson = msgJson.map(function (m) {
- return m[k]
- })
- }
- if (msgJson.length === 1)
- msg = JSON.stringify(msgJson[0], null, 2) + '\n'
- else if (msgJson.length > 1)
- msg = JSON.stringify(msgJson, null, 2) + '\n'
- }
-
- // disable the progress bar entirely, as we can't meaningfully update it if
- // we may have partial lines printed.
- log.disableProgress()
-
- // only log if there is something to log
- if (msg !== '')
- console.log(msg.trim())
-}
-
function cleanup (data) {
if (Array.isArray(data))
return data.map(cleanup)
@@ -447,10 +462,9 @@ function cleanup (data) {
return data
}
+
function unparsePerson (d) {
return d.name +
(d.email ? ' <' + d.email + '>' : '') +
(d.url ? ' (' + d.url + ')' : '')
}
-
-module.exports = Object.assign(cmd, { completion, usage })
diff --git a/deps/npm/lib/whoami.js b/deps/npm/lib/whoami.js
index bbbc39ccec..39184ed9c5 100644
--- a/deps/npm/lib/whoami.js
+++ b/deps/npm/lib/whoami.js
@@ -1,16 +1,29 @@
-const npm = require('./npm.js')
const output = require('./utils/output.js')
const getIdentity = require('./utils/get-identity.js')
const usageUtil = require('./utils/usage.js')
-const cmd = (args, cb) => whoami(args).then(() => cb()).catch(cb)
+class Whoami {
+ constructor (npm) {
+ this.npm = npm
+ }
-const usage = usageUtil('whoami', 'npm whoami [--registry <registry>]\n(just prints username according to given registry)')
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil(
+ 'whoami',
+ 'npm whoami [--registry <registry>]\n' +
+ '(just prints username according to given registry)'
+ )
+ }
-const whoami = async ([spec]) => {
- const opts = npm.flatOptions
- const username = await getIdentity(opts, spec)
- output(opts.json ? JSON.stringify(username) : username)
-}
+ exec (args, cb) {
+ this.whoami(args).then(() => cb()).catch(cb)
+ }
-module.exports = Object.assign(cmd, { usage })
+ async whoami (args) {
+ const opts = this.npm.flatOptions
+ const username = await getIdentity(this.npm, opts)
+ output(opts.json ? JSON.stringify(username) : username)
+ }
+}
+module.exports = Whoami