Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGar <gar+gh@danger.computer>2021-02-25 02:54:50 +0300
committerRuy Adorno <ruyadorno@hotmail.com>2021-03-05 00:05:08 +0300
commit4a5dd3a5a200b3f4f7b47168497d8e03dca3a2ca (patch)
treed34a1ea229b719c3cfbdce85899ceaf67b43e7ab
parentb33c760cea7fe2696d35b5530abc1b455980fef1 (diff)
fix(npm) pass npm context everywhere
Instead of files randomly requiring the npm singleton, we pass it where it needs to go so that tests don't need to do so much require mocking everywhere PR-URL: https://github.com/npm/cli/pull/2772 Credit: @wraithgar Close: #2772 Reviewed-by: @ruyadorno
-rw-r--r--lib/access.js285
-rw-r--r--lib/adduser.js105
-rw-r--r--lib/audit.js90
-rw-r--r--lib/auth/legacy.js8
-rw-r--r--lib/auth/oauth.js5
-rw-r--r--lib/auth/saml.js5
-rw-r--r--lib/auth/sso.js5
-rw-r--r--lib/bin.js33
-rw-r--r--lib/birthday.js27
-rw-r--r--lib/bugs.js69
-rw-r--r--lib/cache.js184
-rw-r--r--lib/ci.js108
-rw-r--r--lib/completion.js201
-rw-r--r--lib/config.js372
-rw-r--r--lib/dedupe.js46
-rw-r--r--lib/deprecate.js115
-rw-r--r--lib/diff.js416
-rw-r--r--lib/dist-tag.js181
-rw-r--r--lib/docs.js74
-rw-r--r--lib/doctor.js487
-rw-r--r--lib/edit.js70
-rw-r--r--lib/exec.js451
-rw-r--r--lib/explain.js150
-rw-r--r--lib/explore.js119
-rw-r--r--lib/find-dupes.js19
-rw-r--r--lib/fund.js344
-rw-r--r--lib/get.js30
-rw-r--r--lib/help-search.js312
-rw-r--r--lib/help.js357
-rw-r--r--lib/hook.js110
-rw-r--r--lib/init.js157
-rw-r--r--lib/install-ci-test.js32
-rw-r--r--lib/install-test.js34
-rw-r--r--lib/install.js233
-rw-r--r--lib/link.js257
-rw-r--r--lib/ll.js22
-rw-r--r--lib/logout.js76
-rw-r--r--lib/ls.js301
-rw-r--r--lib/npm.js60
-rw-r--r--lib/org.js249
-rw-r--r--lib/outdated.js369
-rw-r--r--lib/owner.js212
-rw-r--r--lib/pack.js63
-rw-r--r--lib/ping.js49
-rw-r--r--lib/prefix.js25
-rw-r--r--lib/profile.js604
-rw-r--r--lib/prune.js40
-rw-r--r--lib/publish.js210
-rw-r--r--lib/rebuild.js98
-rw-r--r--lib/repo.js80
-rw-r--r--lib/restart.js11
-rw-r--r--lib/root.js25
-rw-r--r--lib/run-script.js235
-rw-r--r--lib/search.js129
-rw-r--r--lib/set-script.js92
-rw-r--r--lib/set.js32
-rw-r--r--lib/shrinkwrap.js92
-rw-r--r--lib/star.js118
-rw-r--r--lib/stars.js61
-rw-r--r--lib/start.js11
-rw-r--r--lib/stop.js11
-rw-r--r--lib/team.js248
-rw-r--r--lib/test.js29
-rw-r--r--lib/token.js314
-rw-r--r--lib/uninstall.js87
-rw-r--r--lib/unpublish.js175
-rw-r--r--lib/unstar.js14
-rw-r--r--lib/update.js62
-rw-r--r--lib/utils/audit-error.js3
-rw-r--r--lib/utils/completion/installed-deep.js3
-rw-r--r--lib/utils/completion/installed-shallow.js6
-rw-r--r--lib/utils/get-identity.js3
-rw-r--r--lib/utils/lifecycle-cmd.js21
-rw-r--r--lib/utils/npm-usage.js10
-rw-r--r--lib/utils/open-url.js41
-rw-r--r--lib/utils/read-local-package.js4
-rw-r--r--lib/utils/reify-finish.js9
-rw-r--r--lib/utils/reify-output.js13
-rw-r--r--lib/utils/usage.js4
-rw-r--r--lib/version.js127
-rw-r--r--lib/view.js636
-rw-r--r--lib/whoami.js31
-rw-r--r--tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js28
-rw-r--r--test/lib/access.js157
-rw-r--r--test/lib/adduser.js67
-rw-r--r--test/lib/audit.js68
-rw-r--r--test/lib/auth/legacy.js40
-rw-r--r--test/lib/auth/oauth.js20
-rw-r--r--test/lib/auth/saml.js20
-rw-r--r--test/lib/auth/sso.js23
-rw-r--r--test/lib/bin.js20
-rw-r--r--test/lib/birthday.js8
-rw-r--r--test/lib/bugs.js11
-rw-r--r--test/lib/cache.js25
-rw-r--r--test/lib/ci.js111
-rw-r--r--test/lib/completion.js44
-rw-r--r--test/lib/config.js60
-rw-r--r--test/lib/dedupe.js56
-rw-r--r--test/lib/deprecate.js24
-rw-r--r--test/lib/diff.js109
-rw-r--r--test/lib/dist-tag.js49
-rw-r--r--test/lib/docs.js11
-rw-r--r--test/lib/doctor.js40
-rw-r--r--test/lib/edit.js38
-rw-r--r--test/lib/exec.js673
-rw-r--r--test/lib/explain.js140
-rw-r--r--test/lib/explore.js97
-rw-r--r--test/lib/find-dupes.js27
-rw-r--r--test/lib/fund.js77
-rw-r--r--test/lib/get.js17
-rw-r--r--test/lib/help-search.js22
-rw-r--r--test/lib/help.js41
-rw-r--r--test/lib/hook.js48
-rw-r--r--test/lib/init.js34
-rw-r--r--test/lib/install-ci-test.js57
-rw-r--r--test/lib/install-test.js57
-rw-r--r--test/lib/install.js94
-rw-r--r--test/lib/link.js23
-rw-r--r--test/lib/ll.js51
-rw-r--r--test/lib/load-all-commands.js6
-rw-r--r--test/lib/logout.js20
-rw-r--r--test/lib/ls.js244
-rw-r--r--test/lib/npm.js10
-rw-r--r--test/lib/org.js50
-rw-r--r--test/lib/outdated.js53
-rw-r--r--test/lib/owner.js56
-rw-r--r--test/lib/pack.js76
-rw-r--r--test/lib/ping.js18
-rw-r--r--test/lib/prefix.js6
-rw-r--r--test/lib/profile.js174
-rw-r--r--test/lib/prune.js16
-rw-r--r--test/lib/publish.js302
-rw-r--r--test/lib/rebuild.js16
-rw-r--r--test/lib/repo.js11
-rw-r--r--test/lib/restart.js17
-rw-r--r--test/lib/root.js6
-rw-r--r--test/lib/run-script.js444
-rw-r--r--test/lib/search.js26
-rw-r--r--test/lib/set-script.js56
-rw-r--r--test/lib/set.js17
-rw-r--r--test/lib/shrinkwrap.js42
-rw-r--r--test/lib/star.js19
-rw-r--r--test/lib/stars.js14
-rw-r--r--test/lib/start.js17
-rw-r--r--test/lib/stop.js17
-rw-r--r--test/lib/team.js80
-rw-r--r--test/lib/test.js19
-rw-r--r--test/lib/token.js70
-rw-r--r--test/lib/uninstall.js19
-rw-r--r--test/lib/unpublish.js92
-rw-r--r--test/lib/unstar.js34
-rw-r--r--test/lib/update.js25
-rw-r--r--test/lib/utils/audit-error.js9
-rw-r--r--test/lib/utils/completion/installed-deep.js35
-rw-r--r--test/lib/utils/completion/installed-shallow.js10
-rw-r--r--test/lib/utils/get-identity.js57
-rw-r--r--test/lib/utils/lifecycle-cmd.js14
-rw-r--r--test/lib/utils/npm-usage.js206
-rw-r--r--test/lib/utils/open-url.js129
-rw-r--r--test/lib/utils/read-local-package.js15
-rw-r--r--test/lib/utils/reify-finish.js9
-rw-r--r--test/lib/utils/reify-output.js23
-rw-r--r--test/lib/version.js21
-rw-r--r--test/lib/view.js209
-rw-r--r--test/lib/whoami.js12
165 files changed, 8445 insertions, 7469 deletions
diff --git a/lib/access.js b/lib/access.js
index 3c4ed5906..e11934af4 100644
--- a/lib/access.js
+++ b/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(
- '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/lib/adduser.js b/lib/adduser.js
index c68c2b80f..dac0f5a46 100644
--- a/lib/adduser.js
+++ b/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/lib/audit.js b/lib/audit.js
index 1b31401b1..dfa01cb27 100644
--- a/lib/audit.js
+++ b/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/lib/auth/legacy.js b/lib/auth/legacy.js
index f291ca794..8659446dc 100644
--- a/lib/auth/legacy.js
+++ b/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/lib/auth/oauth.js b/lib/auth/oauth.js
index ee4531711..99c2ca0ca 100644
--- a/lib/auth/oauth.js
+++ b/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/lib/auth/saml.js b/lib/auth/saml.js
index f30d82849..3dd31ca01 100644
--- a/lib/auth/saml.js
+++ b/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/lib/auth/sso.js b/lib/auth/sso.js
index 378295f5f..ca8c50168 100644
--- a/lib/auth/sso.js
+++ b/lib/auth/sso.js
@@ -13,7 +13,6 @@ 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 otplease = require('../utils/otplease.js')
@@ -38,7 +37,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 +64,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/lib/bin.js b/lib/bin.js
index e627ce22f..11490c41c 100644
--- a/lib/bin.js
+++ b/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/lib/birthday.js b/lib/birthday.js
index 6c71a9e71..5ea855512 100644
--- a/lib/birthday.js
+++ b/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/lib/bugs.js b/lib/bugs.js
index 09856313c..fb0d7c927 100644
--- a/lib/bugs.js
+++ b/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/lib/cache.js b/lib/cache.js
index 7b84353b4..846955976 100644
--- a/lib/cache.js
+++ b/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/lib/ci.js b/lib/ci.js
index 51c165acc..03a91a604 100644
--- a/lib/ci.js
+++ b/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/lib/completion.js b/lib/completion.js
index b31867d98..4c37e6ef3 100644
--- a/lib/completion.js
+++ b/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/lib/config.js b/lib/config.js
index e4da296de..2805db9b8 100644
--- a/lib/config.js
+++ b/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/lib/dedupe.js b/lib/dedupe.js
index 2211fcac8..59978895e 100644
--- a/lib/dedupe.js
+++ b/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/lib/deprecate.js b/lib/deprecate.js
index 42d099b54..48f27ab6c 100644
--- a/lib/deprecate.js
+++ b/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/lib/diff.js b/lib/diff.js
index 9ef5a78a2..ea0340a49 100644
--- a/lib/diff.js
+++ b/lib/diff.js
@@ -8,258 +8,270 @@ 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)
-}
-
-const retrieveSpecs = ([a, b]) => {
- // no arguments, defaults to comparing cwd
- // to its latest published registry version
- if (!a)
- return defaultSpec()
+ get where () {
+ const globalTop = resolve(this.npm.globalDir, '..')
+ const { global } = this.npm.flatOptions
+ return global ? globalTop : this.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 transformSingleSpec(a)
+ exec (args, cb) {
+ this.diff(args).then(() => cb()).catch(cb)
+ }
- return convertVersionsToSpecs([a, b])
- .then(findVersionsByPackageName)
-}
+ 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}`
+ )
+ }
-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 [a, b] = await this.retrieveSpecs(specs)
+ npmlog.info('diff', { src: a, dst: b })
- if (!pkgName || noPackageJson) {
- throw new Error(
- 'Needs multiple arguments to compare or run from a project dir.\n\n' +
- `Usage:\n${usage}`
+ const res = await libdiff(
+ [a, b],
+ { ...this.npm.flatOptions, diffFiles: args }
)
+ return output(res)
}
- return [
- `${pkgName}@${npm.flatOptions.defaultTag}`,
- `file:${npm.prefix}`,
- ]
-}
+ async retrieveSpecs ([a, b]) {
+ // no arguments, defaults to comparing cwd
+ // to its latest published registry version
+ if (!a)
+ return this.defaultSpec()
-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 missingPackageJson = new Error(
- 'Needs multiple arguments to compare or run from a project dir.\n\n' +
- `Usage:\n${usage}`
- )
+ // 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 specSelf = () => {
- if (noPackageJson)
- throw missingPackageJson
-
- return `file:${npm.prefix}`
+ const specs = await this.convertVersionsToSpecs([a, b])
+ return this.findVersionsByPackageName(specs)
}
- // 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
+ 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
+ }
+
+ 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/lib/dist-tag.js b/lib/dist-tag.js
index e958bb754..171a88c52 100644
--- a/lib/dist-tag.js
+++ b/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/lib/docs.js b/lib/docs.js
index fa0adb3d3..2dad7a26d 100644
--- a/lib/docs.js
+++ b/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/lib/doctor.js b/lib/doctor.js
index e149aec12..81860004e 100644
--- a/lib/doctor.js
+++ b/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/lib/edit.js b/lib/edit.js
index 9ae634926..a7dbb3820 100644
--- a/lib/edit.js
+++ b/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/lib/exec.js b/lib/exec.js
index dab65c23a..d1db49128 100644
--- a/lib/exec.js
+++ b/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/lib/explain.js b/lib/explain.js
index a0a4427bc..01541040e 100644
--- a/lib/explain.js
+++ b/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/lib/explore.js b/lib/explore.js
index e9b09707e..fdfe6e1bc 100644
--- a/lib/explore.js
+++ b/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/lib/find-dupes.js b/lib/find-dupes.js
index 19e7ea6a7..5061be9cc 100644
--- a/lib/find-dupes.js
+++ b/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/lib/fund.js b/lib/fund.js
index 41dd48c46..1e9724266 100644
--- a/lib/fund.js
+++ b/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/lib/get.js b/lib/get.js
index 8a416027d..a5b2f5514 100644
--- a/lib/get.js
+++ b/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/lib/help-search.js b/lib/help-search.js
index b18473504..ed2bc23b9 100644
--- a/lib/help-search.js
+++ b/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/lib/help.js b/lib/help.js
index 6f215c76c..d7897326f 100644
--- a/lib/help.js
+++ b/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/lib/hook.js b/lib/hook.js
index 7d69ccbf2..312f542d7 100644
--- a/lib/hook.js
+++ b/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/lib/init.js b/lib/init.js
index a029779f8..af97a9614 100644
--- a/lib/init.js
+++ b/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/lib/install-ci-test.js b/lib/install-ci-test.js
index 52c41c413..d1740999d 100644
--- a/lib/install-ci-test.js
+++ b/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/lib/install-test.js b/lib/install-test.js
index 9593361e3..487f8da00 100644
--- a/lib/install-test.js
+++ b/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/lib/install.js b/lib/install.js
index 5f0137db1..d7fd384d5 100644
--- a/lib/install.js
+++ b/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/lib/link.js b/lib/link.js
index 0bb3d87b5..6d5e20710 100644
--- a/lib/link.js
+++ b/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/lib/ll.js b/lib/ll.js
index 1d5a6217d..7915f5d27 100644
--- a/lib/ll.js
+++ b/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/lib/logout.js b/lib/logout.js
index d2762c1ba..9fb1eab21 100644
--- a/lib/logout.js
+++ b/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/lib/ls.js b/lib/ls.js
index d9c06de87..359fe21e6 100644
--- a/lib/ls.js
+++ b/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 }) => {
- 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)
@@ -358,137 +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,
- })
-
- 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 })
- )
-
- // 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/lib/npm.js b/lib/npm.js
index 949e6309e..1f8c785e7 100644
--- a/lib/npm.js
+++ b/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)
})
diff --git a/lib/org.js b/lib/org.js
index aa9c97d49..054e1833d 100644
--- a/lib/org.js
+++ b/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/lib/outdated.js b/lib/outdated.js
index c10f63a12..fc6967faf 100644
--- a/lib/outdated.js
+++ b/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/lib/owner.js b/lib/owner.js
index 6dce3ec70..6cb990488 100644
--- a/lib/owner.js
+++ b/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/lib/pack.js b/lib/pack.js
index ff906cc2b..cf1e77f48 100644
--- a/lib/pack.js
+++ b/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/lib/ping.js b/lib/ping.js
index efa226310..e43f0640f 100644
--- a/lib/ping.js
+++ b/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/lib/prefix.js b/lib/prefix.js
index d108b9d42..e46f9c4cd 100644
--- a/lib/prefix.js
+++ b/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/lib/profile.js b/lib/profile.js
index f5db75eb4..dab99092b 100644
--- a/lib/profile.js
+++ b/lib/profile.js
@@ -6,72 +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(
- '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 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',
@@ -86,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',
@@ -154,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/lib/prune.js b/lib/prune.js
index 228fd3eeb..b839301d5 100644
--- a/lib/prune.js
+++ b/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/lib/publish.js b/lib/publish.js
index 5ec66d42f..c8e82c44c 100644
--- a/lib/publish.js
+++ b/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/lib/rebuild.js b/lib/rebuild.js
index ab34b7f3d..1091b0158 100644
--- a/lib/rebuild.js
+++ b/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/lib/repo.js b/lib/repo.js
index e9074dca6..60fe6dbaf 100644
--- a/lib/repo.js
+++ b/lib/repo.js
@@ -1,52 +1,64 @@
const log = require('npmlog')
const pacote = require('pacote')
+const { URL } = require('url')
const { promisify } = require('util')
+
+const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js')
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> ...]]')
+class Repo {
+ constructor (npm) {
+ this.npm = npm
+ }
-const cmd = (args, cb) => repo(args).then(() => cb()).catch(cb)
+ /* istanbul ignore next - see test/lib/load-all-commands.js */
+ get usage () {
+ return usageUtil('repo', 'npm repo [<pkgname> [<pkgname> ...]]')
+ }
-const repo = async args => {
- if (!args || !args.length)
- args = ['.']
+ exec (args, cb) {
+ this.repo(args).then(() => cb()).catch(cb)
+ }
- await Promise.all(args.map(pkg => getRepo(pkg)))
-}
+ async repo (args) {
+ if (!args || !args.length)
+ args = ['.']
-const getRepo = async pkg => {
- const opts = { ...npm.flatOptions, fullMetadata: true }
- const mani = await pacote.manifest(pkg, opts)
+ await Promise.all(args.map(pkg => this.get(pkg)))
+ }
- const r = mani.repository
- const rurl = !r ? null
- : typeof r === 'string' ? r
- : typeof r === 'object' && typeof r.url === 'string' ? r.url
- : null
+ async get (pkg) {
+ const opts = { ...this.npm.flatOptions, fullMetadata: true }
+ const mani = await pacote.manifest(pkg, opts)
- if (!rurl) {
- throw Object.assign(new Error('no repository'), {
- pkgid: pkg,
- })
- }
+ const r = mani.repository
+ const rurl = !r ? null
+ : typeof r === 'string' ? r
+ : typeof r === 'object' && typeof r.url === 'string' ? r.url
+ : null
- const info = hostedFromMani(mani)
- const url = info ?
- info.browse(mani.repository.directory) : unknownHostedUrl(rurl)
+ if (!rurl) {
+ throw Object.assign(new Error('no repository'), {
+ pkgid: pkg,
+ })
+ }
- if (!url) {
- throw Object.assign(new Error('no repository: could not get url'), {
- 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 +79,3 @@ const unknownHostedUrl = url => {
return null
}
}
-
-module.exports = Object.assign(cmd, { usage })
diff --git a/lib/restart.js b/lib/restart.js
index 1462cf605..d5a7789ca 100644
--- a/lib/restart.js
+++ b/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/lib/root.js b/lib/root.js
index 631aef838..8e5ac63d7 100644
--- a/lib/root.js
+++ b/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/lib/run-script.js b/lib/run-script.js
index 4dfb854ca..cdfd88f10 100644
--- a/lib/run-script.js
+++ b/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/lib/search.js b/lib/search.js
index 3f8fd99fb..ba6c28163 100644
--- a/lib/search.js
+++ b/lib/search.js
@@ -5,68 +5,9 @@ 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) {
return args
.map(s => s.toLowerCase())
@@ -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, this.npm.flatOptions.search.opts),
+ 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/lib/set-script.js b/lib/set-script.js
index 7bac6eca5..25545898e 100644
--- a/lib/set-script.js
+++ b/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/lib/set.js b/lib/set.js
index 3d61c1e93..cbce1547e 100644
--- a/lib/set.js
+++ b/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/lib/shrinkwrap.js b/lib/shrinkwrap.js
index 8768f35b5..a7516131d 100644
--- a/lib/shrinkwrap.js
+++ b/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/lib/star.js b/lib/star.js
index c02fdeed8..b39d23b2c 100644
--- a/lib/star.js
+++ b/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/lib/stars.js b/lib/stars.js
index d1c2b73fb..fe280705b 100644
--- a/lib/stars.js
+++ b/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/lib/start.js b/lib/start.js
index 9fa076d5e..3d46a3a7b 100644
--- a/lib/start.js
+++ b/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/lib/stop.js b/lib/stop.js
index 827d414d1..d7df5887e 100644
--- a/lib/stop.js
+++ b/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/lib/team.js b/lib/team.js
index 24e8360b6..4947739a0 100644
--- a/lib/team.js
+++ b/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/lib/test.js b/lib/test.js
index ea5914ea3..12292d292 100644
--- a/lib/test.js
+++ b/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/lib/token.js b/lib/token.js
index 1e78584c7..ad6d5c6fc 100644
--- a/lib/token.js
+++ b/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/lib/uninstall.js b/lib/uninstall.js
index 15995c0b3..d7116e4c2 100644
--- a/lib/uninstall.js
+++ b/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/lib/unpublish.js b/lib/unpublish.js
index bb931682b..34751da4a 100644
--- a/lib/unpublish.js
+++ b/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/lib/unstar.js b/lib/unstar.js
index 554bd60bc..c814ef2b6 100644
--- a/lib/unstar.js
+++ b/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/lib/update.js b/lib/update.js
index 6eaa085b8..98043e09c 100644
--- a/lib/update.js
+++ b/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/lib/utils/audit-error.js b/lib/utils/audit-error.js
index c7423c447..ae0749ff6 100644
--- a/lib/utils/audit-error.js
+++ b/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/lib/utils/completion/installed-deep.js b/lib/utils/completion/installed-deep.js
index f464bb9a9..b65c17e41 100644
--- a/lib/utils/completion/installed-deep.js
+++ b/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/lib/utils/completion/installed-shallow.js b/lib/utils/completion/installed-shallow.js
index c9c680e7d..1c9b8ef5a 100644
--- a/lib/utils/completion/installed-shallow.js
+++ b/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/lib/utils/get-identity.js b/lib/utils/get-identity.js
index d5c560161..e92a2c524 100644
--- a/lib/utils/get-identity.js
+++ b/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/lib/utils/lifecycle-cmd.js b/lib/utils/lifecycle-cmd.js
index 94b109942..8be9b5a12 100644
--- a/lib/utils/lifecycle-cmd.js
+++ b/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/lib/utils/npm-usage.js b/lib/utils/npm-usage.js
index d4261f79d..220f8037f 100644
--- a/lib/utils/npm-usage.js
+++ b/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/lib/utils/open-url.js b/lib/utils/open-url.js
index 28c2d038a..1fe456bd0 100644
--- a/lib/utils/open-url.js
+++ b/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/lib/utils/read-local-package.js b/lib/utils/read-local-package.js
index 7ab130c1f..c31bca994 100644
--- a/lib/utils/read-local-package.js
+++ b/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/lib/utils/reify-finish.js b/lib/utils/reify-finish.js
index 9c95e9fcf..1c02b93a4 100644
--- a/lib/utils/reify-finish.js
+++ b/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/lib/utils/reify-output.js b/lib/utils/reify-output.js
index 4abaadc2e..216f0e902 100644
--- a/lib/utils/reify-output.js
+++ b/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/lib/utils/usage.js b/lib/utils/usage.js
index ddcbd708b..5f4eca73e 100644
--- a/lib/utils/usage.js
+++ b/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/lib/version.js b/lib/version.js
index 9d87b2f2c..1ba834f5d 100644
--- a/lib/version.js
+++ b/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/lib/view.js b/lib/view.js
index d6d79d35c..d0d5fa59d 100644
--- a/lib/view.js
+++ b/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/lib/whoami.js b/lib/whoami.js
index eac592383..39184ed9c 100644
--- a/lib/whoami.js
+++ b/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().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 () => {
- const opts = npm.flatOptions
- const username = await getIdentity(opts)
- 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
diff --git a/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js b/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
index aced03ecf..8e1603eb4 100644
--- a/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
+++ b/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js
@@ -5,7 +5,7 @@
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
-exports[`test/lib/utils/npm-usage.js TAP basic usage > must match snapshot 1`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage basic usage > must match snapshot 1`] = `
Usage: npm <command>
@@ -41,7 +41,7 @@ npm@{VERSION} {BASEDIR}
`
-exports[`test/lib/utils/npm-usage.js TAP did you mean? > must match snapshot 1`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage did you mean? > must match snapshot 1`] = `
Usage: npm <command>
@@ -77,14 +77,14 @@ npm@{VERSION} {BASEDIR}
`
-exports[`test/lib/utils/npm-usage.js TAP did you mean? > must match snapshot 2`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage did you mean? > must match snapshot 2`] = `
Did you mean one of these?
install
uninstall
`
-exports[`test/lib/utils/npm-usage.js TAP set process.stdout.columns columns=0 > must match snapshot 1`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage set process.stdout.columns columns=0 > must match snapshot 1`] = `
Usage: npm <command>
@@ -120,7 +120,7 @@ npm@{VERSION} {BASEDIR}
`
-exports[`test/lib/utils/npm-usage.js TAP set process.stdout.columns columns=90 > must match snapshot 1`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage set process.stdout.columns columns=90 > must match snapshot 1`] = `
Usage: npm <command>
@@ -156,7 +156,7 @@ npm@{VERSION} {BASEDIR}
`
-exports[`test/lib/utils/npm-usage.js TAP with browser > must match snapshot 1`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage with browser > must match snapshot 1`] = `
Usage: npm <command>
@@ -192,7 +192,7 @@ npm@{VERSION} {BASEDIR}
`
-exports[`test/lib/utils/npm-usage.js TAP with long > must match snapshot 1`] = `
+exports[`test/lib/utils/npm-usage.js TAP usage with long > must match snapshot 1`] = `
Usage: npm <command>
@@ -360,9 +360,9 @@ All commands:
alias: ln
- ll npm ls [[<@scope>/]<pkg> ...]
+ ll npm ll [[<@scope>/]<pkg> ...]
- alias: list
+ alias: la
login npm adduser [--registry=url] [--scope=@orgname] [--always-auth]
@@ -377,6 +377,8 @@ All commands:
org npm org set orgname username [developer | admin | owner]
npm org rm orgname username
npm org ls orgname [<username>]
+
+ alias: ogr
outdated npm outdated [[<@scope>/]<pkg> ...]
@@ -394,9 +396,10 @@ All commands:
prefix npm prefix [-g]
profile npm profile enable-2fa [auth-only|auth-and-writes]
- npm profile disable-2fa
- npm profile get [<key>]
- npm profile set <key> <value>
+
+
+ common options: npm profile disable-2fa
+
prune npm prune [[<@scope>/]<pkg>...] [--production]
@@ -473,6 +476,7 @@ All commands:
'npm view <pkg> version' to view a package's published version
'npm ls' to inspect current package/dependency versions
+
alias: verison
view npm view [<@scope>/]<pkg>[@<version>] [<field>[.subfield]...]
diff --git a/test/lib/access.js b/test/lib/access.js
index fb799f2df..3a732ad0a 100644
--- a/test/lib/access.js
+++ b/test/lib/access.js
@@ -1,17 +1,12 @@
const { test } = require('tap')
const requireInject = require('require-inject')
-const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': {
- flatOptions: {},
- },
-})
+const Access = require('../../lib/access.js')
test('completion', t => {
- const { completion } = access
-
+ const access = new Access({ flatOptions: {} })
const testComp = (argv, expect) => {
- const res = completion({conf: {argv: {remain: argv}}})
+ const res = access.completion({conf: {argv: {remain: argv}}})
t.resolves(res, expect, argv.join(' '))
}
@@ -32,7 +27,7 @@ test('completion', t => {
testComp(['npm', 'access', 'revoke'], [])
t.rejects(
- completion({conf: {argv: {remain: ['npm', 'access', 'foobar']}}}),
+ access.completion({conf: {argv: {remain: ['npm', 'access', 'foobar']}}}),
{ message: 'foobar not recognized' }
)
@@ -40,14 +35,16 @@ test('completion', t => {
})
test('subcommand required', t => {
- access([], (err) => {
+ const access = new Access({ flatOptions: {} })
+ access.exec([], (err) => {
t.equal(err, '\nUsage: Subcommand is required.\n\n' + access.usage)
t.end()
})
})
test('unrecognized subcommand', (t) => {
- access(['blerg'], (err) => {
+ const access = new Access({ flatOptions: {} })
+ access.exec(['blerg'], (err) => {
t.match(
err,
/Usage: blerg is not a recognized subcommand/,
@@ -58,7 +55,8 @@ test('unrecognized subcommand', (t) => {
})
test('edit', (t) => {
- access([
+ const access = new Access({ flatOptions: {} })
+ access.exec([
'edit',
'@scoped/another',
], (err) => {
@@ -77,10 +75,8 @@ test('access public on unscoped package', (t) => {
name: 'npm-access-public-pkg',
}),
})
- const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': { prefix },
- })
- access([
+ const access = new Access({ prefix })
+ access.exec([
'public',
], (err) => {
t.match(
@@ -98,7 +94,7 @@ test('access public on scoped package', (t) => {
const prefix = t.testdir({
'package.json': JSON.stringify({ name }),
})
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
public: (pkg, { registry }) => {
t.equal(pkg, name, 'should use pkg name ref')
@@ -110,14 +106,12 @@ test('access public on scoped package', (t) => {
return true
},
},
- '../../lib/npm.js': {
- flatOptions: {
- registry: 'https://registry.npmjs.org',
- },
- prefix,
- },
})
- access([
+ const access = new Access({
+ flatOptions: { registry: 'https://registry.npmjs.org' },
+ prefix,
+ })
+ access.exec([
'public',
], (err) => {
t.ifError(err, 'npm access')
@@ -129,10 +123,8 @@ test('access public on missing package.json', (t) => {
const prefix = t.testdir({
node_modules: {},
})
- const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': { prefix },
- })
- access([
+ const access = new Access({ prefix })
+ access.exec([
'public',
], (err) => {
t.match(
@@ -149,10 +141,8 @@ test('access public on invalid package.json', (t) => {
'package.json': '{\n',
node_modules: {},
})
- const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': { prefix },
- })
- access([
+ const access = new Access({ prefix })
+ access.exec([
'public',
], (err) => {
t.match(
@@ -170,10 +160,8 @@ test('access restricted on unscoped package', (t) => {
name: 'npm-access-restricted-pkg',
}),
})
- const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': { prefix },
- })
- access([
+ const access = new Access({ prefix })
+ access.exec([
'restricted',
], (err) => {
t.match(
@@ -191,7 +179,7 @@ test('access restricted on scoped package', (t) => {
const prefix = t.testdir({
'package.json': JSON.stringify({ name }),
})
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
restricted: (pkg, { registry }) => {
t.equal(pkg, name, 'should use pkg name ref')
@@ -203,14 +191,12 @@ test('access restricted on scoped package', (t) => {
return true
},
},
- '../../lib/npm.js': {
- flatOptions: {
- registry: 'https://registry.npmjs.org',
- },
- prefix,
- },
})
- access([
+ const access = new Access({
+ flatOptions: { registry: 'https://registry.npmjs.org' },
+ prefix,
+ })
+ access.exec([
'restricted',
], (err) => {
t.ifError(err, 'npm access')
@@ -222,10 +208,8 @@ test('access restricted on missing package.json', (t) => {
const prefix = t.testdir({
node_modules: {},
})
- const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': { prefix },
- })
- access([
+ const access = new Access({ prefix })
+ access.exec([
'restricted',
], (err) => {
t.match(
@@ -242,10 +226,8 @@ test('access restricted on invalid package.json', (t) => {
'package.json': '{\n',
node_modules: {},
})
- const access = requireInject('../../lib/access.js', {
- '../../lib/npm.js': { prefix },
- })
- access([
+ const access = new Access({ prefix })
+ access.exec([
'restricted',
], (err) => {
t.match(
@@ -259,7 +241,7 @@ test('access restricted on invalid package.json', (t) => {
test('access grant read-only', (t) => {
t.plan(5)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
grant: (spec, team, permissions) => {
t.equal(spec, '@scoped/another', 'should use expected spec')
@@ -268,9 +250,9 @@ test('access grant read-only', (t) => {
return true
},
},
- '../../lib/npm.js': {},
})
- access([
+ const access = new Access({})
+ access.exec([
'grant',
'read-only',
'myorg:myteam',
@@ -283,7 +265,7 @@ test('access grant read-only', (t) => {
test('access grant read-write', (t) => {
t.plan(5)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
grant: (spec, team, permissions) => {
t.equal(spec, '@scoped/another', 'should use expected spec')
@@ -292,9 +274,9 @@ test('access grant read-write', (t) => {
return true
},
},
- '../../lib/npm.js': {},
})
- access([
+ const access = new Access({})
+ access.exec([
'grant',
'read-write',
'myorg:myteam',
@@ -312,7 +294,7 @@ test('access grant current cwd', (t) => {
name: 'yargs',
}),
})
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
grant: (spec, team, permissions) => {
t.equal(spec, 'yargs', 'should use expected spec')
@@ -321,9 +303,9 @@ test('access grant current cwd', (t) => {
return true
},
},
- '../../lib/npm.js': { prefix },
})
- access([
+ const access = new Access({ prefix })
+ access.exec([
'grant',
'read-write',
'myorg:myteam',
@@ -334,7 +316,8 @@ test('access grant current cwd', (t) => {
})
test('access grant others', (t) => {
- access([
+ const access = new Access({ flatOptions: {} })
+ access.exec([
'grant',
'rerere',
'myorg:myteam',
@@ -350,7 +333,8 @@ test('access grant others', (t) => {
})
test('access grant missing team args', (t) => {
- access([
+ const access = new Access({ flatOptions: {} })
+ access.exec([
'grant',
'read-only',
undefined,
@@ -366,7 +350,8 @@ test('access grant missing team args', (t) => {
})
test('access grant malformed team arg', (t) => {
- access([
+ const access = new Access({ flatOptions: {} })
+ access.exec([
'grant',
'read-only',
'foo',
@@ -383,7 +368,7 @@ test('access grant malformed team arg', (t) => {
test('access 2fa-required/2fa-not-required', t => {
t.plan(2)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
tfaRequired: (spec) => {
t.equal(spec, '@scope/pkg', 'should use expected spec')
@@ -394,15 +379,15 @@ test('access 2fa-required/2fa-not-required', t => {
return true
},
},
- '../../lib/npm.js': {},
})
+ const access = new Access({})
- access(['2fa-required', '@scope/pkg'], er => {
+ access.exec(['2fa-required', '@scope/pkg'], er => {
if (er)
throw er
})
- access(['2fa-not-required', 'unscoped-pkg'], er => {
+ access.exec(['2fa-not-required', 'unscoped-pkg'], er => {
if (er)
throw er
})
@@ -410,7 +395,7 @@ test('access 2fa-required/2fa-not-required', t => {
test('access revoke', (t) => {
t.plan(4)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
revoke: (spec, team) => {
t.equal(spec, '@scoped/another', 'should use expected spec')
@@ -418,9 +403,9 @@ test('access revoke', (t) => {
return true
},
},
- '../../lib/npm.js': {},
})
- access([
+ const access = new Access({})
+ access.exec([
'revoke',
'myorg:myteam',
'@scoped/another',
@@ -431,7 +416,8 @@ test('access revoke', (t) => {
})
test('access revoke missing team args', (t) => {
- access([
+ const access = new Access({ flatOptions: {} })
+ access.exec([
'revoke',
undefined,
'@scoped/another',
@@ -446,7 +432,8 @@ test('access revoke missing team args', (t) => {
})
test('access revoke malformed team arg', (t) => {
- access([
+ const access = new Access({ flatOptions: {} })
+ access.exec([
'revoke',
'foo',
'@scoped/another',
@@ -462,7 +449,7 @@ test('access revoke malformed team arg', (t) => {
test('npm access ls-packages with no team', (t) => {
t.plan(3)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
lsPackages: (entity) => {
t.equal(entity, 'foo', 'should use expected entity')
@@ -471,9 +458,9 @@ test('npm access ls-packages with no team', (t) => {
},
'../../lib/utils/get-identity.js': () => Promise.resolve('foo'),
'../../lib/utils/output.js': () => null,
- '../../lib/npm.js': {},
})
- access([
+ const access = new Access({})
+ access.exec([
'ls-packages',
], (err) => {
t.ifError(err, 'npm access')
@@ -483,7 +470,7 @@ test('npm access ls-packages with no team', (t) => {
test('access ls-packages on team', (t) => {
t.plan(3)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
lsPackages: (entity) => {
t.equal(entity, 'myorg:myteam', 'should use expected entity')
@@ -491,9 +478,9 @@ test('access ls-packages on team', (t) => {
},
},
'../../lib/utils/output.js': () => null,
- '../../lib/npm.js': {},
})
- access([
+ const access = new Access({})
+ access.exec([
'ls-packages',
'myorg:myteam',
], (err) => {
@@ -509,7 +496,7 @@ test('access ls-collaborators on current', (t) => {
name: 'yargs',
}),
})
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
lsCollaborators: (spec) => {
t.equal(spec, 'yargs', 'should use expected spec')
@@ -517,9 +504,9 @@ test('access ls-collaborators on current', (t) => {
},
},
'../../lib/utils/output.js': () => null,
- '../../lib/npm.js': { prefix },
})
- access([
+ const access = new Access({ prefix })
+ access.exec([
'ls-collaborators',
], (err) => {
t.ifError(err, 'npm access')
@@ -529,7 +516,7 @@ test('access ls-collaborators on current', (t) => {
test('access ls-collaborators on spec', (t) => {
t.plan(3)
- const access = requireInject('../../lib/access.js', {
+ const Access = requireInject('../../lib/access.js', {
libnpmaccess: {
lsCollaborators: (spec) => {
t.equal(spec, 'yargs', 'should use expected spec')
@@ -537,9 +524,9 @@ test('access ls-collaborators on spec', (t) => {
},
},
'../../lib/utils/output.js': () => null,
- '../../lib/npm.js': {},
})
- access([
+ const access = new Access({})
+ access.exec([
'ls-collaborators',
'yargs',
], (err) => {
diff --git a/test/lib/adduser.js b/test/lib/adduser.js
index 36f59e085..32fd97c1b 100644
--- a/test/lib/adduser.js
+++ b/test/lib/adduser.js
@@ -16,7 +16,7 @@ let failSave = false
let deletedConfig = {}
let registryOutput = ''
let setConfig = {}
-const authDummy = (options) => {
+const authDummy = (npm, options) => {
if (!options.fromFlatOptions)
throw new Error('did not pass full flatOptions to auth function')
@@ -37,46 +37,49 @@ const deleteMock = (key, where) => {
[key]: where,
}
}
-const adduser = requireInject('../../lib/adduser.js', {
+const npm = {
+ flatOptions: _flatOptions,
+ config: {
+ delete: deleteMock,
+ get (key, where) {
+ if (!where || where === 'user')
+ return _flatOptions[key]
+ },
+ getCredentialsByURI,
+ async save () {
+ if (failSave)
+ throw new Error('error saving user config')
+ },
+ set (key, value, where) {
+ setConfig = {
+ ...setConfig,
+ [key]: {
+ value,
+ where,
+ },
+ }
+ },
+ setCredentialsByURI,
+ },
+}
+
+const AddUser = requireInject('../../lib/adduser.js', {
npmlog: {
disableProgress: () => null,
notice: (_, msg) => {
registryOutput = msg
},
},
- '../../lib/npm.js': {
- flatOptions: _flatOptions,
- config: {
- delete: deleteMock,
- get (key, where) {
- if (!where || where === 'user')
- return _flatOptions[key]
- },
- getCredentialsByURI,
- async save () {
- if (failSave)
- throw new Error('error saving user config')
- },
- set (key, value, where) {
- setConfig = {
- ...setConfig,
- [key]: {
- value,
- where,
- },
- }
- },
- setCredentialsByURI,
- },
- },
'../../lib/utils/output.js': msg => {
result = msg
},
'../../lib/auth/legacy.js': authDummy,
})
+const adduser = new AddUser(npm)
+
test('simple login', (t) => {
- adduser([], (err) => {
+ adduser.exec([], (err) => {
t.ifError(err, 'npm adduser')
t.equal(
@@ -129,7 +132,7 @@ test('simple login', (t) => {
test('bad auth type', (t) => {
_flatOptions.authType = 'foo'
- adduser([], (err) => {
+ adduser.exec([], (err) => {
t.match(
err,
/Error: no such auth module/,
@@ -147,7 +150,7 @@ test('bad auth type', (t) => {
test('scoped login', (t) => {
_flatOptions.scope = '@myscope'
- adduser([], (err) => {
+ adduser.exec([], (err) => {
t.ifError(err, 'npm adduser')
t.deepEqual(
@@ -168,7 +171,7 @@ test('scoped login with valid scoped registry config', (t) => {
_flatOptions['@myscope:registry'] = 'https://diff-registry.npmjs.com/'
_flatOptions.scope = '@myscope'
- adduser([], (err) => {
+ adduser.exec([], (err) => {
t.ifError(err, 'npm adduser')
t.deepEqual(
@@ -189,7 +192,7 @@ test('scoped login with valid scoped registry config', (t) => {
test('save config failure', (t) => {
failSave = true
- adduser([], (err) => {
+ adduser.exec([], (err) => {
t.match(
err,
/error saving user config/,
diff --git a/test/lib/audit.js b/test/lib/audit.js
index 3d6296bac..6fd9c8a2c 100644
--- a/test/lib/audit.js
+++ b/test/lib/audit.js
@@ -1,6 +1,5 @@
const t = require('tap')
const requireInject = require('require-inject')
-const audit = require('../../lib/audit.js')
t.test('should audit using Arborist', t => {
let ARB_ARGS = null
@@ -10,13 +9,13 @@ t.test('should audit using Arborist', t => {
let OUTPUT_CALLED = false
let ARB_OBJ = null
- const audit = requireInject('../../lib/audit.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- json: false,
- },
+ const npm = {
+ prefix: 'foo',
+ flatOptions: {
+ json: false,
},
+ }
+ const Audit = requireInject('../../lib/audit.js', {
'npm-audit-report': () => {
AUDIT_REPORT_CALLED = true
return {
@@ -32,7 +31,7 @@ t.test('should audit using Arborist', t => {
this.auditReport = {}
}
},
- '../../lib/utils/reify-finish.js': arb => {
+ '../../lib/utils/reify-finish.js': (npm, arb) => {
if (arb !== ARB_OBJ)
throw new Error('got wrong object passed to reify-output')
@@ -43,8 +42,10 @@ t.test('should audit using Arborist', t => {
},
})
+ const audit = new Audit(npm)
+
t.test('audit', t => {
- audit([], () => {
+ audit.exec([], () => {
t.match(ARB_ARGS, { audit: true, path: 'foo' })
t.equal(AUDIT_CALLED, true, 'called audit')
t.equal(AUDIT_REPORT_CALLED, true, 'called audit report')
@@ -54,7 +55,7 @@ t.test('should audit using Arborist', t => {
})
t.test('audit fix', t => {
- audit(['fix'], () => {
+ audit.exec(['fix'], () => {
t.equal(REIFY_FINISH_CALLED, true, 'called reify output')
t.end()
})
@@ -64,13 +65,14 @@ t.test('should audit using Arborist', t => {
})
t.test('should audit - json', t => {
- const audit = requireInject('../../lib/audit.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- json: true,
- },
+ const npm = {
+ prefix: 'foo',
+ flatOptions: {
+ json: true,
},
+ }
+
+ const Audit = requireInject('../../lib/audit.js', {
'npm-audit-report': () => ({
report: 'there are vulnerabilities',
exitCode: 0,
@@ -83,8 +85,9 @@ t.test('should audit - json', t => {
'../../lib/utils/reify-output.js': () => {},
'../../lib/utils/output.js': () => {},
})
+ const audit = new Audit(npm)
- audit([], (err) => {
+ audit.exec([], (err) => {
t.notOk(err, 'no errors')
t.end()
})
@@ -95,17 +98,17 @@ t.test('report endpoint error', t => {
t.test(`json=${json}`, t => {
const OUTPUT = []
const LOGS = []
- const mocks = {
- '../../lib/npm.js': {
- prefix: 'foo',
- command: 'audit',
- flatOptions: {
- json,
- },
- log: {
- warn: (...warning) => LOGS.push(warning),
- },
+ const npm = {
+ prefix: 'foo',
+ command: 'audit',
+ flatOptions: {
+ json,
},
+ log: {
+ warn: (...warning) => LOGS.push(warning),
+ },
+ }
+ const Audit = requireInject('../../lib/audit.js', {
'npm-audit-report': () => {
throw new Error('should not call audit report when there are errors')
},
@@ -130,15 +133,10 @@ t.test('report endpoint error', t => {
'../../lib/utils/output.js': (...msg) => {
OUTPUT.push(msg)
},
- }
- // have to pass mocks to both to get the npm and output set right
- const auditError = requireInject('../../lib/utils/audit-error.js', mocks)
- const audit = requireInject('../../lib/audit.js', {
- ...mocks,
- '../../lib/utils/audit-error.js': auditError,
})
+ const audit = new Audit(npm)
- audit([], (err) => {
+ audit.exec([], (err) => {
t.equal(err, 'audit endpoint returned an error')
t.strictSame(OUTPUT, [
[
@@ -168,6 +166,8 @@ t.test('report endpoint error', t => {
})
t.test('completion', t => {
+ const Audit = require('../../lib/audit.js')
+ const audit = new Audit({})
t.test('fix', async t => {
t.resolveMatch(audit.completion({ conf: { argv: { remain: ['npm', 'audit'] } } }), ['fix'], 'completes to fix')
t.end()
diff --git a/test/lib/auth/legacy.js b/test/lib/auth/legacy.js
index f926ae130..f5297c581 100644
--- a/test/lib/auth/legacy.js
+++ b/test/lib/auth/legacy.js
@@ -13,19 +13,19 @@ const legacy = requireInject('../../../lib/auth/legacy.js', {
},
},
'npm-profile': profile,
- '../../../lib/utils/open-url.js': (url, msg, cb) => {
- if (url)
- cb()
- else {
- cb(Object.assign(
- new Error('failed open url'),
- { code: 'ERROR' }
- ))
- }
+ '../../../lib/utils/open-url.js': (npm, url, msg) => {
+ if (!url)
+ throw Object.assign(new Error('failed open url'), { code: 'ERROR' })
},
'../../../lib/utils/read-user-info.js': read,
})
+const npm = {
+ config: {
+ get: () => null,
+ },
+}
+
test('login using username/password with token result', async (t) => {
profile.login = () => {
return { token }
@@ -34,7 +34,7 @@ test('login using username/password with token result', async (t) => {
const {
message,
newCreds,
- } = await legacy({
+ } = await legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -75,7 +75,7 @@ test('login using username/password with user info result', async (t) => {
const {
message,
newCreds,
- } = await legacy({
+ } = await legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -126,7 +126,7 @@ test('login otp requested', async (t) => {
const {
message,
newCreds,
- } = await legacy({
+ } = await legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -162,7 +162,7 @@ test('login missing basic credential info', async (t) => {
))
await t.rejects(
- legacy({
+ legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -196,7 +196,7 @@ test('create new user when user not found', async (t) => {
const {
message,
newCreds,
- } = await legacy({
+ } = await legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -246,7 +246,7 @@ test('prompts for user info if required', async (t) => {
const {
message,
newCreds,
- } = await legacy({
+ } = await legacy(npm, {
creds: {
alwaysAuth: true,
},
@@ -304,7 +304,7 @@ test('request otp when creating new user', async (t) => {
}
read.otp = () => Promise.resolve('1234')
- await legacy({
+ await legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -333,7 +333,7 @@ test('unknown error during user creation', async (t) => {
))
await t.rejects(
- legacy({
+ legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -358,7 +358,7 @@ test('open url error', async (t) => {
}
await t.rejects(
- legacy({
+ legacy(npm, {
creds: {
username: 'u',
password: 'p',
@@ -377,7 +377,7 @@ test('open url error', async (t) => {
test('login no credentials provided', async (t) => {
profile.login = () => ({ token })
- await legacy({
+ await legacy(npm, {
creds: {
username: undefined,
password: undefined,
@@ -401,7 +401,7 @@ test('login no credentials provided', async (t) => {
test('scoped login', async (t) => {
profile.login = () => ({ token })
- const { message } = await legacy({
+ const { message } = await legacy(npm, {
creds: {
username: 'u',
password: 'p',
diff --git a/test/lib/auth/oauth.js b/test/lib/auth/oauth.js
index 82d478b52..c2f4c3443 100644
--- a/test/lib/auth/oauth.js
+++ b/test/lib/auth/oauth.js
@@ -9,21 +9,21 @@ test('oauth login', (t) => {
scope: 'myscope',
}
+ const npm = {
+ config: {
+ set: (key, value) => {
+ t.equal(key, 'sso-type', 'should define sso-type')
+ t.equal(value, 'oauth', 'should set sso-type to oauth')
+ },
+ },
+ }
const oauth = requireInject('../../../lib/auth/oauth.js', {
- '../../../lib/auth/sso.js': (opts) => {
+ '../../../lib/auth/sso.js': (npm, opts) => {
t.equal(opts, oauthOpts, 'should forward opts')
},
- '../../../lib/npm.js': {
- config: {
- set: (key, value) => {
- t.equal(key, 'sso-type', 'should define sso-type')
- t.equal(value, 'oauth', 'should set sso-type to oauth')
- },
- },
- },
})
- oauth(oauthOpts)
+ oauth(npm, oauthOpts)
t.end()
})
diff --git a/test/lib/auth/saml.js b/test/lib/auth/saml.js
index 87fa6688b..b8c21f649 100644
--- a/test/lib/auth/saml.js
+++ b/test/lib/auth/saml.js
@@ -9,21 +9,21 @@ test('saml login', (t) => {
scope: 'myscope',
}
+ const npm = {
+ config: {
+ set: (key, value) => {
+ t.equal(key, 'sso-type', 'should define sso-type')
+ t.equal(value, 'saml', 'should set sso-type to saml')
+ },
+ },
+ }
const saml = requireInject('../../../lib/auth/saml.js', {
- '../../../lib/auth/sso.js': (opts) => {
+ '../../../lib/auth/sso.js': (npm, opts) => {
t.equal(opts, samlOpts, 'should forward opts')
},
- '../../../lib/npm.js': {
- config: {
- set: (key, value) => {
- t.equal(key, 'sso-type', 'should define sso-type')
- t.equal(value, 'saml', 'should set sso-type to saml')
- },
- },
- },
})
- saml(samlOpts)
+ saml(npm, samlOpts)
t.end()
})
diff --git a/test/lib/auth/sso.js b/test/lib/auth/sso.js
index 1fc04c64c..9d77e7c27 100644
--- a/test/lib/auth/sso.js
+++ b/test/lib/auth/sso.js
@@ -22,10 +22,7 @@ const sso = requireInject('../../../lib/auth/sso.js', {
},
'npm-profile': profile,
'npm-registry-fetch': npmFetch,
- '../../../lib/npm.js': {
- flatOptions: _flatOptions,
- },
- '../../../lib/utils/open-url.js': (url, msg, cb) => {
+ '../../../lib/utils/open-url.js': (npm, url, msg, cb) => {
if (url)
cb()
else {
@@ -47,11 +44,15 @@ const sso = requireInject('../../../lib/auth/sso.js', {
},
})
+const npm = {
+ flatOptions: _flatOptions,
+}
+
test('empty login', async (t) => {
_flatOptions.ssoType = false
await t.rejects(
- sso({}),
+ sso(npm, {}),
{ message: 'Missing option: sso-type' },
'should throw if no sso-type defined in flatOptions'
)
@@ -92,7 +93,7 @@ test('simple login', async (t) => {
const {
message,
newCreds,
- } = await sso({
+ } = await sso(npm, {
creds: {},
registry: 'https://registry.npmjs.org/',
scope: '',
@@ -157,7 +158,7 @@ test('polling retry', async (t) => {
))
}
- await sso({
+ await sso(npm, {
creds: {},
registry: 'https://registry.npmjs.org/',
scope: '',
@@ -177,7 +178,7 @@ test('polling error', async (t) => {
))
await t.rejects(
- sso({
+ sso(npm, {
creds: {},
registry: 'https://registry.npmjs.org/',
scope: '',
@@ -196,7 +197,7 @@ test('no token retrieved from loginCouch', async (t) => {
profile.loginCouch = () => ({})
await t.rejects(
- sso({
+ sso(npm, {
creds: {},
registry: 'https://registry.npmjs.org/',
scope: '',
@@ -214,7 +215,7 @@ test('no sso url retrieved from loginCouch', async (t) => {
profile.loginCouch = () => Promise.resolve({ token })
await t.rejects(
- sso({
+ sso(npm, {
creds: {},
registry: 'https://registry.npmjs.org/',
scope: '',
@@ -235,7 +236,7 @@ test('scoped login', async (t) => {
const {
message,
newCreds,
- } = await sso({
+ } = await sso(npm, {
creds: {},
registry: 'https://diff-registry.npmjs.org/',
scope: 'myscope',
diff --git a/test/lib/bin.js b/test/lib/bin.js
index c5ed2a91b..e96eb91af 100644
--- a/test/lib/bin.js
+++ b/test/lib/bin.js
@@ -5,14 +5,15 @@ test('bin', (t) => {
t.plan(3)
const dir = '/bin/dir'
- const bin = requireInject('../../lib/bin.js', {
- '../../lib/npm.js': { bin: dir, flatOptions: { global: false } },
+ const Bin = requireInject('../../lib/bin.js', {
'../../lib/utils/output.js': (output) => {
t.equal(output, dir, 'prints the correct directory')
},
})
- bin([], (err) => {
+ const bin = new Bin({ bin: dir, flatOptions: { global: false } })
+
+ bin.exec([], (err) => {
t.ifError(err, 'npm bin')
t.ok('should have printed directory')
})
@@ -30,15 +31,16 @@ test('bin -g', (t) => {
}
const dir = '/bin/dir'
- const bin = requireInject('../../lib/bin.js', {
- '../../lib/npm.js': { bin: dir, flatOptions: { global: true } },
+ const Bin = requireInject('../../lib/bin.js', {
'../../lib/utils/path.js': [dir],
'../../lib/utils/output.js': (output) => {
t.equal(output, dir, 'prints the correct directory')
},
})
- bin([], (err) => {
+ const bin = new Bin({ bin: dir, flatOptions: { global: true } })
+
+ bin.exec([], (err) => {
t.ifError(err, 'npm bin')
t.ok('should have printed directory')
})
@@ -56,15 +58,15 @@ test('bin -g (not in path)', (t) => {
}
const dir = '/bin/dir'
- const bin = requireInject('../../lib/bin.js', {
- '../../lib/npm.js': { bin: dir, flatOptions: { global: true } },
+ const Bin = requireInject('../../lib/bin.js', {
'../../lib/utils/path.js': ['/not/my/dir'],
'../../lib/utils/output.js': (output) => {
t.equal(output, dir, 'prints the correct directory')
},
})
+ const bin = new Bin({ bin: dir, flatOptions: { global: true } })
- bin([], (err) => {
+ bin.exec([], (err) => {
t.ifError(err, 'npm bin')
t.ok('should have printed directory')
})
diff --git a/test/lib/birthday.js b/test/lib/birthday.js
index 3b8110fc8..c818223fb 100644
--- a/test/lib/birthday.js
+++ b/test/lib/birthday.js
@@ -1,5 +1,4 @@
const t = require('tap')
-const requireInject = require('require-inject')
const npm = {
flatOptions: {
yes: false,
@@ -17,10 +16,9 @@ const npm = {
},
}
-const birthday = requireInject('../../lib/birthday.js', {
- '../../lib/npm.js': npm,
-})
+const Birthday = require('../../lib/birthday.js')
+const birthday = new Birthday(npm)
let calledCb = false
-birthday([], () => calledCb = true)
+birthday.exec([], () => calledCb = true)
t.equal(calledCb, true, 'called the callback')
diff --git a/test/lib/bugs.js b/test/lib/bugs.js
index 992bd9f61..e98131f11 100644
--- a/test/lib/bugs.js
+++ b/test/lib/bugs.js
@@ -43,17 +43,18 @@ const pacote = {
// keep a tally of which urls got opened
const opened = {}
-const openUrl = (url, errMsg, cb) => {
+const openUrl = async (npm, url, errMsg) => {
opened[url] = opened[url] || 0
opened[url]++
- process.nextTick(cb)
}
-const bugs = requireInject('../../lib/bugs.js', {
+const Bugs = requireInject('../../lib/bugs.js', {
pacote,
'../../lib/utils/open-url.js': openUrl,
})
+const bugs = new Bugs({ flatOptions: {} })
+
t.test('open bugs urls', t => {
const expect = {
nobugs: 'https://www.npmjs.com/package/nobugs',
@@ -68,7 +69,7 @@ t.test('open bugs urls', t => {
t.plan(keys.length)
keys.forEach(pkg => {
t.test(pkg, t => {
- bugs([pkg], (er) => {
+ bugs.exec([pkg], (er) => {
if (er)
throw er
t.equal(opened[expect[pkg]], 1, 'opened expected url', {opened})
@@ -79,7 +80,7 @@ t.test('open bugs urls', t => {
})
t.test('open default package if none specified', t => {
- bugs([], (er) => {
+ bugs.exec([], (er) => {
if (er)
throw er
t.equal(opened['https://example.com'], 2, 'opened expected url', {opened})
diff --git a/test/lib/cache.js b/test/lib/cache.js
index 05d269dd4..67499f37e 100644
--- a/test/lib/cache.js
+++ b/test/lib/cache.js
@@ -58,27 +58,26 @@ const cacache = {
},
}
-const mocks = {
+const Cache = requireInject('../../lib/cache.js', {
cacache,
npmlog,
pacote,
rimraf,
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': output,
'../../lib/utils/usage.js': usageUtil,
-}
+})
-const cache = requireInject('../../lib/cache.js', mocks)
+const cache = new Cache(npm)
t.test('cache no args', t => {
- cache([], err => {
+ cache.exec([], err => {
t.equal(err.message, 'usage instructions', 'should throw usage instructions')
t.end()
})
})
t.test('cache clean', t => {
- cache(['clean'], err => {
+ cache.exec(['clean'], err => {
t.match(err.message, 'the npm cache self-heals', 'should throw warning')
t.end()
})
@@ -91,7 +90,7 @@ t.test('cache clean (force)', t => {
flatOptions.force = false
})
- cache(['clear'], err => {
+ cache.exec(['clear'], err => {
t.ifError(err)
t.equal(rimrafPath, path.join(npm.cache, '_cacache'))
t.end()
@@ -99,7 +98,7 @@ t.test('cache clean (force)', t => {
})
t.test('cache clean with arg', t => {
- cache(['rm', 'pkg'], err => {
+ cache.exec(['rm', 'pkg'], err => {
t.match(err.message, 'does not accept arguments', 'should throw error')
t.end()
})
@@ -110,7 +109,7 @@ t.test('cache add no arg', t => {
logOutput = []
})
- cache(['add'], err => {
+ cache.exec(['add'], err => {
t.strictSame(logOutput, [
['silly', 'cache add', 'args', []],
], 'logs correctly')
@@ -126,7 +125,7 @@ t.test('cache add pkg only', t => {
tarballStreamOpts = {}
})
- cache(['add', 'mypkg'], err => {
+ cache.exec(['add', 'mypkg'], err => {
t.ifError(err)
t.strictSame(logOutput, [
['silly', 'cache add', 'args', ['mypkg']],
@@ -145,7 +144,7 @@ t.test('cache add pkg w/ spec modifier', t => {
tarballStreamOpts = {}
})
- cache(['add', 'mypkg', 'latest'], err => {
+ cache.exec(['add', 'mypkg', 'latest'], err => {
t.ifError(err)
t.strictSame(logOutput, [
['silly', 'cache add', 'args', ['mypkg', 'latest']],
@@ -162,7 +161,7 @@ t.test('cache verify', t => {
outputOutput = []
})
- cache(['verify'], err => {
+ cache.exec(['verify'], err => {
t.ifError(err)
t.match(outputOutput, [
`Cache verified and compressed (${path.join(npm.cache, '_cacache')})`,
@@ -189,7 +188,7 @@ t.test('cache verify w/ extra output', t => {
delete cacacheVerifyStats.missingContent
})
- cache(['check'], err => {
+ cache.exec(['check'], err => {
t.ifError(err)
t.match(outputOutput, [
`Cache verified and compressed (~${path.join('/fake/path', '_cacache')})`,
diff --git a/test/lib/ci.js b/test/lib/ci.js
index 28c66b056..3419218ef 100644
--- a/test/lib/ci.js
+++ b/test/lib/ci.js
@@ -9,19 +9,8 @@ const requireInject = require('require-inject')
test('should ignore scripts with --ignore-scripts', (t) => {
const SCRIPTS = []
let REIFY_CALLED = false
- const ci = requireInject('../../lib/ci.js', {
+ const CI = requireInject('../../lib/ci.js', {
'../../lib/utils/reify-finish.js': async () => {},
- '../../lib/npm.js': {
- globalDir: 'path/to/node_modules/',
- prefix: 'foo',
- flatOptions: {
- global: false,
- ignoreScripts: true,
- },
- config: {
- get: () => false,
- },
- },
'@npmcli/run-script': ({ event }) => {
SCRIPTS.push(event)
},
@@ -32,7 +21,20 @@ test('should ignore scripts with --ignore-scripts', (t) => {
}
},
})
- ci([], er => {
+
+ const ci = new CI({
+ globalDir: 'path/to/node_modules/',
+ prefix: 'foo',
+ flatOptions: {
+ global: false,
+ ignoreScripts: true,
+ },
+ config: {
+ get: () => false,
+ },
+ })
+
+ ci.exec([], er => {
if (er)
throw er
t.equal(REIFY_CALLED, true, 'called reify')
@@ -87,13 +89,7 @@ test('should use Arborist and run-script', (t) => {
const expectRimrafs = 3
let actualRimrafs = 0
- const ci = requireInject('../../lib/ci.js', {
- '../../lib/npm.js': {
- prefix: path,
- flatOptions: {
- global: false,
- },
- },
+ const CI = requireInject('../../lib/ci.js', {
'../../lib/utils/reify-finish.js': async () => {},
'@npmcli/run-script': opts => {
t.match(opts, { event: scripts.shift() })
@@ -118,7 +114,15 @@ test('should use Arborist and run-script', (t) => {
t.ok(arb, 'gets arborist tree')
},
})
- ci(null, er => {
+
+ const ci = new CI({
+ prefix: path,
+ flatOptions: {
+ global: false,
+ },
+ })
+
+ ci.exec(null, er => {
if (er)
throw er
for (const [msg, result] of Object.entries(timers))
@@ -131,13 +135,7 @@ test('should use Arborist and run-script', (t) => {
})
test('should pass flatOptions to Arborist.reify', (t) => {
- const ci = requireInject('../../lib/ci.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- production: true,
- },
- },
+ const CI = requireInject('../../lib/ci.js', {
'../../lib/utils/reify-finish.js': async () => {},
'@npmcli/run-script': opts => {},
'@npmcli/arborist': function () {
@@ -148,7 +146,13 @@ test('should pass flatOptions to Arborist.reify', (t) => {
}
},
})
- ci(null, er => {
+ const ci = new CI({
+ prefix: 'foo',
+ flatOptions: {
+ production: true,
+ },
+ })
+ ci.exec(null, er => {
if (er)
throw er
})
@@ -160,13 +164,7 @@ test('should throw if package-lock.json or npm-shrinkwrap missing', (t) => {
'package.json': 'some info',
})
- const ci = requireInject('../../lib/ci.js', {
- '../../lib/npm.js': {
- prefix: testDir,
- flatOptions: {
- global: false,
- },
- },
+ const CI = requireInject('../../lib/ci.js', {
'@npmcli/run-script': opts => {},
'../../lib/utils/reify-finish.js': async () => {},
npmlog: {
@@ -175,7 +173,13 @@ test('should throw if package-lock.json or npm-shrinkwrap missing', (t) => {
},
},
})
- ci(null, (err, res) => {
+ const ci = new CI({
+ prefix: testDir,
+ flatOptions: {
+ global: false,
+ },
+ })
+ ci.exec(null, (err, res) => {
t.ok(err, 'throws error when there is no package-lock')
t.notOk(res)
t.end()
@@ -183,17 +187,17 @@ test('should throw if package-lock.json or npm-shrinkwrap missing', (t) => {
})
test('should throw ECIGLOBAL', (t) => {
- const ci = requireInject('../../lib/ci.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- global: true,
- },
- },
+ const CI = requireInject('../../lib/ci.js', {
'@npmcli/run-script': opts => {},
'../../lib/utils/reify-finish.js': async () => {},
})
- ci(null, (err, res) => {
+ const ci = new CI({
+ prefix: 'foo',
+ flatOptions: {
+ global: true,
+ },
+ })
+ ci.exec(null, (err, res) => {
t.equals(err.code, 'ECIGLOBAL', 'throws error with global packages')
t.notOk(res)
t.end()
@@ -207,13 +211,7 @@ test('should remove existing node_modules before installing', (t) => {
},
})
- const ci = requireInject('../../lib/ci.js', {
- '../../lib/npm.js': {
- prefix: testDir,
- flatOptions: {
- global: false,
- },
- },
+ const CI = requireInject('../../lib/ci.js', {
'@npmcli/run-script': opts => {},
'../../lib/utils/reify-finish.js': async () => {},
'@npmcli/arborist': function () {
@@ -229,7 +227,14 @@ test('should remove existing node_modules before installing', (t) => {
},
})
- ci(null, er => {
+ const ci = new CI({
+ prefix: testDir,
+ flatOptions: {
+ global: false,
+ },
+ })
+
+ ci.exec(null, er => {
if (er)
throw er
})
diff --git a/test/lib/completion.js b/test/lib/completion.js
index 19f70df20..89e8134eb 100644
--- a/test/lib/completion.js
+++ b/test/lib/completion.js
@@ -75,8 +75,7 @@ const deref = (cmd) => {
return cmd
}
-const completion = requireInject('../../lib/completion.js', {
- '../../lib/npm.js': npm,
+const Completion = requireInject('../../lib/completion.js', {
'../../lib/utils/cmd-list.js': cmdList,
'../../lib/utils/config.js': config,
'../../lib/utils/deref-command.js': deref,
@@ -85,6 +84,7 @@ const completion = requireInject('../../lib/completion.js', {
output.push(line)
},
})
+const completion = new Completion(npm)
test('completion completion', async t => {
const home = process.env.HOME
@@ -125,11 +125,13 @@ test('completion completion wrong word count', async t => {
})
test('completion errors in windows without bash', t => {
- const compl = requireInject('../../lib/completion.js', {
+ const Compl = requireInject('../../lib/completion.js', {
'../../lib/utils/is-windows-shell.js': true,
})
- compl({}, (err) => {
+ const compl = new Compl()
+
+ compl.exec({}, (err) => {
t.match(err, {
code: 'ENOTSUP',
message: /completion supported only in MINGW/,
@@ -162,7 +164,7 @@ test('dump script when completion is not being attempted', t => {
})
}
- completion({}, (err) => {
+ completion.exec({}, (err) => {
if (err)
throw err
@@ -195,7 +197,7 @@ test('dump script exits correctly when EPIPE is emitted on stdout', t => {
})
}
- completion({}, (err) => {
+ completion.exec({}, (err) => {
if (err)
throw err
@@ -228,7 +230,7 @@ test('non EPIPE errors cause failures', t => {
})
}
- completion({}, (err) => {
+ completion.exec({}, (err) => {
t.equal(err.errno, 'ESOMETHINGELSE', 'propagated error')
t.equal(data, completionScript, 'wrote the completion script')
t.end()
@@ -248,7 +250,7 @@ test('completion completes single command name', t => {
output.length = 0
})
- completion(['npm', 'c'], (err, res) => {
+ completion.exec(['npm', 'c'], (err, res) => {
if (err)
throw err
@@ -270,7 +272,7 @@ test('completion completes command names', t => {
output.length = 0
})
- completion(['npm', 'a'], (err, res) => {
+ completion.exec(['npm', 'a'], (err, res) => {
if (err)
throw err
@@ -292,7 +294,7 @@ test('completion of invalid command name does nothing', t => {
output.length = 0
})
- completion(['npm', 'compute'], (err, res) => {
+ completion.exec(['npm', 'compute'], (err, res) => {
if (err)
throw err
@@ -314,7 +316,7 @@ test('handles async completion function', t => {
output.length = 0
})
- completion(['npm', 'promise', ''], (err, res) => {
+ completion.exec(['npm', 'promise', ''], (err, res) => {
if (err)
throw err
@@ -343,7 +345,7 @@ test('completion triggers command completions', t => {
output.length = 0
})
- completion(['npm', 'access', ''], (err, res) => {
+ completion.exec(['npm', 'access', ''], (err, res) => {
if (err)
throw err
@@ -372,7 +374,7 @@ test('completion triggers filtered command completions', t => {
output.length = 0
})
- completion(['npm', 'access', 'p'], (err, res) => {
+ completion.exec(['npm', 'access', 'p'], (err, res) => {
if (err)
throw err
@@ -401,7 +403,7 @@ test('completions for commands that return nested arrays are joined', t => {
output.length = 0
})
- completion(['npm', 'completion', ''], (err, res) => {
+ completion.exec(['npm', 'completion', ''], (err, res) => {
if (err)
throw err
@@ -430,7 +432,7 @@ test('completions for commands that return nothing work correctly', t => {
output.length = 0
})
- completion(['npm', 'donothing', ''], (err, res) => {
+ completion.exec(['npm', 'donothing', ''], (err, res) => {
if (err)
throw err
@@ -459,7 +461,7 @@ test('completions for commands that return a single item work correctly', t => {
output.length = 0
})
- completion(['npm', 'driveaboat', ''], (err, res) => {
+ completion.exec(['npm', 'driveaboat', ''], (err, res) => {
if (err)
throw err
@@ -489,7 +491,7 @@ test('command completion for commands with no completion return no results', t =
})
// quotes around adduser are to ensure coverage when unescaping commands
- completion(['npm', '\'adduser\'', ''], (err, res) => {
+ completion.exec(['npm', '\'adduser\'', ''], (err, res) => {
if (err)
throw err
@@ -520,7 +522,7 @@ test('command completion errors propagate', t => {
accessCompletionError = false
})
- completion(['npm', 'access', ''], (err, res) => {
+ completion.exec(['npm', 'access', ''], (err, res) => {
t.match(err, /access completion failed/, 'catches the appropriate error')
t.strictSame(npmConfig, {
argv: {
@@ -547,7 +549,7 @@ test('completion can complete flags', t => {
output.length = 0
})
- completion(['npm', 'install', '--'], (err, res) => {
+ completion.exec(['npm', 'install', '--'], (err, res) => {
if (err)
throw err
@@ -570,7 +572,7 @@ test('double dashes escape from flag completion', t => {
output.length = 0
})
- completion(['npm', '--', 'install', '--'], (err, res) => {
+ completion.exec(['npm', '--', 'install', '--'], (err, res) => {
if (err)
throw err
@@ -593,7 +595,7 @@ test('completion cannot complete options that take a value in mid-command', t =>
output.length = 0
})
- completion(['npm', '--registry', 'install'], (err, res) => {
+ completion.exec(['npm', '--registry', 'install'], (err, res) => {
if (err)
throw err
diff --git a/test/lib/config.js b/test/lib/config.js
index edaa6486c..c2420aefb 100644
--- a/test/lib/config.js
+++ b/test/lib/config.js
@@ -68,17 +68,17 @@ const usageUtil = () => 'usage instructions'
const mocks = {
'../../lib/utils/config.js': { defaults, types },
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': msg => {
result = msg
},
'../../lib/utils/usage.js': usageUtil,
}
-const config = requireInject('../../lib/config.js', mocks)
+const Config = requireInject('../../lib/config.js', mocks)
+const config = new Config(npm)
t.test('config no args', t => {
- config([], (err) => {
+ config.exec([], (err) => {
t.match(err, /usage instructions/, 'should not error out on empty locations')
t.end()
})
@@ -94,7 +94,7 @@ t.test('config list', t => {
delete npm.config.find
})
- config(['list'], (err) => {
+ config.exec(['list'], (err) => {
t.ifError(err, 'npm config list')
t.matchSnapshot(result, 'should list configs')
})
@@ -120,7 +120,7 @@ t.test('config list overrides', t => {
delete npm.config.find
})
- config(['list'], (err) => {
+ config.exec(['list'], (err) => {
t.ifError(err, 'npm config list')
t.matchSnapshot(result, 'should list overridden configs')
})
@@ -138,7 +138,7 @@ t.test('config list --long', t => {
result = ''
})
- config(['list'], (err) => {
+ config.exec(['list'], (err) => {
t.ifError(err, 'npm config list --long')
t.matchSnapshot(result, 'should list all configs')
})
@@ -163,7 +163,7 @@ t.test('config list --json', t => {
result = ''
})
- config(['list'], (err) => {
+ config.exec(['list'], (err) => {
t.ifError(err, 'npm config list --json')
t.deepEqual(
JSON.parse(result),
@@ -179,7 +179,7 @@ t.test('config list --json', t => {
})
t.test('config delete no args', t => {
- config(['delete'], (err) => {
+ config.exec(['delete'], (err) => {
t.equal(
err.message,
'usage instructions',
@@ -202,7 +202,7 @@ t.test('config delete key', t => {
t.equal(where, 'user', 'should save user config post-delete')
}
- config(['delete', 'foo'], (err) => {
+ config.exec(['delete', 'foo'], (err) => {
t.ifError(err, 'npm config delete key')
})
@@ -229,7 +229,7 @@ t.test('config delete multiple key', t => {
t.equal(where, 'user', 'should save user config post-delete')
}
- config(['delete', 'foo', 'bar'], (err) => {
+ config.exec(['delete', 'foo', 'bar'], (err) => {
t.ifError(err, 'npm config delete keys')
})
@@ -252,7 +252,7 @@ t.test('config delete key --global', t => {
}
flatOptions.global = true
- config(['delete', 'foo'], (err) => {
+ config.exec(['delete', 'foo'], (err) => {
t.ifError(err, 'npm config delete key --global')
})
@@ -264,7 +264,7 @@ t.test('config delete key --global', t => {
})
t.test('config set no args', t => {
- config(['set'], (err) => {
+ config.exec(['set'], (err) => {
t.equal(
err.message,
'usage instructions',
@@ -287,7 +287,7 @@ t.test('config set key', t => {
t.equal(where, 'user', 'should save user config')
}
- config(['set', 'foo', 'bar'], (err) => {
+ config.exec(['set', 'foo', 'bar'], (err) => {
t.ifError(err, 'npm config set key')
})
@@ -310,7 +310,7 @@ t.test('config set key=val', t => {
t.equal(where, 'user', 'should save user config')
}
- config(['set', 'foo=bar'], (err) => {
+ config.exec(['set', 'foo=bar'], (err) => {
t.ifError(err, 'npm config set key')
})
@@ -341,7 +341,7 @@ t.test('config set multiple keys', t => {
t.equal(where, 'user', 'should save user config')
}
- config(['set', ...args], (err) => {
+ config.exec(['set', ...args], (err) => {
t.ifError(err, 'npm config set key')
})
@@ -364,7 +364,7 @@ t.test('config set key to empty value', t => {
t.equal(where, 'user', 'should save user config')
}
- config(['set', 'foo'], (err) => {
+ config.exec(['set', 'foo'], (err) => {
t.ifError(err, 'npm config set key to empty value')
})
@@ -392,7 +392,7 @@ t.test('config set invalid key', t => {
delete npm.log.warn
})
- config(['set', 'foo', 'bar'], (err) => {
+ config.exec(['set', 'foo', 'bar'], (err) => {
t.ifError(err, 'npm config set invalid key')
})
})
@@ -411,7 +411,7 @@ t.test('config set key --global', t => {
}
flatOptions.global = true
- config(['set', 'foo', 'bar'], (err) => {
+ config.exec(['set', 'foo', 'bar'], (err) => {
t.ifError(err, 'npm config set key --global')
})
@@ -432,7 +432,7 @@ t.test('config get no args', t => {
delete npm.config.find
})
- config(['get'], (err) => {
+ config.exec(['get'], (err) => {
t.ifError(err, 'npm config get no args')
t.matchSnapshot(result, 'should list configs on config get no args')
})
@@ -451,7 +451,7 @@ t.test('config get key', t => {
throw new Error('should not save')
}
- config(['get', 'foo'], (err) => {
+ config.exec(['get', 'foo'], (err) => {
t.ifError(err, 'npm config get key')
})
@@ -479,7 +479,7 @@ t.test('config get multiple keys', t => {
throw new Error('should not save')
}
- config(['get', 'foo', 'bar'], (err) => {
+ config.exec(['get', 'foo', 'bar'], (err) => {
t.ifError(err, 'npm config get multiple keys')
t.equal(result, 'foo=asdf\nbar=asdf')
})
@@ -492,7 +492,7 @@ t.test('config get multiple keys', t => {
})
t.test('config get private key', t => {
- config(['get', '//private-reg.npmjs.org/:_authThoken'], (err) => {
+ config.exec(['get', '//private-reg.npmjs.org/:_authThoken'], (err) => {
t.match(
err,
/The \/\/private-reg.npmjs.org\/:_authThoken option is protected, and cannot be retrieved in this way/,
@@ -538,16 +538,19 @@ sign-git-commit=true`
},
},
}
- const config = requireInject('../../lib/config.js', editMocks)
- config(['edit'], (err) => {
+ const Config = requireInject('../../lib/config.js', editMocks)
+ const config = new Config(npm)
+
+ config.exec(['edit'], (err) => {
t.ifError(err, 'npm config edit')
// test no config file result
editMocks.fs.readFile = (p, e, cb) => {
cb(new Error('ERR'))
}
- const config = requireInject('../../lib/config.js', editMocks)
- config(['edit'], (err) => {
+ const Config = requireInject('../../lib/config.js', editMocks)
+ const config = new Config(npm)
+ config.exec(['edit'], (err) => {
t.ifError(err, 'npm config edit')
})
})
@@ -594,8 +597,9 @@ t.test('config edit --global', t => {
},
},
}
- const config = requireInject('../../lib/config.js', editMocks)
- config(['edit'], (err) => {
+ const Config = requireInject('../../lib/config.js', editMocks)
+ const config = new Config(npm)
+ config.exec(['edit'], (err) => {
t.match(err, /exited with code: 137/, 'propagated exit code from editor')
})
diff --git a/test/lib/dedupe.js b/test/lib/dedupe.js
index b14185525..3e8b2f4c0 100644
--- a/test/lib/dedupe.js
+++ b/test/lib/dedupe.js
@@ -1,29 +1,29 @@
const { test } = require('tap')
const requireInject = require('require-inject')
-test('should throw in global mode', (t) => {
- const dedupe = requireInject('../../lib/dedupe.js', {
- '../../lib/npm.js': {
- flatOptions: {
- global: true,
- },
+const npm = (base) => {
+ const config = base.config
+ return {
+ ...base,
+ flatOptions: { dryRun: false },
+ config: {
+ get: (k) => config[k],
},
- })
+ }
+}
+
+test('should throw in global mode', (t) => {
+ const Dedupe = requireInject('../../lib/dedupe.js')
+ const dedupe = new Dedupe(npm({ config: { global: true }}))
- dedupe([], er => {
+ dedupe.exec([], er => {
t.match(er, { code: 'EDEDUPEGLOBAL' }, 'throws EDEDUPEGLOBAL')
t.end()
})
})
test('should remove dupes using Arborist', (t) => {
- const dedupe = requireInject('../../lib/dedupe.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- dryRun: 'false',
- },
- },
+ const Dedupe = requireInject('../../lib/dedupe.js', {
'@npmcli/arborist': function (args) {
t.ok(args, 'gets options object')
t.ok(args.path, 'gets path option')
@@ -32,11 +32,17 @@ test('should remove dupes using Arborist', (t) => {
t.ok(true, 'dedupe is called')
}
},
- '../../lib/utils/reify-finish.js': (arb) => {
+ '../../lib/utils/reify-finish.js': (npm, arb) => {
t.ok(arb, 'gets arborist tree')
},
})
- dedupe({ dryRun: true }, er => {
+ const dedupe = new Dedupe(npm({
+ prefix: 'foo',
+ config: {
+ 'dry-run': 'true',
+ },
+ }))
+ dedupe.exec([], er => {
if (er)
throw er
t.ok(true, 'callback is called')
@@ -45,20 +51,20 @@ test('should remove dupes using Arborist', (t) => {
})
test('should remove dupes using Arborist - no arguments', (t) => {
- const dedupe = requireInject('../../lib/dedupe.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- dryRun: 'true',
- },
- },
+ const Dedupe = requireInject('../../lib/dedupe.js', {
'@npmcli/arborist': function (args) {
t.ok(args.dryRun, 'gets dryRun from flatOptions')
this.dedupe = () => {}
},
'../../lib/utils/reify-output.js': () => {},
})
- dedupe(null, () => {
+ const dedupe = new Dedupe(npm({
+ prefix: 'foo',
+ config: {
+ 'dry-run': true,
+ },
+ }))
+ dedupe.exec(null, () => {
t.end()
})
})
diff --git a/test/lib/deprecate.js b/test/lib/deprecate.js
index fd563de12..03100166a 100644
--- a/test/lib/deprecate.js
+++ b/test/lib/deprecate.js
@@ -18,10 +18,7 @@ npmFetch.json = async (uri, opts) => {
}
}
-const deprecate = requireInject('../../lib/deprecate.js', {
- '../../lib/npm.js': {
- flatOptions: { registry: 'https://registry.npmjs.org' },
- },
+const Deprecate = requireInject('../../lib/deprecate.js', {
'../../lib/utils/get-identity.js': async () => getIdentityImpl(),
'../../lib/utils/otplease.js': async (opts, fn) => fn(opts),
libnpmaccess: {
@@ -30,16 +27,19 @@ const deprecate = requireInject('../../lib/deprecate.js', {
'npm-registry-fetch': npmFetch,
})
+const deprecate = new Deprecate({
+ flatOptions: { registry: 'https://registry.npmjs.org' },
+})
+
test('completion', async t => {
const defaultIdentityImpl = getIdentityImpl
t.teardown(() => {
getIdentityImpl = defaultIdentityImpl
})
- const { completion } = deprecate
-
const testComp = async (argv, expect) => {
- const res = await completion({ conf: { argv: { remain: argv } } })
+ const res =
+ await deprecate.completion({ conf: { argv: { remain: argv } } })
t.strictSame(res, expect, `completion: ${argv}`)
}
@@ -59,21 +59,21 @@ test('completion', async t => {
})
test('no args', t => {
- deprecate([], (err) => {
+ deprecate.exec([], (err) => {
t.match(err, /Usage: npm deprecate/, 'logs usage')
t.end()
})
})
test('only one arg', t => {
- deprecate(['foo'], (err) => {
+ deprecate.exec(['foo'], (err) => {
t.match(err, /Usage: npm deprecate/, 'logs usage')
t.end()
})
})
test('invalid semver range', t => {
- deprecate(['foo@notaversion', 'this will fail'], (err) => {
+ deprecate.exec(['foo@notaversion', 'this will fail'], (err) => {
t.match(err, /invalid version range/, 'logs semver error')
t.end()
})
@@ -84,7 +84,7 @@ test('deprecates given range', t => {
npmFetchBody = null
})
- deprecate(['foo@1.0.0', 'this version is deprecated'], (err) => {
+ deprecate.exec(['foo@1.0.0', 'this version is deprecated'], (err) => {
if (err)
throw err
@@ -110,7 +110,7 @@ test('deprecates all versions when no range is specified', t => {
npmFetchBody = null
})
- deprecate(['foo', 'this version is deprecated'], (err) => {
+ deprecate.exec(['foo', 'this version is deprecated'], (err) => {
if (err)
throw err
diff --git a/test/lib/diff.js b/test/lib/diff.js
index 926c54fdf..5e60f125c 100644
--- a/test/lib/diff.js
+++ b/test/lib/diff.js
@@ -28,7 +28,6 @@ const mocks = {
npmlog: { info: noop, verbose: noop },
libnpmdiff: (...args) => libnpmdiff(...args),
'npm-registry-fetch': async () => ({}),
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': noop,
'../../lib/utils/read-local-package.js': async () => rlp(),
'../../lib/utils/usage.js': () => 'usage instructions',
@@ -42,7 +41,8 @@ t.afterEach(cb => {
cb()
})
-const diff = requireInject('../../lib/diff.js', mocks)
+const Diff = requireInject('../../lib/diff.js', mocks)
+const diff = new Diff(npm)
t.test('no args', t => {
t.test('in a project dir', t => {
@@ -56,7 +56,7 @@ t.test('no args', t => {
}
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -65,7 +65,7 @@ t.test('no args', t => {
t.test('no args, missing package.json name in cwd', t => {
rlp = () => undefined
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Needs multiple arguments to compare or run from a project dir./,
@@ -80,7 +80,7 @@ t.test('no args', t => {
throw new Error('ERR')
}
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Needs multiple arguments to compare or run from a project dir./,
@@ -106,7 +106,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['foo@1.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -120,7 +120,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['foo@1.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Needs multiple arguments to compare or run from a project dir./,
@@ -142,7 +142,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['foo@~1.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -160,7 +160,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['2.1.4']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -172,7 +172,7 @@ t.test('single arg', t => {
}
npm.flatOptions.diff = ['2.1.4']
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Needs multiple arguments to compare or run from a project dir./,
@@ -200,7 +200,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['2.1.4']
npm.flatOptions.prefix = path
- diff(['./foo.js', './bar.js'], err => {
+ diff.exec(['./foo.js', './bar.js'], err => {
if (err)
throw err
})
@@ -224,7 +224,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['bar@1.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -250,7 +250,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['simple-output']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -264,7 +264,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['bar']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Needs multiple arguments to compare or run from a project dir./,
@@ -297,7 +297,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['bar']
npm.flatOptions.prefix = path
- const diff = requireInject('../../lib/diff.js', {
+ const Diff = requireInject('../../lib/diff.js', {
...mocks,
pacote: {
packument: (spec) => {
@@ -313,8 +313,9 @@ t.test('single arg', t => {
t.equal(b, 'bar@1.8.10', 'should have possible semver range spec')
},
})
+ const diff = new Diff(npm)
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -359,7 +360,7 @@ t.test('single arg', t => {
npm.flatOptions.prefix = resolve(path, 'project')
npm.globalDir = resolve(path, 'globalDir/lib/node_modules')
- const diff = requireInject('../../lib/diff.js', {
+ const Diff = requireInject('../../lib/diff.js', {
...mocks,
pacote: {
packument: (spec) => {
@@ -375,8 +376,9 @@ t.test('single arg', t => {
t.equal(b, 'lorem@2.1.0', 'should have possible semver range spec')
},
})
+ const diff = new Diff(npm)
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -410,7 +412,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['bar@2.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -445,7 +447,7 @@ t.test('single arg', t => {
}),
})
- const diff = requireInject('../../lib/diff.js', {
+ const Diff = requireInject('../../lib/diff.js', {
...mocks,
'../../lib/utils/read-local-package.js': async () => 'my-project',
pacote: {
@@ -462,11 +464,12 @@ t.test('single arg', t => {
t.equal(b, 'lorem@2.2.2', 'should have expected target spec')
},
})
+ const diff = new Diff(npm)
npm.flatOptions.diff = ['lorem']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -481,7 +484,7 @@ t.test('single arg', t => {
}),
})
- const diff = requireInject('../../lib/diff.js', {
+ const Diff = requireInject('../../lib/diff.js', {
...mocks,
'../../lib/utils/read-local-package.js': async () => 'my-project',
'@npmcli/arborist': class {
@@ -494,11 +497,12 @@ t.test('single arg', t => {
t.equal(b, `file:${path}`, 'should target current cwd')
},
})
+ const diff = new Diff(npm)
npm.flatOptions.diff = ['lorem']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -517,7 +521,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['bar']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -535,7 +539,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['my-project']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -553,7 +557,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['/path/to/other-dir']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -564,7 +568,7 @@ t.test('single arg', t => {
npm.flatOptions.diff = ['git+https://github.com/user/foo']
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Spec type not supported./,
@@ -588,7 +592,7 @@ t.test('first arg is a qualified spec', t => {
}
npm.flatOptions.diff = ['bar@1.0.0', 'bar@^2.0.0']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -622,7 +626,7 @@ t.test('first arg is a qualified spec', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['bar@2.0.0', 'bar']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -638,7 +642,7 @@ t.test('first arg is a qualified spec', t => {
t.equal(b, 'bar@2.0.0', 'should use name from first arg')
}
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -653,7 +657,7 @@ t.test('first arg is a qualified spec', t => {
}
npm.flatOptions.diff = ['bar@1.0.0', 'bar-fork']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -691,7 +695,7 @@ t.test('first arg is a known dependency name', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['bar', 'bar@2.0.0']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -731,7 +735,7 @@ t.test('first arg is a known dependency name', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['bar', 'bar-fork']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -765,7 +769,7 @@ t.test('first arg is a known dependency name', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['bar', '2.0.0']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -799,7 +803,7 @@ t.test('first arg is a known dependency name', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['bar', 'bar-fork']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -819,7 +823,7 @@ t.test('first arg is a valid semver range', t => {
t.equal(b, 'bar@2.0.0', 'should use expected spec')
}
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -853,7 +857,7 @@ t.test('first arg is a valid semver range', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['1.0.0', 'bar']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -869,7 +873,7 @@ t.test('first arg is a valid semver range', t => {
}
npm.flatOptions.diff = ['1.0.0', '2.0.0']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -883,7 +887,7 @@ t.test('first arg is a valid semver range', t => {
npm.flatOptions.diff = ['1.0.0', '2.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Needs to be run from a project dir in order to diff two versions./,
@@ -903,7 +907,7 @@ t.test('first arg is a valid semver range', t => {
}
npm.flatOptions.diff = ['1.0.0', 'bar']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -918,7 +922,7 @@ t.test('first arg is a valid semver range', t => {
}),
})
- const diff = requireInject('../../lib/diff.js', {
+ const Diff = requireInject('../../lib/diff.js', {
...mocks,
'../../lib/utils/read-local-package.js': async () => 'my-project',
'@npmcli/arborist': class {
@@ -931,11 +935,12 @@ t.test('first arg is a valid semver range', t => {
t.equal(b, 'lorem@2.0.0', 'should target expected spec')
},
})
+ const diff = new Diff(npm)
npm.flatOptions.diff = ['1.0.0', 'lorem@2.0.0']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -955,7 +960,7 @@ t.test('first arg is an unknown dependency name', t => {
}
npm.flatOptions.diff = ['bar', 'bar@2.0.0']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -989,7 +994,7 @@ t.test('first arg is an unknown dependency name', t => {
npm.flatOptions.prefix = path
npm.flatOptions.diff = ['bar-fork', 'bar']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -1004,7 +1009,7 @@ t.test('first arg is an unknown dependency name', t => {
}
npm.flatOptions.diff = ['bar', '^1.0.0']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -1019,7 +1024,7 @@ t.test('first arg is an unknown dependency name', t => {
}
npm.flatOptions.diff = ['bar', 'bar-fork']
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -1040,7 +1045,7 @@ t.test('first arg is an unknown dependency name', t => {
npm.flatOptions.diff = ['bar', 'bar-fork']
npm.flatOptions.prefix = path
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -1062,7 +1067,7 @@ t.test('various options', t => {
}, 'should forward nameOnly=true option')
}
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -1085,7 +1090,7 @@ t.test('various options', t => {
}, 'should forward diffFiles values')
}
- diff(['./foo.js', './bar.js'], err => {
+ diff.exec(['./foo.js', './bar.js'], err => {
if (err)
throw err
})
@@ -1109,7 +1114,7 @@ t.test('various options', t => {
}
npm.flatOptions.prefix = path
- diff(['./foo.js', './bar.js'], err => {
+ diff.exec(['./foo.js', './bar.js'], err => {
if (err)
throw err
})
@@ -1137,7 +1142,7 @@ t.test('various options', t => {
}, 'should forward diff options')
}
- diff([], err => {
+ diff.exec([], err => {
if (err)
throw err
})
@@ -1148,7 +1153,7 @@ t.test('various options', t => {
t.test('too many args', t => {
npm.flatOptions.diff = ['a', 'b', 'c']
- diff([], err => {
+ diff.exec([], err => {
t.match(
err,
/Can't use more than two --diff arguments./,
diff --git a/test/lib/dist-tag.js b/test/lib/dist-tag.js
index c18935230..b761fb103 100644
--- a/test/lib/dist-tag.js
+++ b/test/lib/dist-tag.js
@@ -48,7 +48,7 @@ const logger = (...msgs) => {
log += '\n'
}
-const distTag = requireInject('../../lib/dist-tag.js', {
+const DistTag = requireInject('../../lib/dist-tag.js', {
npmlog: {
error: logger,
info: logger,
@@ -58,26 +58,27 @@ const distTag = requireInject('../../lib/dist-tag.js', {
get 'npm-registry-fetch' () {
return npmRegistryFetchMock
},
- '../../lib/npm.js': {
- flatOptions: _flatOptions,
- config: {
- get (key) {
- return _flatOptions[key]
- },
- },
- },
'../../lib/utils/output.js': msg => {
result = msg
},
})
+const distTag = new DistTag({
+ flatOptions: _flatOptions,
+ config: {
+ get (key) {
+ return _flatOptions[key]
+ },
+ },
+})
+
test('ls in current package', (t) => {
prefix = t.testdir({
'package.json': JSON.stringify({
name: '@scoped/pkg',
}),
})
- distTag(['ls'], (err) => {
+ distTag.exec(['ls'], (err) => {
t.ifError(err, 'npm dist-tags ls')
t.matchSnapshot(
result,
@@ -95,7 +96,7 @@ test('no args in current package', (t) => {
name: '@scoped/pkg',
}),
})
- distTag([], (err) => {
+ distTag.exec([], (err) => {
t.ifError(err, 'npm dist-tags ls')
t.matchSnapshot(
result,
@@ -109,7 +110,7 @@ test('no args in current package', (t) => {
test('borked cmd usage', (t) => {
prefix = t.testdir({})
- distTag(['borked', '@scoped/pkg'], (err) => {
+ distTag.exec(['borked', '@scoped/pkg'], (err) => {
t.matchSnapshot(err, 'should show usage error')
result = ''
log = ''
@@ -119,7 +120,7 @@ test('borked cmd usage', (t) => {
test('ls on named package', (t) => {
prefix = t.testdir({})
- distTag(['ls', '@scoped/another'], (err) => {
+ distTag.exec(['ls', '@scoped/another'], (err) => {
t.ifError(err, 'npm dist-tags ls')
t.matchSnapshot(
result,
@@ -133,7 +134,7 @@ test('ls on named package', (t) => {
test('ls on missing package', (t) => {
prefix = t.testdir({})
- distTag(['ls', 'foo'], (err) => {
+ distTag.exec(['ls', 'foo'], (err) => {
t.matchSnapshot(
log,
'should log no dist-tag found msg'
@@ -154,7 +155,7 @@ test('ls on missing name in current package', (t) => {
version: '1.0.0',
}),
})
- distTag(['ls'], (err) => {
+ distTag.exec(['ls'], (err) => {
t.matchSnapshot(
err,
'should throw usage error message'
@@ -167,7 +168,7 @@ test('ls on missing name in current package', (t) => {
test('only named package arg', (t) => {
prefix = t.testdir({})
- distTag(['@scoped/another'], (err) => {
+ distTag.exec(['@scoped/another'], (err) => {
t.ifError(err, 'npm dist-tags ls')
t.matchSnapshot(
result,
@@ -186,7 +187,7 @@ test('add new tag', (t) => {
t.equal(opts.body, '7.7.7', 'should point to expected version')
}
prefix = t.testdir({})
- distTag(['add', '@scoped/another@7.7.7', 'c'], (err) => {
+ distTag.exec(['add', '@scoped/another@7.7.7', 'c'], (err) => {
t.ifError(err, 'npm dist-tags add')
t.matchSnapshot(
result,
@@ -201,7 +202,7 @@ test('add new tag', (t) => {
test('add using valid semver range as name', (t) => {
prefix = t.testdir({})
- distTag(['add', '@scoped/another@7.7.7', '1.0.0'], (err) => {
+ distTag.exec(['add', '@scoped/another@7.7.7', '1.0.0'], (err) => {
t.match(
err,
/Error: Tag name must not be a valid SemVer range: 1.0.0/,
@@ -219,7 +220,7 @@ test('add using valid semver range as name', (t) => {
test('add missing args', (t) => {
prefix = t.testdir({})
- distTag(['add', '@scoped/another@7.7.7'], (err) => {
+ distTag.exec(['add', '@scoped/another@7.7.7'], (err) => {
t.matchSnapshot(err, 'should exit usage error message')
result = ''
log = ''
@@ -229,7 +230,7 @@ test('add missing args', (t) => {
test('add missing pkg name', (t) => {
prefix = t.testdir({})
- distTag(['add', null], (err) => {
+ distTag.exec(['add', null], (err) => {
t.matchSnapshot(err, 'should exit usage error message')
result = ''
log = ''
@@ -239,7 +240,7 @@ test('add missing pkg name', (t) => {
test('set existing version', (t) => {
prefix = t.testdir({})
- distTag(['set', '@scoped/another@0.6.0', 'b'], (err) => {
+ distTag.exec(['set', '@scoped/another@0.6.0', 'b'], (err) => {
t.ifError(err, 'npm dist-tags set')
t.matchSnapshot(
log,
@@ -256,7 +257,7 @@ test('remove existing tag', (t) => {
t.equal(opts.method, 'DELETE', 'should trigger request to remove tag')
}
prefix = t.testdir({})
- distTag(['rm', '@scoped/another', 'c'], (err) => {
+ distTag.exec(['rm', '@scoped/another', 'c'], (err) => {
t.ifError(err, 'npm dist-tags rm')
t.matchSnapshot(log, 'should log remove info')
t.matchSnapshot(result, 'should return success msg')
@@ -269,7 +270,7 @@ test('remove existing tag', (t) => {
test('remove non-existing tag', (t) => {
prefix = t.testdir({})
- distTag(['rm', '@scoped/another', 'nonexistent'], (err) => {
+ distTag.exec(['rm', '@scoped/another', 'nonexistent'], (err) => {
t.match(
err,
/Error: nonexistent is not a dist-tag on @scoped\/another/,
@@ -284,7 +285,7 @@ test('remove non-existing tag', (t) => {
test('remove missing pkg name', (t) => {
prefix = t.testdir({})
- distTag(['rm', null], (err) => {
+ distTag.exec(['rm', null], (err) => {
t.matchSnapshot(err, 'should exit usage error message')
result = ''
log = ''
diff --git a/test/lib/docs.js b/test/lib/docs.js
index 8a59ed7cc..a7325738b 100644
--- a/test/lib/docs.js
+++ b/test/lib/docs.js
@@ -33,17 +33,18 @@ const pacote = {
// keep a tally of which urls got opened
const opened = {}
-const openUrl = (url, errMsg, cb) => {
+const openUrl = async (npm, url, errMsg) => {
opened[url] = opened[url] || 0
opened[url]++
- process.nextTick(cb)
}
-const docs = requireInject('../../lib/docs.js', {
+const Docs = requireInject('../../lib/docs.js', {
pacote,
'../../lib/utils/open-url.js': openUrl,
})
+const docs = new Docs({ flatOptions: {} })
+
t.test('open docs urls', t => {
const expect = {
nodocs: 'https://www.npmjs.com/package/nodocs',
@@ -56,7 +57,7 @@ t.test('open docs urls', t => {
t.plan(keys.length)
keys.forEach(pkg => {
t.test(pkg, t => {
- docs([pkg], (er) => {
+ docs.exec([pkg], (er) => {
if (er)
throw er
const url = expect[pkg]
@@ -68,7 +69,7 @@ t.test('open docs urls', t => {
})
t.test('open default package if none specified', t => {
- docs([], (er) => {
+ docs.exec([], (er) => {
if (er)
throw er
t.equal(opened['https://example.com'], 2, 'opened expected url', {opened})
diff --git a/test/lib/doctor.js b/test/lib/doctor.js
index f5e6fd062..820049347 100644
--- a/test/lib/doctor.js
+++ b/test/lib/doctor.js
@@ -120,18 +120,18 @@ const cacache = {
},
}
-const doctor = requireInject('../../lib/doctor.js', {
+const Doctor = requireInject('../../lib/doctor.js', {
'../../lib/utils/is-windows.js': false,
'../../lib/utils/ping.js': ping,
'../../lib/utils/output.js': (data) => {
output.push(data)
},
- '../../lib/npm.js': npm,
cacache,
pacote,
'make-fetch-happen': fetch,
which,
})
+const doctor = new Doctor(npm)
const origVersion = process.version
test('node versions', t => {
@@ -162,7 +162,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
if (err) {
st.fail(output)
return st.end()
@@ -211,7 +211,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
if (err) {
st.fail(err)
return st.end()
@@ -255,7 +255,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'detected the ping error')
st.match(logs, {
checkPing: { finished: true },
@@ -282,18 +282,18 @@ test('node versions', t => {
})
vt.test('npm doctor skips some tests in windows', st => {
- const winDoctor = requireInject('../../lib/doctor.js', {
+ const WinDoctor = requireInject('../../lib/doctor.js', {
'../../lib/utils/is-windows.js': true,
'../../lib/utils/ping.js': ping,
'../../lib/utils/output.js': (data) => {
output.push(data)
},
- '../../lib/npm.js': npm,
cacache,
pacote,
'make-fetch-happen': fetch,
which,
})
+ const winDoctor = new WinDoctor(npm)
const dir = st.testdir()
npm.cache = npm.flatOptions.cache = dir
@@ -312,7 +312,7 @@ test('node versions', t => {
clearLogs()
})
- winDoctor([], (err) => {
+ winDoctor.exec([], (err) => {
if (err) {
st.fail(output)
return st.end()
@@ -360,7 +360,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'detected the ping error')
st.match(logs, {
checkPing: { finished: true },
@@ -409,7 +409,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'detected the ping error')
st.match(logs, {
checkPing: { finished: true },
@@ -458,7 +458,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'detected the out of date npm')
st.match(logs, {
checkPing: { finished: true },
@@ -563,19 +563,19 @@ test('node versions', t => {
}
}
- const doctor = requireInject('../../lib/doctor.js', {
+ const Doctor = requireInject('../../lib/doctor.js', {
'../../lib/utils/is-windows.js': false,
'../../lib/utils/ping.js': ping,
'../../lib/utils/output.js': (data) => {
output.push(data)
},
- '../../lib/npm.js': npm,
cacache,
pacote,
'make-fetch-happen': fetch,
which,
fs,
})
+ const doctor = new Doctor(npm)
// it's necessary to allow tests in node 10.x to not mark 12.x as lted
npm.cache = npm.flatOptions.cache = join(dir, 'cache')
@@ -600,7 +600,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'identified problems')
st.match(logs, {
checkPing: { finished: true },
@@ -653,7 +653,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'detected the missing git')
st.match(logs, {
checkPing: { finished: true },
@@ -706,7 +706,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
// cache verification problems get fixed and so do not throw an error
if (err) {
st.fail(output)
@@ -765,7 +765,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
// cache verification problems get fixed and so do not throw an error
if (err) {
st.fail(output)
@@ -823,7 +823,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
// cache verification problems get fixed and so do not throw an error
if (err) {
st.fail(output)
@@ -878,7 +878,7 @@ test('node versions', t => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
// cache verification problems get fixed and so do not throw an error
st.match(err, /Some problems found/, 'detected the non-default registry')
st.match(logs, {
@@ -942,7 +942,7 @@ test('outdated node version', vt => {
clearLogs()
})
- doctor([], (err) => {
+ doctor.exec([], (err) => {
st.match(err, /Some problems found/, 'detected the out of date nodejs')
st.match(logs, {
checkPing: { finished: true },
diff --git a/test/lib/edit.js b/test/lib/edit.js
index 0d3bbb4c5..acf03fa43 100644
--- a/test/lib/edit.js
+++ b/test/lib/edit.js
@@ -23,6 +23,7 @@ const childProcess = {
}
let rebuildArgs = null
+let rebuildFail = null
let EDITOR = 'vim'
const npm = {
config: {
@@ -32,17 +33,17 @@ const npm = {
commands: {
rebuild: (args, cb) => {
rebuildArgs = args
- return cb()
+ return cb(rebuildFail)
},
},
}
const gracefulFs = require('graceful-fs')
-const edit = requireInject('../../lib/edit.js', {
- '../../lib/npm.js': npm,
+const Edit = requireInject('../../lib/edit.js', {
child_process: childProcess,
'graceful-fs': gracefulFs,
})
+const edit = new Edit(npm)
test('npm edit', t => {
t.teardown(() => {
@@ -52,7 +53,7 @@ test('npm edit', t => {
editorOpts = null
})
- return edit(['semver'], (err) => {
+ return edit.exec(['semver'], (err) => {
if (err)
throw err
@@ -65,6 +66,27 @@ test('npm edit', t => {
})
})
+test('rebuild fails', t => {
+ t.teardown(() => {
+ rebuildFail = null
+ rebuildArgs = null
+ editorBin = null
+ editorArgs = null
+ editorOpts = null
+ })
+
+ rebuildFail = new Error('test error')
+ return edit.exec(['semver'], (err) => {
+ const path = resolve(__dirname, '../../node_modules/semver')
+ t.strictSame(editorBin, EDITOR, 'used the correct editor')
+ t.strictSame(editorArgs, [path], 'edited the correct directory')
+ t.strictSame(editorOpts, { stdio: 'inherit' }, 'passed the correct opts')
+ t.strictSame(rebuildArgs, [path], 'passed the correct path to rebuild')
+ t.match(err, { message: 'test error' })
+ t.end()
+ })
+})
+
test('npm edit editor has flags', t => {
EDITOR = 'code -w'
t.teardown(() => {
@@ -75,7 +97,7 @@ test('npm edit editor has flags', t => {
EDITOR = 'vim'
})
- return edit(['semver'], (err) => {
+ return edit.exec(['semver'], (err) => {
if (err)
throw err
@@ -89,7 +111,7 @@ test('npm edit editor has flags', t => {
})
test('npm edit no args', t => {
- return edit([], (err) => {
+ return edit.exec([], (err) => {
t.match(err, /npm edit/, 'throws usage error')
t.end()
})
@@ -104,7 +126,7 @@ test('npm edit lstat error propagates', t => {
gracefulFs.lstat = _lstat
})
- return edit(['semver'], (err) => {
+ return edit.exec(['semver'], (err) => {
t.match(err, /lstat failed/, 'user received correct error')
t.end()
})
@@ -116,7 +138,7 @@ test('npm edit editor exit code error propagates', t => {
EDITOR_CODE = 0
})
- return edit(['semver'], (err) => {
+ return edit.exec(['semver'], (err) => {
t.match(err, /exited with code: 137/, 'user received correct error')
t.end()
})
diff --git a/test/lib/exec.js b/test/lib/exec.js
index ac813ade7..4dc7f31cc 100644
--- a/test/lib/exec.js
+++ b/test/lib/exec.js
@@ -90,13 +90,13 @@ const mocks = {
'@npmcli/arborist': Arborist,
'@npmcli/run-script': runScript,
'@npmcli/ci-detect': () => CI_NAME,
- '../../lib/npm.js': npm,
pacote,
read,
'mkdirp-infer-owner': mkdirp,
'../../lib/utils/output.js': output,
}
-const exec = requireInject('../../lib/exec.js', mocks)
+const Exec = requireInject('../../lib/exec.js', mocks)
+const exec = new Exec(npm)
t.afterEach(cb => {
MKDIRPS.length = 0
@@ -116,7 +116,7 @@ t.afterEach(cb => {
cb()
})
-t.test('npx foo, bin already exists locally', async t => {
+t.test('npx foo, bin already exists locally', t => {
const path = t.testdir({
foo: 'just some file',
})
@@ -124,24 +124,25 @@ t.test('npx foo, bin already exists locally', async t => {
PROGRESS_IGNORED = true
npm.localBin = path
- await exec(['foo', 'one arg', 'two arg'], er => {
+ exec.exec(['foo', 'one arg', 'two arg'], er => {
t.ifError(er, 'npm exec')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foo' }},
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: {
+ PATH: [path, ...PATH].join(delimiter),
+ },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foo' }},
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: {
- PATH: [path, ...PATH].join(delimiter),
- },
- stdio: 'inherit',
- }])
})
-t.test('npx foo, bin already exists globally', async t => {
+t.test('npx foo, bin already exists globally', t => {
const path = t.testdir({
foo: 'just some file',
})
@@ -149,24 +150,25 @@ t.test('npx foo, bin already exists globally', async t => {
PROGRESS_IGNORED = true
npm.globalBin = path
- await exec(['foo', 'one arg', 'two arg'], er => {
+ exec.exec(['foo', 'one arg', 'two arg'], er => {
t.ifError(er, 'npm exec')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foo' }},
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: {
+ PATH: [path, ...PATH].join(delimiter),
+ },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foo' }},
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: {
- PATH: [path, ...PATH].join(delimiter),
- },
- stdio: 'inherit',
- }])
})
-t.test('npm exec foo, already present locally', async t => {
+t.test('npm exec foo, already present locally', t => {
const path = t.testdir()
npm.localPrefix = path
ARB_ACTUAL_TREE[path] = {
@@ -180,94 +182,103 @@ t.test('npm exec foo, already present locally', async t => {
},
_from: 'foo@',
}
- await exec(['foo', 'one arg', 'two arg'], er => {
+ exec.exec(['foo', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [], 'no need to make any dirs')
+ t.match(ARB_CTOR, [{ package: ['foo'], path }])
+ t.strictSame(ARB_REIFY, [], 'no need to reify anything')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foo' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH: process.env.PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [], 'no need to make any dirs')
- t.match(ARB_CTOR, [{ package: ['foo'], path }])
- t.strictSame(ARB_REIFY, [], 'no need to reify anything')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foo' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH: process.env.PATH },
- stdio: 'inherit',
- }])
})
-t.test('npm exec <noargs>, run interactive shell', async t => {
+t.test('npm exec <noargs>, run interactive shell', t => {
CI_NAME = null
const { isTTY } = process.stdin
process.stdin.isTTY = true
t.teardown(() => process.stdin.isTTY = isTTY)
- const run = async (t, doRun = true) => {
+ const run = (t, doRun, cb) => {
LOG_WARN.length = 0
ARB_CTOR.length = 0
MKDIRPS.length = 0
ARB_REIFY.length = 0
OUTPUT.length = 0
- await exec([], er => {
+ exec.exec([], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [], 'no need to make any dirs')
+ t.strictSame(ARB_CTOR, [], 'no need to instantiate arborist')
+ t.strictSame(ARB_REIFY, [], 'no need to reify anything')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ if (doRun) {
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'shell-cmd' } },
+ args: [],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH: process.env.PATH },
+ stdio: 'inherit',
+ }])
+ } else
+ t.strictSame(RUN_SCRIPTS, [])
+
+ RUN_SCRIPTS.length = 0
+ cb()
})
- t.strictSame(MKDIRPS, [], 'no need to make any dirs')
- t.strictSame(ARB_CTOR, [], 'no need to instantiate arborist')
- t.strictSame(ARB_REIFY, [], 'no need to reify anything')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- if (doRun) {
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'shell-cmd' } },
- args: [],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH: process.env.PATH },
- stdio: 'inherit',
- }])
- } else
- t.strictSame(RUN_SCRIPTS, [])
- RUN_SCRIPTS.length = 0
}
- t.test('print message when tty and not in CI', async t => {
+ t.test('print message when tty and not in CI', t => {
CI_NAME = null
process.stdin.isTTY = true
- await run(t)
- t.strictSame(LOG_WARN, [])
- t.strictSame(OUTPUT, [
- ['\nEntering npm script environment\nType \'exit\' or ^D when finished\n'],
- ], 'printed message about interactive shell')
+ run(t, true, () => {
+ t.strictSame(LOG_WARN, [])
+ t.strictSame(OUTPUT, [
+ ['\nEntering npm script environment\nType \'exit\' or ^D when finished\n'],
+ ], 'printed message about interactive shell')
+ t.end()
+ })
})
- t.test('no message when not TTY', async t => {
+ t.test('no message when not TTY', t => {
CI_NAME = null
process.stdin.isTTY = false
- await run(t)
- t.strictSame(LOG_WARN, [])
- t.strictSame(OUTPUT, [], 'no message about interactive shell')
+ run(t, true, () => {
+ t.strictSame(LOG_WARN, [])
+ t.strictSame(OUTPUT, [], 'no message about interactive shell')
+ t.end()
+ })
})
- t.test('print warning when in CI and interactive', async t => {
+ t.test('print warning when in CI and interactive', t => {
CI_NAME = 'travis-ci'
process.stdin.isTTY = true
- await run(t, false)
- t.strictSame(LOG_WARN, [
- ['exec', 'Interactive mode disabled in CI environment'],
- ])
- t.strictSame(OUTPUT, [], 'no message about interactive shell')
+ run(t, false, () => {
+ t.strictSame(LOG_WARN, [
+ ['exec', 'Interactive mode disabled in CI environment'],
+ ])
+ t.strictSame(OUTPUT, [], 'no message about interactive shell')
+ t.end()
+ })
})
t.end()
})
-t.test('npm exec foo, not present locally or in central loc', async t => {
+t.test('npm exec foo, not present locally or in central loc', t => {
const path = t.testdir()
const installDir = resolve('cache-dir/_npx/f7fbba6e0636f890')
npm.localPrefix = path
@@ -285,28 +296,29 @@ t.test('npm exec foo, not present locally or in central loc', async t => {
},
_from: 'foo@',
}
- await exec(['foo', 'one arg', 'two arg'], er => {
+ exec.exec(['foo', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: ['foo'], path }])
+ t.match(ARB_REIFY, [{add: ['foo@'], legacyPeerDeps: false}], 'need to install foo@')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foo' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: ['foo'], path }])
- t.match(ARB_REIFY, [{add: ['foo@'], legacyPeerDeps: false}], 'need to install foo@')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foo' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
})
-t.test('npm exec foo, not present locally but in central loc', async t => {
+t.test('npm exec foo, not present locally but in central loc', t => {
const path = t.testdir()
const installDir = resolve('cache-dir/_npx/f7fbba6e0636f890')
npm.localPrefix = path
@@ -324,28 +336,29 @@ t.test('npm exec foo, not present locally but in central loc', async t => {
},
_from: 'foo@',
}
- await exec(['foo', 'one arg', 'two arg'], er => {
+ exec.exec(['foo', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: ['foo'], path }])
+ t.match(ARB_REIFY, [], 'no need to install again, already there')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foo' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: ['foo'], path }])
- t.match(ARB_REIFY, [], 'no need to install again, already there')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foo' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
})
-t.test('npm exec foo, present locally but wrong version', async t => {
+t.test('npm exec foo, present locally but wrong version', t => {
const path = t.testdir()
const installDir = resolve('cache-dir/_npx/2badf4630f1cfaad')
npm.localPrefix = path
@@ -363,28 +376,29 @@ t.test('npm exec foo, present locally but wrong version', async t => {
},
_from: 'foo@2.x',
}
- await exec(['foo@2.x', 'one arg', 'two arg'], er => {
+ exec.exec(['foo@2.x', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: ['foo'], path }])
+ t.match(ARB_REIFY, [{ add: ['foo@2.x'], legacyPeerDeps: false }], 'need to add foo@2.x')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foo' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: ['foo'], path }])
- t.match(ARB_REIFY, [{ add: ['foo@2.x'], legacyPeerDeps: false }], 'need to add foo@2.x')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foo' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
})
-t.test('npm exec --package=foo bar', async t => {
+t.test('npm exec --package=foo bar', t => {
const path = t.testdir()
npm.localPrefix = path
ARB_ACTUAL_TREE[path] = {
@@ -399,27 +413,28 @@ t.test('npm exec --package=foo bar', async t => {
_from: 'foo@',
}
npm.flatOptions.package = ['foo']
- await exec(['bar', 'one arg', 'two arg'], er => {
+ exec.exec(['bar', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [], 'no need to make any dirs')
+ t.match(ARB_CTOR, [{ package: ['foo'], path }])
+ t.strictSame(ARB_REIFY, [], 'no need to reify anything')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'bar' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH: process.env.PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [], 'no need to make any dirs')
- t.match(ARB_CTOR, [{ package: ['foo'], path }])
- t.strictSame(ARB_REIFY, [], 'no need to reify anything')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'bar' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH: process.env.PATH },
- stdio: 'inherit',
- }])
})
-t.test('npm exec @foo/bar -- --some=arg, locally installed', async t => {
+t.test('npm exec @foo/bar -- --some=arg, locally installed', t => {
const foobarManifest = {
name: '@foo/bar',
version: '1.2.3',
@@ -440,27 +455,28 @@ t.test('npm exec @foo/bar -- --some=arg, locally installed', async t => {
children: new Map([['@foo/bar', { name: '@foo/bar', version: '1.2.3' }]]),
}
MANIFESTS['@foo/bar'] = foobarManifest
- await exec(['@foo/bar', '--some=arg'], er => {
+ exec.exec(['@foo/bar', '--some=arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [], 'no need to make any dirs')
+ t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }])
+ t.strictSame(ARB_REIFY, [], 'no need to reify anything')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'bar' } },
+ args: ['--some=arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH: process.env.PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [], 'no need to make any dirs')
- t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }])
- t.strictSame(ARB_REIFY, [], 'no need to reify anything')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'bar' } },
- args: ['--some=arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH: process.env.PATH },
- stdio: 'inherit',
- }])
})
-t.test('npm exec @foo/bar, with same bin alias and no unscoped named bin, locally installed', async t => {
+t.test('npm exec @foo/bar, with same bin alias and no unscoped named bin, locally installed', t => {
const foobarManifest = {
name: '@foo/bar',
version: '1.2.3',
@@ -482,24 +498,25 @@ t.test('npm exec @foo/bar, with same bin alias and no unscoped named bin, locall
children: new Map([['@foo/bar', { name: '@foo/bar', version: '1.2.3' }]]),
}
MANIFESTS['@foo/bar'] = foobarManifest
- await exec(['@foo/bar', 'one arg', 'two arg'], er => {
+ exec.exec(['@foo/bar', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [], 'no need to make any dirs')
+ t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }])
+ t.strictSame(ARB_REIFY, [], 'no need to reify anything')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'baz' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH: process.env.PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [], 'no need to make any dirs')
- t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }])
- t.strictSame(ARB_REIFY, [], 'no need to reify anything')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'baz' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH: process.env.PATH },
- stdio: 'inherit',
- }])
})
t.test('npm exec @foo/bar, with different bin alias and no unscoped named bin, locally installed', t => {
@@ -519,12 +536,12 @@ t.test('npm exec @foo/bar, with different bin alias and no unscoped named bin, l
_from: 'foo@',
_id: '@foo/bar@1.2.3',
}
- return t.rejects(exec(['@foo/bar'], er => {
- if (er)
- throw er
- }), {
- message: 'could not determine executable to run',
- pkgid: '@foo/bar@1.2.3',
+ exec.exec(['@foo/bar'], er => {
+ t.match(er, {
+ message: 'could not determine executable to run',
+ pkgid: '@foo/bar@1.2.3',
+ })
+ t.end()
})
})
@@ -534,7 +551,7 @@ t.test('run command with 2 packages, need install, verify sort', t => {
const cases = [['foo', 'bar'], ['bar', 'foo']]
t.plan(cases.length)
for (const packages of cases) {
- t.test(packages.join(', '), async t => {
+ t.test(packages.join(', '), t => {
npm.flatOptions.package = packages
const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b))
const path = t.testdir()
@@ -562,25 +579,26 @@ t.test('run command with 2 packages, need install, verify sort', t => {
},
_from: 'bar@',
}
- await exec(['foobar', 'one arg', 'two arg'], er => {
+ exec.exec(['foobar', 'one arg', 'two arg'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foobar' } },
+ args: ['one arg', 'two arg'],
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foobar' } },
- args: ['one arg', 'two arg'],
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
})
}
})
@@ -597,12 +615,12 @@ t.test('npm exec foo, no bin in package', t => {
_from: 'foo@',
_id: 'foo@1.2.3',
}
- return t.rejects(exec(['foo'], er => {
- if (er)
- throw er
- }), {
- message: 'could not determine executable to run',
- pkgid: 'foo@1.2.3',
+ exec.exec(['foo'], er => {
+ t.match(er, {
+ message: 'could not determine executable to run',
+ pkgid: 'foo@1.2.3',
+ })
+ t.end()
})
})
@@ -622,16 +640,16 @@ t.test('npm exec foo, many bins in package, none named foo', t => {
_from: 'foo@',
_id: 'foo@1.2.3',
}
- return t.rejects(exec(['foo'], er => {
- if (er)
- throw er
- }), {
- message: 'could not determine executable to run',
- pkgid: 'foo@1.2.3',
+ exec.exec(['foo'], er => {
+ t.match(er, {
+ message: 'could not determine executable to run',
+ pkgid: 'foo@1.2.3',
+ })
+ t.end()
})
})
-t.test('npm exec -p foo -c "ls -laF"', async t => {
+t.test('npm exec -p foo -c "ls -laF"', t => {
const path = t.testdir()
npm.localPrefix = path
npm.flatOptions.package = ['foo']
@@ -644,31 +662,35 @@ t.test('npm exec -p foo -c "ls -laF"', async t => {
version: '1.2.3',
_from: 'foo@',
}
- await exec([], er => {
+ exec.exec([], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [], 'no need to make any dirs')
+ t.match(ARB_CTOR, [{ package: ['foo'], path }])
+ t.strictSame(ARB_REIFY, [], 'no need to reify anything')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'ls -laF' } },
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH: process.env.PATH },
+ stdio: 'inherit',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [], 'no need to make any dirs')
- t.match(ARB_CTOR, [{ package: ['foo'], path }])
- t.strictSame(ARB_REIFY, [], 'no need to reify anything')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'ls -laF' } },
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH: process.env.PATH },
- stdio: 'inherit',
- }])
})
t.test('positional args and --call together is an error', t => {
npm.flatOptions.call = 'true'
- return exec(['foo'], er => t.equal(er, exec.usage))
+ exec.exec(['foo'], er => {
+ t.equal(er, exec.usage)
+ t.end()
+ })
})
-t.test('prompt when installs are needed if not already present and shell is a TTY', async t => {
+t.test('prompt when installs are needed if not already present and shell is a TTY', t => {
const stdoutTTY = process.stdout.isTTY
const stdinTTY = process.stdin.isTTY
t.teardown(() => {
@@ -712,31 +734,32 @@ t.test('prompt when installs are needed if not already present and shell is a TT
},
_from: 'bar@',
}
- await exec(['foobar'], er => {
+ exec.exec(['foobar'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foobar' } },
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.strictSame(READ, [{
+ prompt: 'Need to install the following packages:\n bar\n foo\nOk to proceed? ',
+ default: 'y',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foobar' } },
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
- t.strictSame(READ, [{
- prompt: 'Need to install the following packages:\n bar\n foo\nOk to proceed? ',
- default: 'y',
- }])
})
-t.test('skip prompt when installs are needed if not already present and shell is not a tty (multiple packages)', async t => {
+t.test('skip prompt when installs are needed if not already present and shell is not a tty (multiple packages)', t => {
const stdoutTTY = process.stdout.isTTY
const stdinTTY = process.stdin.isTTY
t.teardown(() => {
@@ -780,29 +803,30 @@ t.test('skip prompt when installs are needed if not already present and shell is
},
_from: 'bar@',
}
- await exec(['foobar'], er => {
+ exec.exec(['foobar'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foobar' } },
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.strictSame(READ, [], 'should not have prompted')
+ t.strictSame(LOG_WARN, [['exec', 'The following packages were not found and will be installed: bar, foo']], 'should have printed a warning')
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foobar' } },
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
- t.strictSame(READ, [], 'should not have prompted')
- t.strictSame(LOG_WARN, [['exec', 'The following packages were not found and will be installed: bar, foo']], 'should have printed a warning')
})
-t.test('skip prompt when installs are needed if not already present and shell is not a tty (single package)', async t => {
+t.test('skip prompt when installs are needed if not already present and shell is not a tty (single package)', t => {
const stdoutTTY = process.stdout.isTTY
const stdinTTY = process.stdin.isTTY
t.teardown(() => {
@@ -838,29 +862,30 @@ t.test('skip prompt when installs are needed if not already present and shell is
},
_from: 'foo@',
}
- await exec(['foobar'], er => {
+ exec.exec(['foobar'], er => {
if (er)
throw er
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install the package')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
+ t.match(RUN_SCRIPTS, [{
+ pkg: { scripts: { npx: 'foobar' } },
+ banner: false,
+ path: process.cwd(),
+ stdioString: true,
+ event: 'npx',
+ env: { PATH },
+ stdio: 'inherit',
+ }])
+ t.strictSame(READ, [], 'should not have prompted')
+ t.strictSame(LOG_WARN, [['exec', 'The following package was not found and will be installed: foo']], 'should have printed a warning')
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install the package')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}`
- t.match(RUN_SCRIPTS, [{
- pkg: { scripts: { npx: 'foobar' } },
- banner: false,
- path: process.cwd(),
- stdioString: true,
- event: 'npx',
- env: { PATH },
- stdio: 'inherit',
- }])
- t.strictSame(READ, [], 'should not have prompted')
- t.strictSame(LOG_WARN, [['exec', 'The following package was not found and will be installed: foo']], 'should have printed a warning')
})
-t.test('abort if prompt rejected', async t => {
+t.test('abort if prompt rejected', t => {
const stdoutTTY = process.stdout.isTTY
const stdinTTY = process.stdin.isTTY
t.teardown(() => {
@@ -903,21 +928,22 @@ t.test('abort if prompt rejected', async t => {
},
_from: 'bar@',
}
- await exec(['foobar'], er => {
+ exec.exec(['foobar'], er => {
t.equal(er, 'canceled', 'should be canceled')
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.strictSame(ARB_REIFY, [], 'no install performed')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.strictSame(RUN_SCRIPTS, [])
+ t.strictSame(READ, [{
+ prompt: 'Need to install the following packages:\n bar\n foo\nOk to proceed? ',
+ default: 'y',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.strictSame(ARB_REIFY, [], 'no install performed')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.strictSame(RUN_SCRIPTS, [])
- t.strictSame(READ, [{
- prompt: 'Need to install the following packages:\n bar\n foo\nOk to proceed? ',
- default: 'y',
- }])
})
-t.test('abort if prompt false', async t => {
+t.test('abort if prompt false', t => {
const stdoutTTY = process.stdout.isTTY
const stdinTTY = process.stdin.isTTY
t.teardown(() => {
@@ -960,21 +986,22 @@ t.test('abort if prompt false', async t => {
},
_from: 'bar@',
}
- await exec(['foobar'], er => {
+ exec.exec(['foobar'], er => {
t.equal(er, 'canceled', 'should be canceled')
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.strictSame(ARB_REIFY, [], 'no install performed')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.strictSame(RUN_SCRIPTS, [])
+ t.strictSame(READ, [{
+ prompt: 'Need to install the following packages:\n bar\n foo\nOk to proceed? ',
+ default: 'y',
+ }])
+ t.end()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.strictSame(ARB_REIFY, [], 'no install performed')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.strictSame(RUN_SCRIPTS, [])
- t.strictSame(READ, [{
- prompt: 'Need to install the following packages:\n bar\n foo\nOk to proceed? ',
- default: 'y',
- }])
})
-t.test('abort if -n provided', async t => {
+t.test('abort if -n provided', t => {
const stdoutTTY = process.stdout.isTTY
const stdinTTY = process.stdin.isTTY
t.teardown(() => {
@@ -1016,18 +1043,19 @@ t.test('abort if -n provided', async t => {
},
_from: 'bar@',
}
- await exec(['foobar'], er => {
+ exec.exec(['foobar'], er => {
t.equal(er, 'canceled', 'should be canceled')
+ t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
+ t.match(ARB_CTOR, [{ package: packages, path }])
+ t.strictSame(ARB_REIFY, [], 'no install performed')
+ t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
+ t.strictSame(RUN_SCRIPTS, [])
+ t.strictSame(READ, [])
+ t.done()
})
- t.strictSame(MKDIRPS, [installDir], 'need to make install dir')
- t.match(ARB_CTOR, [{ package: packages, path }])
- t.strictSame(ARB_REIFY, [], 'no install performed')
- t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')
- t.strictSame(RUN_SCRIPTS, [])
- t.strictSame(READ, [])
})
-t.test('forward legacyPeerDeps opt', async t => {
+t.test('forward legacyPeerDeps opt', t => {
const path = t.testdir()
const installDir = resolve('cache-dir/_npx/f7fbba6e0636f890')
npm.localPrefix = path
@@ -1047,9 +1075,10 @@ t.test('forward legacyPeerDeps opt', async t => {
}
npm.flatOptions.yes = true
npm.flatOptions.legacyPeerDeps = true
- await exec(['foo'], er => {
+ exec.exec(['foo'], er => {
if (er)
throw er
+ t.match(ARB_REIFY, [{add: ['foo@'], legacyPeerDeps: true}], 'need to install foo@ using legacyPeerDeps opt')
+ t.done()
})
- t.match(ARB_REIFY, [{add: ['foo@'], legacyPeerDeps: true}], 'need to install foo@ using legacyPeerDeps opt')
})
diff --git a/test/lib/explain.js b/test/lib/explain.js
index 1eeca8c4c..22bfb8639 100644
--- a/test/lib/explain.js
+++ b/test/lib/explain.js
@@ -9,9 +9,7 @@ const { resolve } = require('path')
const OUTPUT = []
-const explain = requireInject('../../lib/explain.js', {
- '../../lib/npm.js': npm,
-
+const Explain = requireInject('../../lib/explain.js', {
'../../lib/utils/output.js': (...args) => {
OUTPUT.push(args)
},
@@ -23,44 +21,40 @@ const explain = requireInject('../../lib/explain.js', {
},
},
})
+const explain = new Explain(npm)
-t.test('no args throws usage', async t => {
+t.test('no args throws usage', t => {
t.plan(1)
- try {
- await explain([], er => {
- throw er
- })
- } catch (er) {
+ explain.exec([], er => {
t.equal(er, explain.usage)
- }
+ t.done()
+ })
})
-t.test('no match throws not found', async t => {
+t.test('no match throws not found', t => {
npm.prefix = t.testdir()
t.plan(1)
- try {
- await explain(['foo@1.2.3', 'node_modules/baz'], er => {
- throw er
- })
- } catch (er) {
+ explain.exec(['foo@1.2.3', 'node_modules/baz'], er => {
t.equal(er, 'No dependencies found matching foo@1.2.3, node_modules/baz')
- }
+ })
})
-t.test('invalid package name throws not found', async t => {
+t.test('invalid package name throws not found', t => {
npm.prefix = t.testdir()
t.plan(1)
const badName = ' not a valid package name '
- try {
- await explain([`${badName}@1.2.3`], er => {
- throw er
- })
- } catch (er) {
+ explain.exec([`${badName}@1.2.3`], er => {
t.equal(er, `No dependencies found matching ${badName}@1.2.3`)
- }
+ })
})
-t.test('explain some nodes', async t => {
+t.test('explain some nodes', t => {
+ t.afterEach((cb) => {
+ OUTPUT.length = 0
+ npm.flatOptions.json = false
+ cb()
+ })
+
npm.prefix = t.testdir({
node_modules: {
foo: {
@@ -111,61 +105,75 @@ t.test('explain some nodes', async t => {
}),
})
- // works with either a full actual path or the location
- const p = 'node_modules/foo'
- for (const path of [p, resolve(npm.prefix, p)]) {
- await explain([path], er => {
+ t.test('works with the location', t => {
+ const path = 'node_modules/foo'
+ explain.exec([path], er => {
if (er)
throw er
+ t.strictSame(OUTPUT, [['foo@1.2.3 depth=Infinity color=true']])
+ t.end()
})
- t.strictSame(OUTPUT, [['foo@1.2.3 depth=Infinity color=true']])
- OUTPUT.length = 0
- }
+ })
+ t.test('works with a full actual path', t => {
+ const path = resolve(npm.prefix, 'node_modules/foo')
+ explain.exec([path], er => {
+ if (er)
+ throw er
+ t.strictSame(OUTPUT, [['foo@1.2.3 depth=Infinity color=true']])
+ t.end()
+ })
+ })
- // finds all nodes by name
- await explain(['bar'], er => {
- if (er)
- throw er
+ t.test('finds all nodes by name', t => {
+ explain.exec(['bar'], er => {
+ if (er)
+ throw er
+ t.strictSame(OUTPUT, [[
+ 'bar@1.2.3 depth=Infinity color=true\n\n' +
+ 'bar@2.3.4 depth=Infinity color=true',
+ ]])
+ t.end()
+ })
})
- t.strictSame(OUTPUT, [[
- 'bar@1.2.3 depth=Infinity color=true\n\n' +
- 'bar@2.3.4 depth=Infinity color=true',
- ]])
- OUTPUT.length = 0
- // finds only nodes that match the spec
- await explain(['bar@1'], er => {
- if (er)
- throw er
+ t.test('finds only nodes that match the spec', t => {
+ explain.exec(['bar@1'], er => {
+ if (er)
+ throw er
+ t.strictSame(OUTPUT, [['bar@1.2.3 depth=Infinity color=true']])
+ t.end()
+ })
})
- t.strictSame(OUTPUT, [['bar@1.2.3 depth=Infinity color=true']])
- OUTPUT.length = 0
- // finds extraneous nodes
- await explain(['extra'], er => {
- if (er)
- throw er
+ t.test('finds extraneous nodes', t => {
+ explain.exec(['extra'], er => {
+ if (er)
+ throw er
+ t.strictSame(OUTPUT, [['extra@99.9999.999999 depth=Infinity color=true']])
+ t.end()
+ })
})
- t.strictSame(OUTPUT, [['extra@99.9999.999999 depth=Infinity color=true']])
- OUTPUT.length = 0
- npm.flatOptions.json = true
- await explain(['node_modules/foo'], er => {
- if (er)
- throw er
+ t.test('json output', t => {
+ npm.flatOptions.json = true
+ explain.exec(['node_modules/foo'], er => {
+ if (er)
+ throw er
+ t.match(JSON.parse(OUTPUT[0][0]), [{
+ name: 'foo',
+ version: '1.2.3',
+ dependents: Array,
+ }])
+ t.end()
+ })
})
- t.match(JSON.parse(OUTPUT[0][0]), [{
- name: 'foo',
- version: '1.2.3',
- dependents: Array,
- }])
- OUTPUT.length = 0
- npm.flatOptions.json = false
- t.test('report if no nodes found', async t => {
+ t.test('report if no nodes found', t => {
t.plan(1)
- await explain(['asdf/foo/bar', 'quux@1.x'], er => {
+ explain.exec(['asdf/foo/bar', 'quux@1.x'], er => {
t.equal(er, 'No dependencies found matching asdf/foo/bar, quux@1.x')
+ t.done()
})
})
+ t.end()
})
diff --git a/test/lib/explore.js b/test/lib/explore.js
index 23eab1172..6f1f3bb47 100644
--- a/test/lib/explore.js
+++ b/test/lib/explore.js
@@ -46,14 +46,20 @@ const mockRunScript = ({ pkg, banner, path, event, stdio }) => {
const output = []
let ERROR_HANDLER_CALLED = null
const logs = []
-const getExplore = windows => requireInject('../../lib/explore.js', {
- '../../lib/utils/is-windows.js': windows,
- path: require('path')[windows ? 'win32' : 'posix'],
- '../../lib/utils/error-handler.js': er => {
- ERROR_HANDLER_CALLED = er
- },
- 'read-package-json-fast': mockRPJ,
- '../../lib/npm.js': {
+const getExplore = (windows) => {
+ const Explore = requireInject('../../lib/explore.js', {
+ '../../lib/utils/is-windows.js': windows,
+ path: require('path')[windows ? 'win32' : 'posix'],
+ '../../lib/utils/error-handler.js': er => {
+ ERROR_HANDLER_CALLED = er
+ },
+ 'read-package-json-fast': mockRPJ,
+ '@npmcli/run-script': mockRunScript,
+ '../../lib/utils/output.js': out => {
+ output.push(out)
+ },
+ })
+ const npm = {
dir: windows ? 'c:\\npm\\dir' : '/npm/dir',
log: {
error: (...msg) => logs.push(msg),
@@ -63,12 +69,9 @@ const getExplore = windows => requireInject('../../lib/explore.js', {
flatOptions: {
shell: 'shell-command',
},
- },
- '@npmcli/run-script': mockRunScript,
- '../../lib/utils/output.js': out => {
- output.push(out)
- },
-})
+ }
+ return new Explore(npm)
+}
const windowsExplore = getExplore(true)
const posixExplore = getExplore(false)
@@ -79,7 +82,7 @@ t.test('basic interactive', t => {
cb()
})
- t.test('windows', t => windowsExplore(['pkg'], er => {
+ t.test('windows', t => windowsExplore.exec(['pkg'], er => {
if (er)
throw er
@@ -95,9 +98,10 @@ t.test('basic interactive', t => {
t.strictSame(output, [
"\nExploring c:\\npm\\dir\\pkg\nType 'exit' or ^D when finished\n",
])
+ t.end()
}))
- t.test('posix', t => posixExplore(['pkg'], er => {
+ t.test('posix', t => posixExplore.exec(['pkg'], er => {
if (er)
throw er
@@ -113,6 +117,7 @@ t.test('basic interactive', t => {
t.strictSame(output, [
"\nExploring /npm/dir/pkg\nType 'exit' or ^D when finished\n",
])
+ t.end()
}))
t.end()
@@ -132,7 +137,7 @@ t.test('interactive tracks exit code', t => {
cb()
})
- t.test('windows', t => windowsExplore(['pkg'], er => {
+ t.test('windows', t => windowsExplore.exec(['pkg'], er => {
if (er)
throw er
@@ -149,9 +154,10 @@ t.test('interactive tracks exit code', t => {
"\nExploring c:\\npm\\dir\\pkg\nType 'exit' or ^D when finished\n",
])
t.equal(process.exitCode, 99)
+ t.end()
}))
- t.test('posix', t => posixExplore(['pkg'], er => {
+ t.test('posix', t => posixExplore.exec(['pkg'], er => {
if (er)
throw er
@@ -168,18 +174,20 @@ t.test('interactive tracks exit code', t => {
"\nExploring /npm/dir/pkg\nType 'exit' or ^D when finished\n",
])
t.equal(process.exitCode, 99)
+ t.end()
}))
t.test('posix spawn fail', t => {
RUN_SCRIPT_ERROR = Object.assign(new Error('glorb'), {
code: 33,
})
- return posixExplore(['pkg'], er => {
+ posixExplore.exec(['pkg'], er => {
t.match(er, { message: 'glorb', code: 33 })
t.strictSame(output, [
"\nExploring /npm/dir/pkg\nType 'exit' or ^D when finished\n",
])
t.equal(process.exitCode, 33)
+ t.end()
})
})
@@ -187,12 +195,13 @@ t.test('interactive tracks exit code', t => {
RUN_SCRIPT_ERROR = Object.assign(new Error('glorb'), {
code: 0,
})
- return posixExplore(['pkg'], er => {
+ posixExplore.exec(['pkg'], er => {
t.match(er, { message: 'glorb', code: 0 })
t.strictSame(output, [
"\nExploring /npm/dir/pkg\nType 'exit' or ^D when finished\n",
])
t.equal(process.exitCode, 1)
+ t.end()
})
})
@@ -200,12 +209,13 @@ t.test('interactive tracks exit code', t => {
RUN_SCRIPT_ERROR = Object.assign(new Error('command failed'), {
code: 'EPROBLEM',
})
- return posixExplore(['pkg'], er => {
+ posixExplore.exec(['pkg'], er => {
t.match(er, { message: 'command failed', code: 'EPROBLEM' })
t.strictSame(output, [
"\nExploring /npm/dir/pkg\nType 'exit' or ^D when finished\n",
])
t.equal(process.exitCode, 1)
+ t.end()
})
})
@@ -218,7 +228,7 @@ t.test('basic non-interactive', t => {
cb()
})
- t.test('windows', t => windowsExplore(['pkg', 'ls'], er => {
+ t.test('windows', t => windowsExplore.exec(['pkg', 'ls'], er => {
if (er)
throw er
@@ -232,9 +242,10 @@ t.test('basic non-interactive', t => {
RUN_SCRIPT_EXEC: 'ls',
})
t.strictSame(output, [])
+ t.end()
}))
- t.test('posix', t => posixExplore(['pkg', 'ls'], er => {
+ t.test('posix', t => posixExplore.exec(['pkg', 'ls'], er => {
if (er)
throw er
@@ -248,6 +259,7 @@ t.test('basic non-interactive', t => {
RUN_SCRIPT_EXEC: 'ls',
})
t.strictSame(output, [])
+ t.end()
}))
t.end()
@@ -272,7 +284,7 @@ t.test('signal fails non-interactive', t => {
cb()
})
- t.test('windows', t => windowsExplore(['pkg', 'ls'], er => {
+ t.test('windows', t => windowsExplore.exec(['pkg', 'ls'], er => {
t.match(er, {
message: 'command failed',
signal: 'SIGPROBLEM',
@@ -286,9 +298,10 @@ t.test('signal fails non-interactive', t => {
RUN_SCRIPT_EXEC: 'ls',
})
t.strictSame(output, [])
+ t.end()
}))
- t.test('posix', t => posixExplore(['pkg', 'ls'], er => {
+ t.test('posix', t => posixExplore.exec(['pkg', 'ls'], er => {
t.match(er, {
message: 'command failed',
signal: 'SIGPROBLEM',
@@ -302,6 +315,7 @@ t.test('signal fails non-interactive', t => {
RUN_SCRIPT_EXEC: 'ls',
})
t.strictSame(output, [])
+ t.end()
}))
t.end()
@@ -322,29 +336,28 @@ t.test('usage if no pkg provided', t => {
]
t.plan(noPkg.length)
for (const args of noPkg) {
- t.test(JSON.stringify(args), t => posixExplore(args, er => {
- t.equal(er, 'npm explore <pkg> [ -- <command>]')
- t.strictSame({
- ERROR_HANDLER_CALLED: null,
- RPJ_CALLED,
- RUN_SCRIPT_EXEC,
- }, {
- ERROR_HANDLER_CALLED: null,
- RPJ_CALLED: '/npm/dir/pkg/package.json',
- RUN_SCRIPT_EXEC: 'ls',
+ t.test(JSON.stringify(args), t => {
+ posixExplore.exec(args, er => {
+ t.equal(er, 'npm explore <pkg> [ -- <command>]')
+ t.strictSame({
+ ERROR_HANDLER_CALLED: null,
+ RPJ_CALLED,
+ RUN_SCRIPT_EXEC,
+ }, {
+ ERROR_HANDLER_CALLED: null,
+ RPJ_CALLED: '/npm/dir/pkg/package.json',
+ RUN_SCRIPT_EXEC: 'ls',
+ })
+ t.end()
})
- }))
+ })
}
})
t.test('pkg not installed', t => {
RPJ_ERROR = new Error('plurple')
- t.plan(2)
-
- posixExplore(['pkg', 'ls'], er => {
- if (er)
- throw er
+ posixExplore.exec(['pkg', 'ls'], er => {
t.strictSame({
ERROR_HANDLER_CALLED,
RPJ_CALLED,
@@ -355,9 +368,9 @@ t.test('pkg not installed', t => {
RUN_SCRIPT_EXEC: 'ls',
})
t.strictSame(output, [])
- }).catch(er => {
t.match(er, { message: 'plurple' })
t.match(logs, [['explore', `It doesn't look like pkg is installed.`]])
+ t.end()
logs.length = 0
})
})
diff --git a/test/lib/find-dupes.js b/test/lib/find-dupes.js
index 73c8fa2dc..c7b33ceb6 100644
--- a/test/lib/find-dupes.js
+++ b/test/lib/find-dupes.js
@@ -1,15 +1,24 @@
-const { test } = require('tap')
-const requireInject = require('require-inject')
+const t = require('tap')
-test('should run dedupe in dryRun mode', (t) => {
- const findDupes = requireInject('../../lib/find-dupes.js', {
- '../../lib/dedupe.js': function (args, cb) {
- t.ok(args.dryRun, 'dryRun is true')
- cb()
+const FindDupes = require('../../lib/find-dupes.js')
+
+t.test('should run dedupe in dryRun mode', (t) => {
+ t.plan(3)
+ const findDupesTest = new FindDupes({
+ config: {
+ set: (k, v) => {
+ t.match(k, 'dry-run')
+ t.match(v, true)
+ },
+ },
+ commands: {
+ dedupe: (args, cb) => {
+ t.match(args, [])
+ cb()
+ },
},
})
- findDupes(null, () => {
- t.ok(true, 'callback is called')
+ findDupesTest.exec({}, () => {
t.end()
})
})
diff --git a/test/lib/fund.js b/test/lib/fund.js
index 73f639b6c..831d76f15 100644
--- a/test/lib/fund.js
+++ b/test/lib/fund.js
@@ -188,11 +188,10 @@ const _flatOptions = {
unicode: false,
which: undefined,
}
-const openUrl = (url, msg, cb) => {
- if (url === 'http://npmjs.org') {
- cb(new Error('ERROR'))
- return
- }
+const openUrl = async (npm, url, msg) => {
+ if (url === 'http://npmjs.org')
+ throw new Error('ERROR')
+
if (_flatOptions.json) {
printUrl = JSON.stringify({
title: msg,
@@ -200,16 +199,8 @@ const openUrl = (url, msg, cb) => {
})
} else
printUrl = `${msg}:\n ${url}`
-
- cb()
}
-const fund = requireInject('../../lib/fund.js', {
- '../../lib/npm.js': {
- flatOptions: _flatOptions,
- get prefix () {
- return _flatOptions.prefix
- },
- },
+const Fund = requireInject('../../lib/fund.js', {
'../../lib/utils/open-url.js': openUrl,
'../../lib/utils/output.js': msg => {
result += msg + '\n'
@@ -222,6 +213,12 @@ const fund = requireInject('../../lib/fund.js', {
: Promise.reject(new Error('ERROR')),
},
})
+const fund = new Fund({
+ flatOptions: _flatOptions,
+ get prefix () {
+ return _flatOptions.prefix
+ },
+})
test('fund with no package containing funding', t => {
_flatOptions.prefix = t.testdir({
@@ -231,7 +228,7 @@ test('fund with no package containing funding', t => {
}),
})
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should print empty funding info')
result = ''
@@ -242,7 +239,7 @@ test('fund with no package containing funding', t => {
test('fund in which same maintainer owns all its deps', t => {
_flatOptions.prefix = t.testdir(maintainerOwnsAllDeps)
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should print stack packages together')
result = ''
@@ -254,7 +251,7 @@ test('fund in which same maintainer owns all its deps, using --json option', t =
_flatOptions.json = true
_flatOptions.prefix = t.testdir(maintainerOwnsAllDeps)
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
JSON.parse(result),
@@ -292,7 +289,7 @@ test('fund in which same maintainer owns all its deps, using --json option', t =
test('fund containing multi-level nested deps with no funding', t => {
_flatOptions.prefix = t.testdir(nestedNoFundingPackages)
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(
result,
@@ -308,7 +305,7 @@ test('fund containing multi-level nested deps with no funding, using --json opti
_flatOptions.prefix = t.testdir(nestedNoFundingPackages)
_flatOptions.json = true
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
JSON.parse(result),
@@ -340,7 +337,7 @@ test('fund containing multi-level nested deps with no funding, using --json opti
_flatOptions.prefix = t.testdir(nestedMultipleFundingPackages)
_flatOptions.json = true
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
JSON.parse(result),
@@ -397,7 +394,7 @@ test('fund does not support global', t => {
_flatOptions.prefix = t.testdir({})
_flatOptions.global = true
- fund([], (err) => {
+ fund.exec([], (err) => {
t.match(err.code, 'EFUNDGLOBAL', 'should throw EFUNDGLOBAL error')
result = ''
@@ -409,7 +406,7 @@ test('fund does not support global', t => {
test('fund using package argument', t => {
_flatOptions.prefix = t.testdir(maintainerOwnsAllDeps)
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(printUrl, 'should open funding url')
@@ -423,7 +420,7 @@ test('fund does not support global, using --json option', t => {
_flatOptions.global = true
_flatOptions.json = true
- fund([], (err) => {
+ fund.exec([], (err) => {
t.equal(err.code, 'EFUNDGLOBAL', 'should use EFUNDGLOBAL error code')
t.equal(
err.message,
@@ -446,7 +443,7 @@ test('fund using string shorthand', t => {
}),
})
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(printUrl, 'should open string-only url')
@@ -458,7 +455,7 @@ test('fund using string shorthand', t => {
test('fund using nested packages with multiple sources', t => {
_flatOptions.prefix = t.testdir(nestedMultipleFundingPackages)
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should prompt with all available URLs')
@@ -486,7 +483,7 @@ test('fund using symlink ref', t => {
})
// using symlinked ref
- fund(['./node_modules/a'], (err) => {
+ fund.exec(['./node_modules/a'], (err) => {
t.ifError(err, 'should not error out')
t.match(
printUrl,
@@ -497,7 +494,7 @@ test('fund using symlink ref', t => {
printUrl = ''
// using target ref
- fund(['./a'], (err) => {
+ fund.exec(['./a'], (err) => {
t.ifError(err, 'should not error out')
t.match(
@@ -547,7 +544,7 @@ test('fund using data from actual tree', t => {
})
// using symlinked ref
- fund(['a'], (err) => {
+ fund.exec(['a'], (err) => {
t.ifError(err, 'should not error out')
t.match(
printUrl,
@@ -564,7 +561,7 @@ test('fund using nested packages with multiple sources, with a source number', t
_flatOptions.prefix = t.testdir(nestedMultipleFundingPackages)
_flatOptions.which = '1'
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(printUrl, 'should open the numbered URL')
@@ -578,7 +575,7 @@ test('fund using pkg name while having conflicting versions', t => {
_flatOptions.prefix = t.testdir(conflictingFundingPackages)
_flatOptions.which = '1'
- fund(['foo'], (err) => {
+ fund.exec(['foo'], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(printUrl, 'should open greatest version')
@@ -591,7 +588,7 @@ test('fund using package argument with no browser, using --json option', t => {
_flatOptions.prefix = t.testdir(maintainerOwnsAllDeps)
_flatOptions.json = true
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
JSON.parse(printUrl),
@@ -611,7 +608,7 @@ test('fund using package argument with no browser, using --json option', t => {
test('fund using package info fetch from registry', t => {
_flatOptions.prefix = t.testdir({})
- fund(['ntl'], (err) => {
+ fund.exec(['ntl'], (err) => {
t.ifError(err, 'should not error out')
t.match(
printUrl,
@@ -627,7 +624,7 @@ test('fund using package info fetch from registry', t => {
test('fund tries to use package info fetch from registry but registry has nothing', t => {
_flatOptions.prefix = t.testdir({})
- fund(['foo'], (err) => {
+ fund.exec(['foo'], (err) => {
t.equal(err.code, 'ENOFUND', 'should have ENOFUND error code')
t.equal(
err.message,
@@ -643,7 +640,7 @@ test('fund tries to use package info fetch from registry but registry has nothin
test('fund but target module has no funding info', t => {
_flatOptions.prefix = t.testdir(nestedNoFundingPackages)
- fund(['foo'], (err) => {
+ fund.exec(['foo'], (err) => {
t.equal(err.code, 'ENOFUND', 'should have ENOFUND error code')
t.equal(
err.message,
@@ -660,7 +657,7 @@ test('fund using bad which value', t => {
_flatOptions.prefix = t.testdir(nestedMultipleFundingPackages)
_flatOptions.which = 3
- fund(['bar'], (err) => {
+ fund.exec(['bar'], (err) => {
t.equal(err.code, 'EFUNDNUMBER', 'should have EFUNDNUMBER error code')
t.equal(
err.message,
@@ -682,7 +679,7 @@ test('fund pkg missing version number', t => {
}),
})
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should print name only')
result = ''
@@ -699,7 +696,7 @@ test('fund a package throws on openUrl', t => {
}),
})
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.equal(err.message, 'ERROR', 'should throw unknown error')
result = ''
t.end()
@@ -723,7 +720,7 @@ test('fund a package with type and multiple sources', t => {
}),
})
- fund(['.'], (err) => {
+ fund.exec(['.'], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should print prompt select message')
@@ -787,7 +784,7 @@ test('fund colors', t => {
})
_flatOptions.color = true
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should print output with color info')
@@ -837,7 +834,7 @@ test('sub dep with fund info and a parent with no funding info', t => {
},
})
- fund([], (err) => {
+ fund.exec([], (err) => {
t.ifError(err, 'should not error out')
t.matchSnapshot(result, 'should nest sub dep as child of root')
diff --git a/test/lib/get.js b/test/lib/get.js
index 5260c00ba..a11597d26 100644
--- a/test/lib/get.js
+++ b/test/lib/get.js
@@ -2,17 +2,16 @@ const { test } = require('tap')
const requireInject = require('require-inject')
test('should retrieve values from npm.commands.config', (t) => {
- const get = requireInject('../../lib/get.js', {
- '../../lib/npm.js': {
- commands: {
- config: ([action, arg]) => {
- t.equal(action, 'get', 'should use config get action')
- t.equal(arg, 'foo', 'should use expected key')
- t.end()
- },
+ const Get = requireInject('../../lib/get.js')
+ const get = new Get({
+ commands: {
+ config: ([action, arg]) => {
+ t.equal(action, 'get', 'should use config get action')
+ t.equal(arg, 'foo', 'should use expected key')
+ t.end()
},
},
})
- get(['foo'])
+ get.exec(['foo'])
})
diff --git a/test/lib/help-search.js b/test/lib/help-search.js
index f74e2f1ef..8b1ecd46e 100644
--- a/test/lib/help-search.js
+++ b/test/lib/help-search.js
@@ -24,7 +24,7 @@ const npm = {
}
let npmUsageArg = null
-const npmUsage = (arg) => {
+const npmUsage = (npm, arg) => {
npmUsageArg = arg
}
@@ -43,12 +43,12 @@ const globDir = {
const glob = (p, cb) =>
cb(null, Object.keys(globDir).map((file) => join(globRoot, file)))
-const helpSearch = requireInject('../../lib/help-search.js', {
- '../../lib/npm.js': npm,
+const HelpSearch = requireInject('../../lib/help-search.js', {
'../../lib/utils/npm-usage.js': npmUsage,
'../../lib/utils/output.js': output,
glob,
})
+const helpSearch = new HelpSearch(npm)
test('npm help-search', t => {
globRoot = t.testdir(globDir)
@@ -57,7 +57,7 @@ test('npm help-search', t => {
globRoot = null
})
- return helpSearch(['exec'], (err) => {
+ return helpSearch.exec(['exec'], (err) => {
if (err)
throw err
@@ -74,7 +74,7 @@ test('npm help-search multiple terms', t => {
globRoot = null
})
- return helpSearch(['run', 'script'], (err) => {
+ return helpSearch.exec(['run', 'script'], (err) => {
if (err)
throw err
@@ -92,7 +92,7 @@ test('npm help-search single result prints full section', t => {
globRoot = null
})
- return helpSearch(['does not exist in'], (err) => {
+ return helpSearch.exec(['does not exist in'], (err) => {
if (err)
throw err
@@ -111,7 +111,7 @@ test('npm help-search single result propagates error', t => {
globRoot = null
})
- return helpSearch(['does not exist in'], (err) => {
+ return helpSearch.exec(['does not exist in'], (err) => {
t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it')
t.match(err, /help broke/, 'propagated the error from help')
t.end()
@@ -127,7 +127,7 @@ test('npm help-search long output', t => {
globRoot = null
})
- return helpSearch(['exec'], (err) => {
+ return helpSearch.exec(['exec'], (err) => {
if (err)
throw err
@@ -147,7 +147,7 @@ test('npm help-search long output with color', t => {
globRoot = null
})
- return helpSearch(['help-search'], (err) => {
+ return helpSearch.exec(['help-search'], (err) => {
if (err)
throw err
@@ -158,7 +158,7 @@ test('npm help-search long output with color', t => {
})
test('npm help-search no args', t => {
- return helpSearch([], (err) => {
+ return helpSearch.exec([], (err) => {
t.match(err, /npm help-search/, 'throws usage')
t.end()
})
@@ -172,7 +172,7 @@ test('npm help-search no matches', t => {
globRoot = null
})
- return helpSearch(['asdfasdf'], (err) => {
+ return helpSearch.exec(['asdfasdf'], (err) => {
if (err)
throw err
diff --git a/test/lib/help.js b/test/lib/help.js
index fc4a32e07..addbe4dcc 100644
--- a/test/lib/help.js
+++ b/test/lib/help.js
@@ -3,7 +3,7 @@ const requireInject = require('require-inject')
const { EventEmitter } = require('events')
let npmUsageArg = null
-const npmUsage = (arg) => {
+const npmUsage = (npm, arg) => {
npmUsageArg = arg
}
@@ -67,13 +67,11 @@ const spawn = (bin, args) => {
}
let openUrlArg = null
-const openUrl = (url, msg, cb) => {
+const openUrl = async (npm, url, msg) => {
openUrlArg = url
- return cb()
}
-const help = requireInject('../../lib/help.js', {
- '../../lib/npm.js': npm,
+const Help = requireInject('../../lib/help.js', {
'../../lib/utils/npm-usage.js': npmUsage,
'../../lib/utils/open-url.js': openUrl,
'../../lib/utils/output.js': output,
@@ -82,13 +80,14 @@ const help = requireInject('../../lib/help.js', {
},
glob,
})
+const help = new Help(npm)
test('npm help', t => {
t.teardown(() => {
npmUsageArg = null
})
- return help([], (err) => {
+ return help.exec([], (err) => {
if (err)
throw err
@@ -117,7 +116,7 @@ test('npm help -h', t => {
OUTPUT.length = 0
})
- return help(['help'], (err) => {
+ return help.exec(['help'], (err) => {
if (err)
throw err
@@ -131,7 +130,7 @@ test('npm help multiple args calls search', t => {
helpSearchArgs = null
})
- return help(['run', 'script'], (err) => {
+ return help.exec(['run', 'script'], (err) => {
if (err)
throw err
@@ -147,7 +146,7 @@ test('npm help no matches calls search', t => {
globResult = globDefaults
})
- return help(['asdfasdf'], (err) => {
+ return help.exec(['asdfasdf'], (err) => {
if (err)
throw err
@@ -164,7 +163,7 @@ test('npm help glob errors propagate', t => {
spawnArgs = null
})
- return help(['whoami'], (err) => {
+ return help.exec(['whoami'], (err) => {
t.match(err, /glob failed/, 'glob error propagates')
t.end()
})
@@ -178,7 +177,7 @@ test('npm help whoami', t => {
spawnArgs = null
})
- return help(['whoami'], (err) => {
+ return help.exec(['whoami'], (err) => {
if (err)
throw err
@@ -202,7 +201,7 @@ test('npm help 1 install', t => {
spawnArgs = null
})
- return help(['1', 'install'], (err) => {
+ return help.exec(['1', 'install'], (err) => {
if (err)
throw err
@@ -225,7 +224,7 @@ test('npm help 5 install', t => {
spawnArgs = null
})
- return help(['5', 'install'], (err) => {
+ return help.exec(['5', 'install'], (err) => {
if (err)
throw err
@@ -247,7 +246,7 @@ test('npm help 7 config', t => {
spawnArgs = null
})
- return help(['7', 'config'], (err) => {
+ return help.exec(['7', 'config'], (err) => {
if (err)
throw err
@@ -270,7 +269,7 @@ test('npm help with browser viewer and invalid section throws', t => {
spawnArgs = null
})
- return help(['9', 'config'], (err) => {
+ return help.exec(['9', 'config'], (err) => {
t.match(err, /invalid man section: 9/, 'throws appropriate error')
t.end()
})
@@ -284,7 +283,7 @@ test('npm help global redirects to folders', t => {
spawnArgs = null
})
- return help(['global'], (err) => {
+ return help.exec(['global'], (err) => {
if (err)
throw err
@@ -302,7 +301,7 @@ test('npm help package.json redirects to package-json', t => {
spawnArgs = null
})
- return help(['package.json'], (err) => {
+ return help.exec(['package.json'], (err) => {
if (err)
throw err
@@ -325,7 +324,7 @@ test('npm help ?(un)star', t => {
spawnArgs = null
})
- return help(['?(un)star'], (err) => {
+ return help.exec(['?(un)star'], (err) => {
if (err)
throw err
@@ -350,7 +349,7 @@ test('npm help - woman viewer propagates errors', t => {
spawnArgs = null
})
- return help(['?(un)star'], (err) => {
+ return help.exec(['?(un)star'], (err) => {
t.match(err, /help process exited with code: 1/, 'received the correct error')
t.equal(spawnBin, 'emacsclient', 'maps woman to emacs correctly')
t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-unstar.1')`], 'passes the correct arguments')
@@ -370,7 +369,7 @@ test('npm help un*', t => {
spawnArgs = null
})
- return help(['un*'], (err) => {
+ return help.exec(['un*'], (err) => {
if (err)
throw err
@@ -394,7 +393,7 @@ test('npm help - man viewer propagates errors', t => {
spawnArgs = null
})
- return help(['un*'], (err) => {
+ return help.exec(['un*'], (err) => {
t.match(err, /help process exited with code: 1/, 'received correct error')
t.equal(spawnBin, 'man', 'calls man by default')
t.strictSame(spawnArgs, ['1', 'npm-unstar'], 'passes the correct arguments')
diff --git a/test/lib/hook.js b/test/lib/hook.js
index 359904202..923f86e81 100644
--- a/test/lib/hook.js
+++ b/test/lib/hook.js
@@ -52,17 +52,17 @@ const libnpmhook = {
}
const output = []
-const hook = requireInject('../../lib/hook.js', {
- '../../lib/npm.js': npm,
+const Hook = requireInject('../../lib/hook.js', {
'../../lib/utils/otplease.js': async (opts, fn) => fn(opts),
'../../lib/utils/output.js': (msg) => {
output.push(msg)
},
libnpmhook,
})
+const hook = new Hook(npm)
test('npm hook no args', t => {
- return hook([], (err) => {
+ return hook.exec([], (err) => {
t.match(err, /npm hook add/, 'throws usage with no arguments')
t.end()
})
@@ -74,7 +74,7 @@ test('npm hook add', t => {
output.length = 0
})
- return hook(['add', 'semver', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['add', 'semver', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -97,7 +97,7 @@ test('npm hook add - unicode output', t => {
output.length = 0
})
- return hook(['add', 'semver', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['add', 'semver', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -120,7 +120,7 @@ test('npm hook add - json output', t => {
output.length = 0
})
- return hook(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -148,7 +148,7 @@ test('npm hook add - parseable output', t => {
output.length = 0
})
- return hook(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -176,7 +176,7 @@ test('npm hook add - silent output', t => {
output.length = 0
})
- return hook(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['add', '@npmcli', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -197,7 +197,7 @@ test('npm hook ls', t => {
output.length = 0
})
- return hook(['ls'], (err) => {
+ return hook.exec(['ls'], (err) => {
if (err)
throw err
@@ -222,7 +222,7 @@ test('npm hook ls, no results', t => {
output.length = 0
})
- return hook(['ls'], (err) => {
+ return hook.exec(['ls'], (err) => {
if (err)
throw err
@@ -249,7 +249,7 @@ test('npm hook ls, single result', t => {
output.length = 0
})
- return hook(['ls'], (err) => {
+ return hook.exec(['ls'], (err) => {
if (err)
throw err
@@ -272,7 +272,7 @@ test('npm hook ls - json output', t => {
output.length = 0
})
- return hook(['ls'], (err) => {
+ return hook.exec(['ls'], (err) => {
if (err)
throw err
@@ -309,7 +309,7 @@ test('npm hook ls - parseable output', t => {
output.length = 0
})
- return hook(['ls'], (err) => {
+ return hook.exec(['ls'], (err) => {
if (err)
throw err
@@ -335,7 +335,7 @@ test('npm hook ls - silent output', t => {
output.length = 0
})
- return hook(['ls'], (err) => {
+ return hook.exec(['ls'], (err) => {
if (err)
throw err
@@ -354,7 +354,7 @@ test('npm hook rm', t => {
output.length = 0
})
- return hook(['rm', '1'], (err) => {
+ return hook.exec(['rm', '1'], (err) => {
if (err)
throw err
@@ -377,7 +377,7 @@ test('npm hook rm - unicode output', t => {
output.length = 0
})
- return hook(['rm', '1'], (err) => {
+ return hook.exec(['rm', '1'], (err) => {
if (err)
throw err
@@ -400,7 +400,7 @@ test('npm hook rm - silent output', t => {
output.length = 0
})
- return hook(['rm', '1'], (err) => {
+ return hook.exec(['rm', '1'], (err) => {
if (err)
throw err
@@ -421,7 +421,7 @@ test('npm hook rm - json output', t => {
output.length = 0
})
- return hook(['rm', '1'], (err) => {
+ return hook.exec(['rm', '1'], (err) => {
if (err)
throw err
@@ -447,7 +447,7 @@ test('npm hook rm - parseable output', t => {
output.length = 0
})
- return hook(['rm', '1'], (err) => {
+ return hook.exec(['rm', '1'], (err) => {
if (err)
throw err
@@ -469,7 +469,7 @@ test('npm hook update', t => {
output.length = 0
})
- return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['update', '1', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -494,7 +494,7 @@ test('npm hook update - unicode', t => {
output.length = 0
})
- return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['update', '1', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -519,7 +519,7 @@ test('npm hook update - json output', t => {
output.length = 0
})
- return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['update', '1', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -547,7 +547,7 @@ test('npm hook update - parseable output', t => {
output.length = 0
})
- return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['update', '1', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
@@ -573,7 +573,7 @@ test('npm hook update - silent output', t => {
output.length = 0
})
- return hook(['update', '1', 'https://google.com', 'some-secret'], (err) => {
+ return hook.exec(['update', '1', 'https://google.com', 'some-secret'], (err) => {
if (err)
throw err
diff --git a/test/lib/init.js b/test/lib/init.js
index e73cc4b30..db5411ba7 100644
--- a/test/lib/init.js
+++ b/test/lib/init.js
@@ -17,13 +17,13 @@ const npm = {
}
const mocks = {
'init-package-json': (dir, initFile, config, cb) => cb(null, 'data'),
- '../../lib/npm.js': npm,
'../../lib/utils/usage.js': () => 'usage instructions',
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
}
-const init = requireInject('../../lib/init.js', mocks)
+const Init = requireInject('../../lib/init.js', mocks)
+const init = new Init(npm)
t.afterEach(cb => {
result = ''
@@ -40,7 +40,7 @@ t.test('classic npm init no args', t => {
return '~/.npm-init.js'
},
}
- init([], err => {
+ init.exec([], err => {
t.ifError(err, 'npm init no args')
t.matchSnapshot(result, 'should print helper info')
t.end()
@@ -65,7 +65,7 @@ t.test('classic npm init -y', t => {
t.equal(title, 'init', 'should print title')
t.equal(msg, 'written successfully', 'should print done info')
}
- init([], err => {
+ init.exec([], err => {
t.ifError(err, 'npm init -y')
t.equal(result, '')
})
@@ -87,7 +87,7 @@ t.test('npm init <arg>', t => {
)
cb()
}
- init(['react-app'], err => {
+ init.exec(['react-app'], err => {
t.ifError(err, 'npm init react-app')
})
})
@@ -102,7 +102,7 @@ t.test('npm init @scope/name', t => {
)
cb()
}
- init(['@npmcli/something'], err => {
+ init.exec(['@npmcli/something'], err => {
t.ifError(err, 'npm init init @scope/name')
})
})
@@ -117,7 +117,7 @@ t.test('npm init git spec', t => {
)
cb()
}
- init(['npm/something'], err => {
+ init.exec(['npm/something'], err => {
t.ifError(err, 'npm init init @scope/name')
})
})
@@ -132,13 +132,13 @@ t.test('npm init @scope', t => {
)
cb()
}
- init(['@npmcli'], err => {
+ init.exec(['@npmcli'], err => {
t.ifError(err, 'npm init init @scope/create')
})
})
t.test('npm init tgz', t => {
- init(['something.tgz'], err => {
+ init.exec(['something.tgz'], err => {
t.match(
err,
/Error: Unrecognized initializer: something.tgz/,
@@ -158,7 +158,7 @@ t.test('npm init <arg>@next', t => {
)
cb()
}
- init(['something@next'], err => {
+ init.exec(['something@next'], err => {
t.ifError(err, 'npm init init something@next')
})
})
@@ -167,7 +167,7 @@ t.test('npm init exec error', t => {
npm.commands.exec = (arr, cb) => {
cb(new Error('ERROR'))
}
- init(['something@next'], err => {
+ init.exec(['something@next'], err => {
t.match(
err,
/ERROR/,
@@ -199,37 +199,39 @@ t.test('should not rewrite flatOptions', t => {
)
cb()
}
- init(['react-app', 'my-app'], err => {
+ init.exec(['react-app', 'my-app'], err => {
t.ifError(err, 'npm init react-app')
})
})
t.test('npm init cancel', t => {
t.plan(3)
- const init = requireInject('../../lib/init.js', {
+ const Init = requireInject('../../lib/init.js', {
...mocks,
'init-package-json': (dir, initFile, config, cb) => cb(
new Error('canceled')
),
})
+ const init = new Init(npm)
npm.log = { ...npm.log }
npm.log.warn = (title, msg) => {
t.equal(title, 'init', 'should have init title')
t.equal(msg, 'canceled', 'should log canceled')
}
- init([], err => {
+ init.exec([], err => {
t.ifError(err, 'npm init cancel')
})
})
t.test('npm init error', t => {
- const init = requireInject('../../lib/init.js', {
+ const Init = requireInject('../../lib/init.js', {
...mocks,
'init-package-json': (dir, initFile, config, cb) => cb(
new Error('Unknown Error')
),
})
- init([], err => {
+ const init = new Init(npm)
+ init.exec([], err => {
t.match(err, /Unknown Error/, 'should throw error')
t.end()
})
diff --git a/test/lib/install-ci-test.js b/test/lib/install-ci-test.js
new file mode 100644
index 000000000..5f30efcab
--- /dev/null
+++ b/test/lib/install-ci-test.js
@@ -0,0 +1,57 @@
+const t = require('tap')
+
+const InstallCITest = require('../../lib/install-ci-test.js')
+
+let ciArgs = null
+let ciCalled = false
+let testArgs = null
+let testCalled = false
+let ciError = null
+
+const installCITest = new InstallCITest({
+ commands: {
+ ci: (args, cb) => {
+ ciArgs = args
+ ciCalled = true
+ cb(ciError)
+ },
+ test: (args, cb) => {
+ testArgs = args
+ testCalled = true
+ cb()
+ },
+ },
+})
+
+t.test('the install-ci-test command', t => {
+ t.afterEach(cb => {
+ ciArgs = null
+ ciCalled = false
+ testArgs = null
+ testCalled = false
+ ciError = null
+ cb()
+ })
+
+ t.test('ci and test', t => {
+ installCITest.exec(['extra'], () => {
+ t.equal(ciCalled, true)
+ t.equal(testCalled, true)
+ t.match(ciArgs, ['extra'])
+ t.match(testArgs, [])
+ t.end()
+ })
+ })
+
+ t.test('ci fails', t => {
+ ciError = new Error('test fail')
+ installCITest.exec(['extra'], (err) => {
+ t.equal(ciCalled, true)
+ t.equal(testCalled, false)
+ t.match(ciArgs, ['extra'])
+ t.match(err, { message: 'test fail' })
+ t.end()
+ })
+ })
+ t.end()
+})
diff --git a/test/lib/install-test.js b/test/lib/install-test.js
new file mode 100644
index 000000000..0c52bd5e3
--- /dev/null
+++ b/test/lib/install-test.js
@@ -0,0 +1,57 @@
+const t = require('tap')
+
+const InstallTest = require('../../lib/install-test.js')
+
+let installArgs = null
+let installCalled = false
+let testArgs = null
+let testCalled = false
+let installError = null
+
+const installTest = new InstallTest({
+ commands: {
+ install: (args, cb) => {
+ installArgs = args
+ installCalled = true
+ cb(installError)
+ },
+ test: (args, cb) => {
+ testArgs = args
+ testCalled = true
+ cb()
+ },
+ },
+})
+
+t.test('the install-test command', t => {
+ t.afterEach(cb => {
+ installArgs = null
+ installCalled = false
+ testArgs = null
+ testCalled = false
+ installError = null
+ cb()
+ })
+
+ t.test('install and test', t => {
+ installTest.exec(['extra'], () => {
+ t.equal(installCalled, true)
+ t.equal(testCalled, true)
+ t.match(installArgs, ['extra'])
+ t.match(testArgs, [])
+ t.end()
+ })
+ })
+
+ t.test('install fails', t => {
+ installError = new Error('test fail')
+ installTest.exec(['extra'], (err) => {
+ t.equal(installCalled, true)
+ t.equal(testCalled, false)
+ t.match(installArgs, ['extra'])
+ t.match(err, { message: 'test fail' })
+ t.end()
+ })
+ })
+ t.end()
+})
diff --git a/test/lib/install.js b/test/lib/install.js
index 859a4bdaa..8b7a96851 100644
--- a/test/lib/install.js
+++ b/test/lib/install.js
@@ -1,6 +1,6 @@
const { test } = require('tap')
-const install = require('../../lib/install.js')
+const Install = require('../../lib/install.js')
const requireInject = require('require-inject')
test('should install using Arborist', (t) => {
@@ -9,17 +9,7 @@ test('should install using Arborist', (t) => {
let REIFY_CALLED = false
let ARB_OBJ = null
- const install = requireInject('../../lib/install.js', {
- '../../lib/npm.js': {
- globalDir: 'path/to/node_modules/',
- prefix: 'foo',
- flatOptions: {
- global: false,
- },
- config: {
- get: () => true,
- },
- },
+ const Install = requireInject('../../lib/install.js', {
'@npmcli/run-script': ({ event }) => {
SCRIPTS.push(event)
},
@@ -33,14 +23,24 @@ test('should install using Arborist', (t) => {
REIFY_CALLED = true
}
},
- '../../lib/utils/reify-finish.js': arb => {
+ '../../lib/utils/reify-finish.js': (npm, arb) => {
if (arb !== ARB_OBJ)
throw new Error('got wrong object passed to reify-finish')
},
})
+ const install = new Install({
+ globalDir: 'path/to/node_modules/',
+ prefix: 'foo',
+ flatOptions: {
+ global: false,
+ },
+ config: {
+ get: () => true,
+ },
+ })
t.test('with args', t => {
- install(['fizzbuzz'], er => {
+ install.exec(['fizzbuzz'], er => {
if (er)
throw er
t.match(ARB_ARGS, { global: false, path: 'foo' })
@@ -51,7 +51,7 @@ test('should install using Arborist', (t) => {
})
t.test('just a local npm install', t => {
- install([], er => {
+ install.exec([], er => {
if (er)
throw er
t.match(ARB_ARGS, { global: false, path: 'foo' })
@@ -75,19 +75,8 @@ test('should install using Arborist', (t) => {
test('should ignore scripts with --ignore-scripts', (t) => {
const SCRIPTS = []
let REIFY_CALLED = false
- const install = requireInject('../../lib/install.js', {
+ const Install = requireInject('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
- '../../lib/npm.js': {
- globalDir: 'path/to/node_modules/',
- prefix: 'foo',
- flatOptions: {
- global: false,
- ignoreScripts: true,
- },
- config: {
- get: () => false,
- },
- },
'@npmcli/run-script': ({ event }) => {
SCRIPTS.push(event)
},
@@ -97,7 +86,18 @@ test('should ignore scripts with --ignore-scripts', (t) => {
}
},
})
- install([], er => {
+ const install = new Install({
+ globalDir: 'path/to/node_modules/',
+ prefix: 'foo',
+ flatOptions: {
+ global: false,
+ ignoreScripts: true,
+ },
+ config: {
+ get: () => false,
+ },
+ })
+ install.exec([], er => {
if (er)
throw er
t.equal(REIFY_CALLED, true, 'called reify')
@@ -107,23 +107,23 @@ test('should ignore scripts with --ignore-scripts', (t) => {
})
test('should install globally using Arborist', (t) => {
- const install = requireInject('../../lib/install.js', {
+ const Install = requireInject('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
- '../../lib/npm.js': {
- globalDir: 'path/to/node_modules/',
- prefix: 'foo',
- flatOptions: {
- global: true,
- },
- config: {
- get: () => false,
- },
- },
'@npmcli/arborist': function () {
this.reify = () => {}
},
})
- install([], er => {
+ const install = new Install({
+ globalDir: 'path/to/node_modules/',
+ prefix: 'foo',
+ flatOptions: {
+ global: true,
+ },
+ config: {
+ get: () => false,
+ },
+ })
+ install.exec([], er => {
if (er)
throw er
t.end()
@@ -131,7 +131,7 @@ test('should install globally using Arborist', (t) => {
})
test('completion to folder', async t => {
- const install = requireInject('../../lib/install.js', {
+ const Install = requireInject('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
util: {
promisify: (fn) => fn,
@@ -145,6 +145,7 @@ test('completion to folder', async t => {
},
},
})
+ const install = new Install({})
const res = await install.completion({ partialWord: '/ar' })
const expect = process.platform === 'win32' ? '\\arborist' : '/arborist'
t.strictSame(res, [expect], 'package dir match')
@@ -152,7 +153,7 @@ test('completion to folder', async t => {
})
test('completion to folder - invalid dir', async t => {
- const install = requireInject('../../lib/install.js', {
+ const Install = requireInject('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
util: {
promisify: (fn) => fn,
@@ -163,13 +164,14 @@ test('completion to folder - invalid dir', async t => {
},
},
})
+ const install = new Install({})
const res = await install.completion({ partialWord: 'path/to/folder' })
t.strictSame(res, [], 'invalid dir: no matching')
t.end()
})
test('completion to folder - no matches', async t => {
- const install = requireInject('../../lib/install.js', {
+ const Install = requireInject('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
util: {
promisify: (fn) => fn,
@@ -180,13 +182,14 @@ test('completion to folder - no matches', async t => {
},
},
})
+ const install = new Install({})
const res = await install.completion({ partialWord: '/pa' })
t.strictSame(res, [], 'no name match')
t.end()
})
test('completion to folder - match is not a package', async t => {
- const install = requireInject('../../lib/install.js', {
+ const Install = requireInject('../../lib/install.js', {
'../../lib/utils/reify-finish.js': async () => {},
util: {
promisify: (fn) => fn,
@@ -200,18 +203,21 @@ test('completion to folder - match is not a package', async t => {
},
},
})
+ const install = new Install({})
const res = await install.completion({ partialWord: '/ar' })
t.strictSame(res, [], 'no name match')
t.end()
})
test('completion to url', async t => {
+ const install = new Install({})
const res = await install.completion({ partialWord: 'http://path/to/url' })
t.strictSame(res, [])
t.end()
})
test('completion', async t => {
+ const install = new Install({})
const res = await install.completion({ partialWord: 'toto' })
t.notOk(res)
t.end()
diff --git a/test/lib/link.js b/test/lib/link.js
index b1048427d..be7af3f52 100644
--- a/test/lib/link.js
+++ b/test/lib/link.js
@@ -40,11 +40,11 @@ const printLinks = async (opts) => {
}
const mocks = {
- '../../lib/npm.js': npm,
'../../lib/utils/reify-output.js': () => reifyOutput(),
}
-const link = requireInject('../../lib/link.js', mocks)
+const Link = requireInject('../../lib/link.js', mocks)
+const link = new Link(npm)
t.test('link to globalDir when in current working dir of pkg and no args', (t) => {
t.plan(2)
@@ -83,7 +83,7 @@ t.test('link to globalDir when in current working dir of pkg and no args', (t) =
t.matchSnapshot(links, 'should create a global link to current pkg')
}
- link([], (err) => {
+ link.exec([], (err) => {
t.ifError(err, 'should not error out')
})
})
@@ -185,7 +185,7 @@ t.test('link global linked pkg to local nm when using args', (t) => {
// - @myscope/bar: prev installed scoped package available in globalDir
// - a: prev installed package available in globalDir
// - file:./link-me-too: pkg that needs to be reified in globalDir first
- link([
+ link.exec([
'test-pkg-link',
'@myscope/linked',
'@myscope/bar',
@@ -254,7 +254,7 @@ t.test('link pkg already in global space', (t) => {
// - @myscope/bar: prev installed scoped package available in globalDir
// - a: prev installed package available in globalDir
// - file:./link-me-too: pkg that needs to be reified in globalDir first
- link(['@myscope/linked'], (err) => {
+ link.exec(['@myscope/linked'], (err) => {
t.ifError(err, 'should not error out')
})
})
@@ -312,7 +312,7 @@ t.test('link pkg already in global space when prefix is a symlink', (t) => {
t.matchSnapshot(links, 'should create a local symlink to global pkg')
}
- link(['@myscope/linked'], (err) => {
+ link.exec(['@myscope/linked'], (err) => {
t.ifError(err, 'should not error out')
})
})
@@ -341,21 +341,18 @@ t.test('completion', async t => {
t.end()
})
-t.test('--global option', async t => {
+t.test('--global option', t => {
const _config = npm.config
npm.config = { get () {
return true
} }
- try {
- await link([])
- t.fail('should not get here')
- } catch (err) {
+ link.exec([], (err) => {
npm.config = _config
t.match(
err.message,
/link should never be --global/,
'should throw an useful error'
)
- }
- t.end()
+ t.end()
+ })
})
diff --git a/test/lib/ll.js b/test/lib/ll.js
index 7d4e2b94f..45eb4ec95 100644
--- a/test/lib/ll.js
+++ b/test/lib/ll.js
@@ -1,31 +1,34 @@
-const t = require('tap')
const requireInject = require('require-inject')
-const configs = {}
-let lsCalled = false
-const ll = requireInject('../../lib/ll.js', {
- '../../lib/npm.js': {
+const t = require('tap')
+
+t.test('ll', t => {
+ t.plan(3)
+
+ class LS {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ exec (args, cb) {
+ t.deepEqual(args, ['pkg'], 'should forward args')
+ cb()
+ }
+ }
+
+ const LL = requireInject('../../lib/ll.js', {
+ '../../lib/ls.js': LS,
+ })
+ const ll = new LL({
config: {
- set: (k, v) => {
- configs[k] = v
- },
- },
- commands: {
- ls: (args, cb) => {
- lsCalled = true
- cb()
+ set: (key, value) => {
+ t.equal(key, 'long', 'should set long config value')
+ t.equal(value, true, 'should set a truthy value')
},
},
- },
-})
+ })
-const ls = require('../../lib/ls.js')
-const { usage, completion } = ls
-t.equal(ll.usage, usage)
-t.equal(ll.completion.toString(), completion.toString())
-t.test('the ll command', t => {
- ll([], () => {
- t.equal(lsCalled, true)
- t.strictSame(configs, { long: true })
- t.end()
+ ll.exec(['pkg'], err => {
+ if (err)
+ throw err
})
})
diff --git a/test/lib/load-all-commands.js b/test/lib/load-all-commands.js
index 02cb0be65..f6d1ae9e1 100644
--- a/test/lib/load-all-commands.js
+++ b/test/lib/load-all-commands.js
@@ -1,4 +1,6 @@
-// just for gathering coverage info
+// Thanks to nyc not working properly with proxies this
+// doesn't affect coverage. but it does ensure that every command
+// has a usage, and if it has completion it is a function
const npm = require('../../lib/npm.js')
const t = require('tap')
const { cmdList } = require('../../lib/utils/cmd-list.js')
@@ -19,7 +21,7 @@ t.test('load each command', t => {
} else
t.plan(2)
t.isa(impl, 'function', 'implementation is a function')
- t.isa(impl.usage, 'string', 'usage is a string')
+ t.match(impl.usage, cmd, 'usage contains the command')
})
}
})
diff --git a/test/lib/logout.js b/test/lib/logout.js
index 96b1bcc7f..b00fa641d 100644
--- a/test/lib/logout.js
+++ b/test/lib/logout.js
@@ -17,13 +17,13 @@ const npmFetch = (url, opts) => {
const mocks = {
npmlog,
'npm-registry-fetch': npmFetch,
- '../../lib/npm.js': {
- flatOptions: _flatOptions,
- config,
- },
}
-const logout = requireInject('../../lib/logout.js', mocks)
+const Logout = requireInject('../../lib/logout.js', mocks)
+const logout = new Logout({
+ flatOptions: _flatOptions,
+ config,
+})
test('token logout', async (t) => {
t.plan(6)
@@ -52,7 +52,7 @@ test('token logout', async (t) => {
}
await new Promise((res, rej) => {
- logout([], (err) => {
+ logout.exec([], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
@@ -121,7 +121,7 @@ test('token scoped logout', async (t) => {
}
await new Promise((res, rej) => {
- logout([], (err) => {
+ logout.exec([], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
@@ -174,7 +174,7 @@ test('user/pass logout', async (t) => {
config.save = () => null
await new Promise((res, rej) => {
- logout([], (err) => {
+ logout.exec([], (err) => {
t.ifError(err, 'should not error out')
delete _flatOptions.username
@@ -189,7 +189,7 @@ test('user/pass logout', async (t) => {
})
test('missing credentials', (t) => {
- logout([], (err) => {
+ logout.exec([], (err) => {
t.match(
err.message,
/not logged in to https:\/\/registry.npmjs.org\/, so can't log out!/,
@@ -228,7 +228,7 @@ test('ignore invalid scoped registry config', async (t) => {
config.save = () => null
await new Promise((res, rej) => {
- logout([], (err) => {
+ logout.exec([], (err) => {
t.ifError(err, 'should not error out')
t.deepEqual(
diff --git a/test/lib/ls.js b/test/lib/ls.js
index b1df9067c..bd81776d5 100644
--- a/test/lib/ls.js
+++ b/test/lib/ls.js
@@ -106,28 +106,28 @@ const _flatOptions = {
},
production: false,
}
-const ls = requireInject('../../lib/ls.js', {
- '../../lib/npm.js': {
- flatOptions: _flatOptions,
- limit: {
- fetch: 3,
- },
- get prefix () {
- return _flatOptions.prefix
- },
- get globalDir () {
- return globalDir
- },
- config: {
- get (key) {
- return _flatOptions[key]
- },
- },
- },
+const LS = requireInject('../../lib/ls.js', {
'../../lib/utils/output.js': msg => {
result = msg
},
})
+const ls = new LS({
+ flatOptions: _flatOptions,
+ limit: {
+ fetch: 3,
+ },
+ get prefix () {
+ return _flatOptions.prefix
+ },
+ get globalDir () {
+ return globalDir
+ },
+ config: {
+ get (key) {
+ return _flatOptions[key]
+ },
+ },
+})
const redactCwd = res =>
res && res.replace(/\\+/g, '/').replace(new RegExp(__dirname.replace(/\\+/g, '/'), 'gi'), '{CWD}')
@@ -155,7 +155,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree representation of dependencies structure')
t.end()
@@ -166,7 +166,7 @@ t.test('ls', (t) => {
prefix = t.testdir({
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.matchSnapshot(
redactCwd(err.message),
@@ -188,7 +188,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.equal(err.code, 'ELSPROBLEMS', 'should have error code')
t.equal(
redactCwd(err.message),
@@ -213,7 +213,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls(['lorem'], (err) => {
+ ls.exec(['lorem'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output')
_flatOptions.color = false
@@ -235,7 +235,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls(['.'], (err) => {
+ ls.exec(['.'], (err) => {
t.ifError(err, 'should not throw on missing dep above current level')
t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output')
_flatOptions.all = true
@@ -256,7 +256,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls(['bar'], (err) => {
+ ls.exec(['bar'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered package and its ancestors')
t.end()
@@ -284,7 +284,7 @@ t.test('ls', (t) => {
},
},
})
- ls(['bar@*', 'lorem@1.0.0'], (err) => {
+ ls.exec(['bar@*', 'lorem@1.0.0'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of multiple filtered packages and their ancestors')
t.end()
@@ -303,7 +303,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls(['notadep'], (err) => {
+ ls.exec(['notadep'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree containing no dependencies info')
t.equal(
@@ -330,7 +330,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies')
_flatOptions.all = true
@@ -353,7 +353,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies')
_flatOptions.all = true
@@ -414,7 +414,7 @@ t.test('ls', (t) => {
},
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps and their deps only')
_flatOptions.all = true
@@ -435,7 +435,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.equal(err.code, 'ELSPROBLEMS', 'should have error code')
t.equal(
redactCwd(err.message).replace(/\r\n/g, '\n'),
@@ -462,7 +462,7 @@ t.test('ls', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.equal(err.code, 'ELSPROBLEMS', 'should have error code')
t.matchSnapshot(redactCwd(result), 'should output tree containing color info')
_flatOptions.color = false
@@ -492,7 +492,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing dev deps')
_flatOptions.dev = false
t.end()
@@ -521,7 +521,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing only development deps')
_flatOptions.only = null
t.end()
@@ -560,7 +560,7 @@ t.test('ls', (t) => {
...diffDepTypesNmFixture.node_modules,
},
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps')
_flatOptions.link = false
t.end()
@@ -596,7 +596,7 @@ t.test('ls', (t) => {
b: t.fixture('symlink', '../b'),
},
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps')
_flatOptions.link = false
t.end()
@@ -625,7 +625,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing production deps')
_flatOptions.production = false
t.end()
@@ -654,7 +654,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing only prod deps')
_flatOptions.only = null
t.end()
@@ -683,7 +683,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree info with descriptions')
_flatOptions.long = true
t.end()
@@ -714,7 +714,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps with descriptions')
_flatOptions.all = true
_flatOptions.depth = Infinity
@@ -727,7 +727,7 @@ t.test('ls', (t) => {
prefix = t.testdir({
'package.json': '{broken json',
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err, { code: 'EJSONPARSE' }, 'should throw EJSONPARSE error')
t.matchSnapshot(redactCwd(result), 'should print empty result')
t.end()
@@ -736,7 +736,7 @@ t.test('ls', (t) => {
t.test('empty location', (t) => {
prefix = t.testdir({})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'should not error out on empty locations')
t.matchSnapshot(redactCwd(result), 'should print empty result')
t.end()
@@ -764,7 +764,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree signaling mismatching peer dep in problems')
t.end()
})
@@ -799,7 +799,7 @@ t.test('ls', (t) => {
},
},
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree signaling mismatching peer dep in problems')
_flatOptions.color = false
t.end()
@@ -828,7 +828,7 @@ t.test('ls', (t) => {
},
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.match(err.message, /missing: b@\^1.0.0/, 'should list missing dep problem')
t.matchSnapshot(redactCwd(result), 'should output parseable signaling missing peer dep in problems')
@@ -846,7 +846,7 @@ t.test('ls', (t) => {
},
}),
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.match(err.message, 'missing: peer-dep@*, required by test-npm-ls@1.0.0', 'should have missing peer-dep error msg')
t.matchSnapshot(redactCwd(result), 'should output tree signaling missing peer dep in problems')
@@ -877,7 +877,7 @@ t.test('ls', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.match(err.message, /invalid: optional-dep@1.0.0/, 'should have invalid dep error msg')
t.matchSnapshot(redactCwd(result), 'should output tree with empty entry for missing optional deps')
@@ -916,7 +916,7 @@ t.test('ls', (t) => {
},
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref')
t.end()
@@ -954,7 +954,7 @@ t.test('ls', (t) => {
},
},
})
- ls(['a'], (err) => {
+ ls.exec(['a'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref')
_flatOptions.color = false
@@ -1002,7 +1002,7 @@ t.test('ls', (t) => {
},
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref')
t.end()
@@ -1051,7 +1051,7 @@ t.test('ls', (t) => {
},
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref')
_flatOptions.all = true
@@ -1101,7 +1101,7 @@ t.test('ls', (t) => {
},
},
})
- ls(['@npmcli/b'], (err) => {
+ ls.exec(['@npmcli/b'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref')
_flatOptions.color = false
@@ -1149,7 +1149,7 @@ t.test('ls', (t) => {
},
},
})
- ls(['@npmcli/c'], (err) => {
+ ls.exec(['@npmcli/c'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref')
t.end()
@@ -1193,7 +1193,7 @@ t.test('ls', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing aliases')
t.end()
})
@@ -1239,7 +1239,7 @@ t.test('ls', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree containing git refs')
t.end()
@@ -1283,7 +1283,7 @@ t.test('ls', (t) => {
},
}),
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should NOT print git refs in output tree')
t.end()
@@ -1338,7 +1338,7 @@ t.test('ls', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should not be printed in tree output')
t.end()
})
@@ -1374,7 +1374,7 @@ t.test('ls', (t) => {
// mimics lib/npm.js globalDir getter but pointing to fixtures
globalDir = resolve(fixtures, 'node_modules')
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should print tree and not mark top-level items extraneous')
globalDir = 'MISSING_GLOBAL_DIR'
_flatOptions.global = false
@@ -1427,7 +1427,7 @@ t.test('ls', (t) => {
},
})
- ls(['c'], (err) => {
+ ls.exec(['c'], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.matchSnapshot(redactCwd(result), 'should print tree and not duplicate child of missing items')
t.end()
@@ -1471,12 +1471,12 @@ t.test('ls', (t) => {
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'should NOT have ELSPROBLEMS error code')
t.matchSnapshot(redactCwd(result), 'should list workspaces properly')
// should also be able to filter out one of the workspaces
- ls(['a'], (err) => {
+ ls.exec(['a'], (err) => {
t.ifError(err, 'should NOT have ELSPROBLEMS error code when filter')
t.matchSnapshot(redactCwd(result), 'should filter single workspace')
@@ -1534,17 +1534,17 @@ t.test('ls', (t) => {
})
t.plan(6)
- ls(['a'], (err) => {
+ ls.exec(['a'], (err) => {
t.ifError(err, 'should NOT have ELSPROBLEMS error code')
t.matchSnapshot(redactCwd(result), 'should list a in top-level only')
- ls(['d'], (err) => {
+ ls.exec(['d'], (err) => {
t.ifError(err, 'should NOT have ELSPROBLEMS error code when filter')
t.matchSnapshot(redactCwd(result), 'should print empty results msg')
// if no --depth config is defined, should print path to dep
_flatOptions.depth = null // default config value
- ls(['d'], (err) => {
+ ls.exec(['d'], (err) => {
t.ifError(err, 'should NOT have ELSPROBLEMS error code when filter')
t.matchSnapshot(redactCwd(result), 'should print expected result')
})
@@ -1576,7 +1576,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable representation of dependencies structure')
t.end()
@@ -1587,7 +1587,7 @@ t.test('ls --parseable', (t) => {
prefix = t.testdir({
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.matchSnapshot(
redactCwd(err.message),
@@ -1609,7 +1609,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.equal(err.code, 'ELSPROBLEMS', 'should have error code')
t.matchSnapshot(redactCwd(result), 'should output containing problems info')
t.end()
@@ -1628,7 +1628,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls(['lorem'], (err) => {
+ ls.exec(['lorem'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurrences of filtered by package')
t.end()
@@ -1647,7 +1647,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls(['bar'], (err) => {
+ ls.exec(['bar'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurrences of filtered package')
t.end()
@@ -1675,7 +1675,7 @@ t.test('ls --parseable', (t) => {
},
},
})
- ls(['bar@*', 'lorem@1.0.0'], (err) => {
+ ls.exec(['bar@*', 'lorem@1.0.0'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable contaning only occurrences of multiple filtered packages and their ancestors')
t.end()
@@ -1694,7 +1694,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls(['notadep'], (err) => {
+ ls.exec(['notadep'], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable output containing no dependencies info')
t.equal(
@@ -1721,7 +1721,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable output containing only top-level dependencies')
_flatOptions.all = true
@@ -1744,7 +1744,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies')
_flatOptions.all = true
@@ -1767,7 +1767,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable containing top-level deps and their deps only')
_flatOptions.all = true
@@ -1788,7 +1788,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err, { code: 'ELSPROBLEMS' }, 'should list dep problems')
t.matchSnapshot(redactCwd(result), 'should output parseable containing top-level deps and their deps only')
t.end()
@@ -1817,7 +1817,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing dev deps')
_flatOptions.dev = false
t.end()
@@ -1846,7 +1846,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing only development deps')
_flatOptions.only = null
t.end()
@@ -1885,7 +1885,7 @@ t.test('ls --parseable', (t) => {
...diffDepTypesNmFixture.node_modules,
},
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps')
_flatOptions.link = false
t.end()
@@ -1914,7 +1914,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing production deps')
_flatOptions.production = false
t.end()
@@ -1943,7 +1943,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing only prod deps')
_flatOptions.only = null
t.end()
@@ -1972,7 +1972,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree info with descriptions')
_flatOptions.long = true
t.end()
@@ -1990,7 +1990,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.equal(err.code, 'ELSPROBLEMS', 'should have error code')
t.match(redactCwd(err.message), 'extraneous: lorem@1.0.0 {CWD}/ls-ls-parseable--long-with-extraneous-deps/node_modules/lorem', 'should have error code')
t.matchSnapshot(redactCwd(result), 'should output long parseable output with extraneous info')
@@ -2011,7 +2011,7 @@ t.test('ls --parseable', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err, { code: 'ELSPROBLEMS' }, 'should list dep problems')
t.matchSnapshot(redactCwd(result), 'should output parseable result containing EXTRANEOUS/INVALID labels')
_flatOptions.long = false
@@ -2051,7 +2051,7 @@ t.test('ls --parseable', (t) => {
...diffDepTypesNmFixture.node_modules,
},
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.matchSnapshot(redactCwd(result), 'should output parseable results with symlink targets')
_flatOptions.long = false
@@ -2083,7 +2083,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps with descriptions')
_flatOptions.all = true
_flatOptions.depth = Infinity
@@ -2096,7 +2096,7 @@ t.test('ls --parseable', (t) => {
prefix = t.testdir({
'package.json': '{broken json',
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err, { code: 'EJSONPARSE' }, 'should throw EJSONPARSE error')
t.matchSnapshot(redactCwd(result), 'should print empty result')
t.end()
@@ -2105,7 +2105,7 @@ t.test('ls --parseable', (t) => {
t.test('empty location', (t) => {
prefix = t.testdir({})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'should not error out on empty locations')
t.matchSnapshot(redactCwd(result), 'should print empty result')
t.end()
@@ -2133,7 +2133,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output parseable signaling missing peer dep in problems')
t.end()
})
@@ -2161,7 +2161,7 @@ t.test('ls --parseable', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.match(err.message, /invalid: optional-dep@1.0.0/, 'should have invalid dep error msg')
t.matchSnapshot(redactCwd(result), 'should output parseable with empty entry for missing optional deps')
@@ -2199,7 +2199,7 @@ t.test('ls --parseable', (t) => {
},
},
})
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should print tree output omitting deduped ref')
t.end()
})
@@ -2238,7 +2238,7 @@ t.test('ls --parseable', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing aliases')
t.end()
})
@@ -2283,7 +2283,7 @@ t.test('ls --parseable', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should output tree containing git refs')
t.end()
})
@@ -2337,7 +2337,7 @@ t.test('ls --parseable', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should not be printed in tree output')
t.end()
})
@@ -2373,7 +2373,7 @@ t.test('ls --parseable', (t) => {
// mimics lib/npm.js globalDir getter but pointing to fixtures
globalDir = resolve(fixtures, 'node_modules')
- ls([], () => {
+ ls.exec([], () => {
t.matchSnapshot(redactCwd(result), 'should print parseable output for global deps')
globalDir = 'MISSING_GLOBAL_DIR'
_flatOptions.global = false
@@ -2400,7 +2400,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2431,7 +2431,7 @@ t.test('ls --json', (t) => {
prefix = t.testdir({
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err, { code: 'ELSPROBLEMS' }, 'should list dep problems')
t.deepEqual(
jsonParse(result),
@@ -2487,7 +2487,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.equal(
redactCwd(err.message),
'extraneous: lorem@1.0.0 {CWD}/ls-ls-json-extraneous-deps/node_modules/lorem',
@@ -2542,7 +2542,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls(['lorem'], (err) => {
+ ls.exec(['lorem'], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2578,7 +2578,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls(['bar'], (err) => {
+ ls.exec(['bar'], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2623,7 +2623,7 @@ t.test('ls --json', (t) => {
},
},
})
- ls(['bar@*', 'lorem@1.0.0'], (err) => {
+ ls.exec(['bar@*', 'lorem@1.0.0'], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2662,7 +2662,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls(['notadep'], (err) => {
+ ls.exec(['notadep'], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2696,7 +2696,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2734,7 +2734,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2772,7 +2772,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'npm ls')
t.deepEqual(
jsonParse(result),
@@ -2813,7 +2813,7 @@ t.test('ls --json', (t) => {
}),
...simpleNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err, { code: 'ELSPROBLEMS' }, 'should list dep problems')
t.deepEqual(
jsonParse(result),
@@ -2882,7 +2882,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -2929,7 +2929,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -2986,7 +2986,7 @@ t.test('ls --json', (t) => {
...diffDepTypesNmFixture.node_modules,
},
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3028,7 +3028,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3069,7 +3069,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3182,7 +3182,7 @@ t.test('ls --json', (t) => {
},
}),
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3239,7 +3239,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3377,7 +3377,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3460,7 +3460,7 @@ t.test('ls --json', (t) => {
prefix = t.testdir({
'package.json': '{broken json',
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.message, 'Failed to parse root package.json', 'should have missin root package.json msg')
t.match(err.code, 'EJSONPARSE', 'should have EJSONPARSE error code')
t.deepEqual(
@@ -3479,7 +3479,7 @@ t.test('ls --json', (t) => {
t.test('empty location', (t) => {
prefix = t.testdir({})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.ifError(err, 'should not error out on empty locations')
t.deepEqual(
jsonParse(result),
@@ -3511,7 +3511,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'Should have ELSPROBLEMS error code')
t.deepEqual(
jsonParse(result),
@@ -3571,7 +3571,7 @@ t.test('ls --json', (t) => {
}),
...diffDepTypesNmFixture,
})
- ls([], (err) => {
+ ls.exec([], (err) => {
t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code')
t.match(err.message, /invalid: optional-dep@1.0.0/, 'should have invalid dep error msg')
t.deepEqual(
@@ -3643,7 +3643,7 @@ t.test('ls --json', (t) => {
},
},
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3701,7 +3701,7 @@ t.test('ls --json', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3761,7 +3761,7 @@ t.test('ls --json', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3845,7 +3845,7 @@ t.test('ls --json', (t) => {
},
})
touchHiddenPackageLock(prefix)
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3870,7 +3870,7 @@ t.test('ls --json', (t) => {
version: '1.0.0',
}),
})
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
@@ -3913,7 +3913,7 @@ t.test('ls --json', (t) => {
// mimics lib/npm.js globalDir getter but pointing to fixtures
globalDir = resolve(fixtures, 'node_modules')
- ls([], () => {
+ ls.exec([], () => {
t.deepEqual(
jsonParse(result),
{
diff --git a/test/lib/npm.js b/test/lib/npm.js
index 9fd9888b9..18bed36de 100644
--- a/test/lib/npm.js
+++ b/test/lib/npm.js
@@ -16,7 +16,7 @@ for (const env of Object.keys(process.env).filter(e => /^npm_/.test(e))) {
'should match "npm test" or "npm run test"'
)
} else
- t.match(process.env[env], 'run-script')
+ t.match(process.env[env], /^(run)|(run-script)|(exec)$/)
}
delete process.env[env]
}
@@ -310,10 +310,10 @@ t.test('npm.load', t => {
if (er)
throw er
- t.same(consoleLogs, [[require('../../lib/ls.js').usage]], 'print usage')
+ t.same(consoleLogs, [[npm.commands.ll.usage]], 'print usage')
consoleLogs.length = 0
npm.config.set('usage', false)
- t.equal(npm.commands.ll, npm.commands.la, 'same command, different name')
+ t.equal(npm.commands.ll, npm.commands.ll, 'same command, different name')
logs.length = 0
})
@@ -352,13 +352,15 @@ t.test('npm.load', t => {
t.test('loading as main will load the cli', t => {
const { spawn } = require('child_process')
const npm = require.resolve('../../lib/npm.js')
+ const LS = require('../../lib/ls.js')
+ const ls = new LS({})
const p = spawn(process.execPath, [npm, 'ls', '-h'])
const out = []
p.stdout.on('data', c => out.push(c))
p.on('close', (code, signal) => {
t.equal(code, 0)
t.equal(signal, null)
- t.equal(Buffer.concat(out).toString().trim(), require('../../lib/ls.js').usage)
+ t.equal(Buffer.concat(out).toString().trim(), ls.usage)
t.end()
})
})
diff --git a/test/lib/org.js b/test/lib/org.js
index 1e8aabc1d..d21df85d6 100644
--- a/test/lib/org.js
+++ b/test/lib/org.js
@@ -39,14 +39,14 @@ const libnpmorg = {
},
}
-const org = requireInject('../../lib/org.js', {
- '../../lib/npm.js': npm,
+const Org = requireInject('../../lib/org.js', {
'../../lib/utils/otplease.js': async (opts, fn) => fn(opts),
'../../lib/utils/output.js': (msg) => {
output.push(msg)
},
libnpmorg,
})
+const org = new Org(npm)
test('completion', async t => {
const completion = (argv) =>
@@ -67,7 +67,7 @@ test('completion', async t => {
})
test('npm org - invalid subcommand', t => {
- return org(['foo'], (err) => {
+ org.exec(['foo'], (err) => {
t.match(err, /npm org set/, 'prints usage information')
t.end()
})
@@ -79,7 +79,7 @@ test('npm org add', t => {
output.length = 0
})
- return org(['add', 'orgname', 'username'], (err) => {
+ org.exec(['add', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -100,7 +100,7 @@ test('npm org add - no org', t => {
output.length = 0
})
- return org(['add', '', 'username'], (err) => {
+ org.exec(['add', '', 'username'], (err) => {
t.match(err, /`orgname` is required/, 'returns the correct error')
t.end()
})
@@ -112,7 +112,7 @@ test('npm org add - no user', t => {
output.length = 0
})
- return org(['add', 'orgname', ''], (err) => {
+ org.exec(['add', 'orgname', ''], (err) => {
t.match(err, /`username` is required/, 'returns the correct error')
t.end()
})
@@ -124,7 +124,7 @@ test('npm org add - invalid role', t => {
output.length = 0
})
- return org(['add', 'orgname', 'username', 'person'], (err) => {
+ org.exec(['add', 'orgname', 'username', 'person'], (err) => {
t.match(err, /`role` must be one of/, 'returns the correct error')
t.end()
})
@@ -138,7 +138,7 @@ test('npm org add - more users', t => {
output.length = 0
})
- return org(['add', 'orgname', 'username'], (err) => {
+ org.exec(['add', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -161,7 +161,7 @@ test('npm org add - json output', t => {
output.length = 0
})
- return org(['add', 'orgname', 'username'], (err) => {
+ org.exec(['add', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -191,7 +191,7 @@ test('npm org add - parseable output', t => {
output.length = 0
})
- return org(['add', 'orgname', 'username'], (err) => {
+ org.exec(['add', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -217,7 +217,7 @@ test('npm org add - silent output', t => {
output.length = 0
})
- return org(['add', 'orgname', 'username'], (err) => {
+ org.exec(['add', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -239,7 +239,7 @@ test('npm org rm', t => {
output.length = 0
})
- return org(['rm', 'orgname', 'username'], (err) => {
+ org.exec(['rm', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -264,7 +264,7 @@ test('npm org rm - no org', t => {
output.length = 0
})
- return org(['rm', '', 'username'], (err) => {
+ org.exec(['rm', '', 'username'], (err) => {
t.match(err, /`orgname` is required/, 'threw the correct error')
t.end()
})
@@ -277,7 +277,7 @@ test('npm org rm - no user', t => {
output.length = 0
})
- return org(['rm', 'orgname'], (err) => {
+ org.exec(['rm', 'orgname'], (err) => {
t.match(err, /`username` is required/, 'threw the correct error')
t.end()
})
@@ -295,7 +295,7 @@ test('npm org rm - one user left', t => {
output.length = 0
})
- return org(['rm', 'orgname', 'username'], (err) => {
+ org.exec(['rm', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -322,7 +322,7 @@ test('npm org rm - json output', t => {
output.length = 0
})
- return org(['rm', 'orgname', 'username'], (err) => {
+ org.exec(['rm', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -354,7 +354,7 @@ test('npm org rm - parseable output', t => {
output.length = 0
})
- return org(['rm', 'orgname', 'username'], (err) => {
+ org.exec(['rm', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -384,7 +384,7 @@ test('npm org rm - silent output', t => {
output.length = 0
})
- return org(['rm', 'orgname', 'username'], (err) => {
+ org.exec(['rm', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -414,7 +414,7 @@ test('npm org ls', t => {
output.length = 0
})
- return org(['ls', 'orgname'], (err) => {
+ org.exec(['ls', 'orgname'], (err) => {
if (err)
throw err
@@ -441,7 +441,7 @@ test('npm org ls - user filter', t => {
output.length = 0
})
- return org(['ls', 'orgname', 'username'], (err) => {
+ org.exec(['ls', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -466,7 +466,7 @@ test('npm org ls - user filter, missing user', t => {
output.length = 0
})
- return org(['ls', 'orgname', 'username'], (err) => {
+ org.exec(['ls', 'orgname', 'username'], (err) => {
if (err)
throw err
@@ -487,7 +487,7 @@ test('npm org ls - no org', t => {
output.length = 0
})
- return org(['ls'], (err) => {
+ org.exec(['ls'], (err) => {
t.match(err, /`orgname` is required/, 'throws the correct error')
t.end()
})
@@ -507,7 +507,7 @@ test('npm org ls - json output', t => {
output.length = 0
})
- return org(['ls', 'orgname'], (err) => {
+ org.exec(['ls', 'orgname'], (err) => {
if (err)
throw err
@@ -534,7 +534,7 @@ test('npm org ls - parseable output', t => {
output.length = 0
})
- return org(['ls', 'orgname'], (err) => {
+ org.exec(['ls', 'orgname'], (err) => {
if (err)
throw err
@@ -566,7 +566,7 @@ test('npm org ls - silent output', t => {
output.length = 0
})
- return org(['ls', 'orgname'], (err) => {
+ org.exec(['ls', 'orgname'], (err) => {
if (err)
throw err
diff --git a/test/lib/outdated.js b/test/lib/outdated.js
index da53b6031..aa8a1bcb6 100644
--- a/test/lib/outdated.js
+++ b/test/lib/outdated.js
@@ -92,19 +92,18 @@ const globalDir = t.testdir({
},
})
-const outdated = (dir, opts) => requireInject(
- '../../lib/outdated.js',
- {
- '../../lib/npm.js': {
- prefix: dir,
- globalDir: `${globalDir}/node_modules`,
- flatOptions: opts,
- },
+const outdated = (dir, opts) => {
+ const Outdated = requireInject('../../lib/outdated.js', {
pacote: {
packument,
},
- }
-)
+ })
+ return new Outdated({
+ prefix: dir,
+ globalDir: `${globalDir}/node_modules`,
+ flatOptions: opts,
+ })
+}
t.beforeEach(cleanLogs)
@@ -180,7 +179,7 @@ t.test('should display outdated deps', t => {
t.test('outdated global', t => {
outdated(null, {
global: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -190,7 +189,7 @@ t.test('should display outdated deps', t => {
outdated(testDir, {
global: false,
color: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -201,7 +200,7 @@ t.test('should display outdated deps', t => {
global: false,
color: true,
omit: ['dev'],
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -212,7 +211,7 @@ t.test('should display outdated deps', t => {
global: false,
color: true,
omit: ['dev', 'peer'],
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -223,7 +222,7 @@ t.test('should display outdated deps', t => {
global: false,
color: true,
omit: ['prod'],
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -233,7 +232,7 @@ t.test('should display outdated deps', t => {
outdated(testDir, {
global: false,
long: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -243,7 +242,7 @@ t.test('should display outdated deps', t => {
outdated(testDir, {
global: false,
json: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -254,7 +253,7 @@ t.test('should display outdated deps', t => {
global: false,
json: true,
long: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -264,7 +263,7 @@ t.test('should display outdated deps', t => {
outdated(testDir, {
global: false,
parseable: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -275,7 +274,7 @@ t.test('should display outdated deps', t => {
global: false,
parseable: true,
long: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -284,7 +283,7 @@ t.test('should display outdated deps', t => {
t.test('outdated --all', t => {
outdated(testDir, {
all: true,
- })([], () => {
+ }).exec([], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -293,7 +292,7 @@ t.test('should display outdated deps', t => {
t.test('outdated specific dep', t => {
outdated(testDir, {
global: false,
- })(['alpha'], () => {
+ }).exec(['alpha'], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -323,7 +322,7 @@ t.test('should return if no outdated deps', t => {
outdated(testDir, {
global: false,
- })([], () => {
+ }).exec([], () => {
t.equals(logs.length, 0, 'no logs')
t.end()
})
@@ -350,7 +349,7 @@ t.test('throws if error with a dep', t => {
outdated(testDir, {
global: false,
- })([], (err) => {
+ }).exec([], (err) => {
t.equals(err.message, 'There is an error with this package.')
t.end()
})
@@ -370,7 +369,7 @@ t.test('should skip missing non-prod deps', t => {
outdated(testDir, {
global: false,
- })([], () => {
+ }).exec([], () => {
t.equals(logs.length, 0, 'no logs')
t.end()
})
@@ -395,7 +394,7 @@ t.test('should skip invalid pkg ranges', t => {
},
})
- outdated(testDir, {})([], () => {
+ outdated(testDir, {}).exec([], () => {
t.equals(logs.length, 0, 'no logs')
t.end()
})
@@ -420,7 +419,7 @@ t.test('should skip git specs', t => {
},
})
- outdated(testDir, {})([], () => {
+ outdated(testDir, {}).exec([], () => {
t.equals(logs.length, 0, 'no logs')
t.end()
})
diff --git a/test/lib/owner.js b/test/lib/owner.js
index aa5e3ee63..4f8f43088 100644
--- a/test/lib/owner.js
+++ b/test/lib/owner.js
@@ -15,7 +15,6 @@ const mocks = {
npmlog,
'npm-registry-fetch': npmFetch,
pacote,
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
@@ -31,7 +30,8 @@ const npmcliMaintainers = [
{ email: 'i@izs.me', name: 'isaacs' },
]
-const owner = requireInject('../../lib/owner.js', mocks)
+const Owner = requireInject('../../lib/owner.js', mocks)
+const owner = new Owner(npm)
t.test('owner no args', t => {
result = ''
@@ -39,7 +39,7 @@ t.test('owner no args', t => {
result = ''
})
- owner([], err => {
+ owner.exec([], err => {
t.equal(
err.message,
'usage instructions',
@@ -73,7 +73,7 @@ t.test('owner ls no args', t => {
readLocalPkgResponse = null
})
- owner(['ls'], err => {
+ owner.exec(['ls'], err => {
t.ifError(err, 'npm owner ls no args')
t.matchSnapshot(result, 'should output owners of cwd package')
})
@@ -86,7 +86,7 @@ t.test('owner ls no args no cwd package', t => {
npmlog.error = noop
})
- owner(['ls'], err => {
+ owner.exec(['ls'], err => {
t.equal(
err.message,
'usage instructions',
@@ -115,7 +115,7 @@ t.test('owner ls fails to retrieve packument', t => {
pacote.packument = noop
})
- owner(['ls'], err => {
+ owner.exec(['ls'], err => {
t.match(
err,
/ERR/,
@@ -145,7 +145,7 @@ t.test('owner ls <pkg>', t => {
pacote.packument = noop
})
- owner(['ls', '@npmcli/map-workspaces'], err => {
+ owner.exec(['ls', '@npmcli/map-workspaces'], err => {
t.ifError(err, 'npm owner ls <pkg>')
t.matchSnapshot(result, 'should output owners of <pkg>')
})
@@ -161,7 +161,7 @@ t.test('owner ls <pkg> no maintainers', t => {
pacote.packument = noop
})
- owner(['ls', '@npmcli/map-workspaces'], err => {
+ owner.exec(['ls', '@npmcli/map-workspaces'], err => {
t.ifError(err, 'npm owner ls <pkg> no maintainers')
t.equal(result, 'no admin found', 'should output no admint found msg')
t.end()
@@ -232,7 +232,7 @@ t.test('owner add <user> <pkg>', t => {
pacote.packument = noop
})
- owner(['add', 'foo', '@npmcli/map-workspaces'], err => {
+ owner.exec(['add', 'foo', '@npmcli/map-workspaces'], err => {
t.ifError(err, 'npm owner add <user> <pkg>')
t.equal(result, '+ foo (@npmcli/map-workspaces)', 'should output add result')
})
@@ -265,7 +265,7 @@ t.test('owner add <user> cwd package', t => {
pacote.packument = noop
})
- owner(['add', 'foo'], err => {
+ owner.exec(['add', 'foo'], err => {
t.ifError(err, 'npm owner add <user> cwd package')
t.equal(result, '+ foo (@npmcli/map-workspaces)', 'should output add result')
t.end()
@@ -308,7 +308,7 @@ t.test('owner add <user> <pkg> already an owner', t => {
pacote.packument = noop
})
- owner(['add', 'ruyadorno', '@npmcli/map-workspaces'], err => {
+ owner.exec(['add', 'ruyadorno', '@npmcli/map-workspaces'], err => {
t.ifError(err, 'npm owner add <user> <pkg> already an owner')
})
})
@@ -336,7 +336,7 @@ t.test('owner add <user> <pkg> fails to retrieve user', t => {
pacote.packument = noop
})
- owner(['add', 'foo', '@npmcli/map-workspaces'], err => {
+ owner.exec(['add', 'foo', '@npmcli/map-workspaces'], err => {
t.match(
err,
/Error: Couldn't get user data for foo: {"ok":false}/,
@@ -377,7 +377,7 @@ t.test('owner add <user> <pkg> fails to PUT updates', t => {
pacote.packument = noop
})
- owner(['add', 'foo', '@npmcli/map-workspaces'], err => {
+ owner.exec(['add', 'foo', '@npmcli/map-workspaces'], err => {
t.match(
err.message,
/Failed to update package/,
@@ -417,7 +417,7 @@ t.test('owner add <user> <pkg> fails to retrieve user info', t => {
pacote.packument = noop
})
- owner(['add', 'foo', '@npmcli/map-workspaces'], err => {
+ owner.exec(['add', 'foo', '@npmcli/map-workspaces'], err => {
t.match(
err.message,
"I'm a teapot",
@@ -453,7 +453,7 @@ t.test('owner add <user> <pkg> no previous maintainers property from server', t
pacote.packument = noop
})
- owner(['add', 'foo', '@npmcli/no-owners-pkg'], err => {
+ owner.exec(['add', 'foo', '@npmcli/no-owners-pkg'], err => {
t.ifError(err, 'npm owner add <user> <pkg>')
t.equal(result, '+ foo (@npmcli/no-owners-pkg)', 'should output add result')
t.end()
@@ -466,7 +466,7 @@ t.test('owner add no user', t => {
result = ''
})
- owner(['add'], err => {
+ owner.exec(['add'], err => {
t.equal(
err.message,
'usage instructions',
@@ -482,7 +482,7 @@ t.test('owner add <user> no cwd package', t => {
result = ''
})
- owner(['add', 'foo'], err => {
+ owner.exec(['add', 'foo'], err => {
t.equal(
err.message,
'usage instructions',
@@ -549,7 +549,7 @@ t.test('owner rm <user> <pkg>', t => {
pacote.packument = noop
})
- owner(['rm', 'ruyadorno', '@npmcli/map-workspaces'], err => {
+ owner.exec(['rm', 'ruyadorno', '@npmcli/map-workspaces'], err => {
t.ifError(err, 'npm owner rm <user> <pkg>')
t.equal(result, '- ruyadorno (@npmcli/map-workspaces)', 'should output rm result')
})
@@ -589,7 +589,7 @@ t.test('owner rm <user> <pkg> not a current owner', t => {
pacote.packument = noop
})
- owner(['rm', 'foo', '@npmcli/map-workspaces'], err => {
+ owner.exec(['rm', 'foo', '@npmcli/map-workspaces'], err => {
t.ifError(err, 'npm owner rm <user> <pkg> not a current owner')
})
})
@@ -621,7 +621,7 @@ t.test('owner rm <user> cwd package', t => {
pacote.packument = noop
})
- owner(['rm', 'ruyadorno'], err => {
+ owner.exec(['rm', 'ruyadorno'], err => {
t.ifError(err, 'npm owner rm <user> cwd package')
t.equal(result, '- ruyadorno (@npmcli/map-workspaces)', 'should output rm result')
t.end()
@@ -656,7 +656,7 @@ t.test('owner rm <user> only user', t => {
pacote.packument = noop
})
- owner(['rm', 'ruyadorno'], err => {
+ owner.exec(['rm', 'ruyadorno'], err => {
t.equal(
err.message,
'Cannot remove all owners of a package. Add someone else first.',
@@ -673,7 +673,7 @@ t.test('owner rm no user', t => {
result = ''
})
- owner(['rm'], err => {
+ owner.exec(['rm'], err => {
t.equal(
err.message,
'usage instructions',
@@ -689,7 +689,7 @@ t.test('owner rm <user> no cwd package', t => {
result = ''
})
- owner(['rm', 'foo'], err => {
+ owner.exec(['rm', 'foo'], err => {
t.equal(
err.message,
'usage instructions',
@@ -700,10 +700,8 @@ t.test('owner rm <user> no cwd package', t => {
})
t.test('completion', async t => {
- const { completion } = owner
-
const testComp = async (argv, expect) => {
- const res = await completion({ conf: { argv: { remain: argv } } })
+ const res = await owner.completion({ conf: { argv: { remain: argv } } })
t.strictSame(res, expect, argv.join(' '))
}
@@ -730,7 +728,7 @@ t.test('completion', async t => {
pacote.packument = noop
})
- const res = await completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } })
+ const res = await owner.completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } })
t.strictSame(res,
['nlf', 'ruyadorno', 'darcyclarke', 'isaacs'],
'should return list of current owners'
@@ -738,7 +736,7 @@ t.test('completion', async t => {
})
t.test('completion npm owner rm no cwd package', async t => {
- const res = await completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } })
+ const res = await owner.completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } })
t.strictSame(res, [], 'should have no owners to autocomplete if not cwd package')
t.end()
})
@@ -757,7 +755,7 @@ t.test('completion', async t => {
pacote.packument = noop
})
- const res = await completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } })
+ const res = await owner.completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } })
t.strictSame(res, [], 'should return no owners if not found')
})
diff --git a/test/lib/pack.js b/test/lib/pack.js
index 851174d25..73a19baa3 100644
--- a/test/lib/pack.js
+++ b/test/lib/pack.js
@@ -17,15 +17,8 @@ t.afterEach(cb => {
})
t.test('should pack current directory with no arguments', (t) => {
- const pack = requireInject('../../lib/pack.js', {
+ const Pack = requireInject('../../lib/pack.js', {
'../../lib/utils/output.js': output,
- '../../lib/npm.js': {
- flatOptions: {
- unicode: false,
- json: false,
- dryRun: false,
- },
- },
libnpmpack,
npmlog: {
notice: () => {},
@@ -33,13 +26,21 @@ t.test('should pack current directory with no arguments', (t) => {
clearProgress: () => {},
},
})
+ const pack = new Pack({
+ flatOptions: {
+ unicode: false,
+ json: false,
+ dryRun: false,
+ },
+ })
- return pack([], er => {
+ pack.exec([], er => {
if (er)
throw er
const filename = `npm-${require('../../package.json').version}.tgz`
t.strictSame(OUTPUT, [[filename]])
+ t.end()
})
})
@@ -51,15 +52,8 @@ t.test('should pack given directory', (t) => {
}, null, 2),
})
- const pack = requireInject('../../lib/pack.js', {
+ const Pack = requireInject('../../lib/pack.js', {
'../../lib/utils/output.js': output,
- '../../lib/npm.js': {
- flatOptions: {
- unicode: true,
- json: true,
- dryRun: true,
- },
- },
libnpmpack,
npmlog: {
notice: () => {},
@@ -67,13 +61,21 @@ t.test('should pack given directory', (t) => {
clearProgress: () => {},
},
})
+ const pack = new Pack({
+ flatOptions: {
+ unicode: true,
+ json: true,
+ dryRun: true,
+ },
+ })
- return pack([testDir], er => {
+ pack.exec([testDir], er => {
if (er)
throw er
const filename = 'my-cool-pkg-1.0.0.tgz'
t.strictSame(OUTPUT, [[filename]])
+ t.end()
})
})
@@ -85,15 +87,8 @@ t.test('should pack given directory for scoped package', (t) => {
}, null, 2),
})
- const pack = requireInject('../../lib/pack.js', {
+ const Pack = requireInject('../../lib/pack.js', {
'../../lib/utils/output.js': output,
- '../../lib/npm.js': {
- flatOptions: {
- unicode: true,
- json: true,
- dryRun: true,
- },
- },
libnpmpack,
npmlog: {
notice: () => {},
@@ -101,18 +96,26 @@ t.test('should pack given directory for scoped package', (t) => {
clearProgress: () => {},
},
})
+ const pack = new Pack({
+ flatOptions: {
+ unicode: true,
+ json: true,
+ dryRun: true,
+ },
+ })
- return pack([testDir], er => {
+ return pack.exec([testDir], er => {
if (er)
throw er
const filename = 'cool-my-pkg-1.0.0.tgz'
t.strictSame(OUTPUT, [[filename]])
+ t.end()
})
})
t.test('should log pack contents', (t) => {
- const pack = requireInject('../../lib/pack.js', {
+ const Pack = requireInject('../../lib/pack.js', {
'../../lib/utils/output.js': output,
'../../lib/utils/tar.js': {
...require('../../lib/utils/tar.js'),
@@ -120,13 +123,6 @@ t.test('should log pack contents', (t) => {
t.ok(true, 'logTar is called')
},
},
- '../../lib/npm.js': {
- flatOptions: {
- unicode: false,
- json: false,
- dryRun: false,
- },
- },
libnpmpack,
npmlog: {
notice: () => {},
@@ -134,12 +130,20 @@ t.test('should log pack contents', (t) => {
clearProgress: () => {},
},
})
+ const pack = new Pack({
+ flatOptions: {
+ unicode: false,
+ json: false,
+ dryRun: false,
+ },
+ })
- return pack([], er => {
+ pack.exec([], er => {
if (er)
throw er
const filename = `npm-${require('../../package.json').version}.tgz`
t.strictSame(OUTPUT, [[filename]])
+ t.end()
})
})
diff --git a/test/lib/ping.js b/test/lib/ping.js
index a185919dd..cf4753074 100644
--- a/test/lib/ping.js
+++ b/test/lib/ping.js
@@ -6,8 +6,7 @@ test('pings', (t) => {
const flatOptions = { registry: 'https://registry.npmjs.org' }
let noticeCalls = 0
- const ping = requireInject('../../lib/ping.js', {
- '../../lib/npm.js': { flatOptions },
+ const Ping = requireInject('../../lib/ping.js', {
'../../lib/utils/ping.js': function (spec) {
t.equal(spec, flatOptions, 'passes flatOptions')
return {}
@@ -25,8 +24,9 @@ test('pings', (t) => {
},
},
})
+ const ping = new Ping({ flatOptions })
- ping([], (err) => {
+ ping.exec([], (err) => {
t.equal(noticeCalls, 2, 'should have logged 2 lines')
t.ifError(err, 'npm ping')
t.ok('should be able to ping')
@@ -39,8 +39,7 @@ test('pings and logs details', (t) => {
const flatOptions = { registry: 'https://registry.npmjs.org' }
const details = { extra: 'data' }
let noticeCalls = 0
- const ping = requireInject('../../lib/ping.js', {
- '../../lib/npm.js': { flatOptions },
+ const Ping = requireInject('../../lib/ping.js', {
'../../lib/utils/ping.js': function (spec) {
t.equal(spec, flatOptions, 'passes flatOptions')
return details
@@ -62,8 +61,9 @@ test('pings and logs details', (t) => {
},
},
})
+ const ping = new Ping({ flatOptions })
- ping([], (err) => {
+ ping.exec([], (err) => {
t.equal(noticeCalls, 3, 'should have logged 3 lines')
t.ifError(err, 'npm ping')
t.ok('should be able to ping')
@@ -76,8 +76,7 @@ test('pings and returns json', (t) => {
const flatOptions = { registry: 'https://registry.npmjs.org', json: true }
const details = { extra: 'data' }
let noticeCalls = 0
- const ping = requireInject('../../lib/ping.js', {
- '../../lib/npm.js': { flatOptions },
+ const Ping = requireInject('../../lib/ping.js', {
'../../lib/utils/ping.js': function (spec) {
t.equal(spec, flatOptions, 'passes flatOptions')
return details
@@ -101,8 +100,9 @@ test('pings and returns json', (t) => {
},
},
})
+ const ping = new Ping({ flatOptions })
- ping([], (err) => {
+ ping.exec([], (err) => {
t.equal(noticeCalls, 2, 'should have logged 2 lines')
t.ifError(err, 'npm ping')
t.ok('should be able to ping')
diff --git a/test/lib/prefix.js b/test/lib/prefix.js
index 83e2d6368..dfb50f174 100644
--- a/test/lib/prefix.js
+++ b/test/lib/prefix.js
@@ -5,14 +5,14 @@ test('prefix', (t) => {
t.plan(3)
const dir = '/prefix/dir'
- const prefix = requireInject('../../lib/prefix.js', {
- '../../lib/npm.js': { prefix: dir },
+ const Prefix = requireInject('../../lib/prefix.js', {
'../../lib/utils/output.js': (output) => {
t.equal(output, dir, 'prints the correct directory')
},
})
+ const prefix = new Prefix({ prefix: dir })
- prefix([], (err) => {
+ prefix.exec([], (err) => {
t.ifError(err, 'npm prefix')
t.ok('should have printed directory')
})
diff --git a/test/lib/profile.js b/test/lib/profile.js
index 3b2e14003..743ba2d68 100644
--- a/test/lib/profile.js
+++ b/test/lib/profile.js
@@ -32,7 +32,6 @@ const mocks = {
.join('\n')
}
},
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
@@ -68,10 +67,11 @@ t.afterEach(cb => {
cb()
})
-const profile = requireInject('../../lib/profile.js', mocks)
+const Profile = requireInject('../../lib/profile.js', mocks)
+const profile = new Profile(npm)
t.test('no args', t => {
- profile([], err => {
+ profile.exec([], err => {
t.match(
err,
/usage instructions/,
@@ -88,13 +88,14 @@ t.test('profile get no args', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
t.test('default output', t => {
- profile(['get'], err => {
+ profile.exec(['get'], err => {
if (err)
throw err
@@ -109,7 +110,7 @@ t.test('profile get no args', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- profile(['get'], err => {
+ profile.exec(['get'], err => {
if (err)
throw err
@@ -125,7 +126,7 @@ t.test('profile get no args', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- profile(['get'], err => {
+ profile.exec(['get'], err => {
if (err)
throw err
@@ -147,12 +148,13 @@ t.test('profile get no args', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['get'], err => {
+ profile.exec(['get'], err => {
if (err)
throw err
@@ -174,12 +176,13 @@ t.test('profile get no args', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['get'], err => {
+ profile.exec(['get'], err => {
if (err)
throw err
@@ -201,12 +204,13 @@ t.test('profile get no args', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['get'], err => {
+ profile.exec(['get'], err => {
if (err)
throw err
@@ -228,13 +232,14 @@ t.test('profile get <key>', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
t.test('default output', t => {
- profile(['get', 'name'], err => {
+ profile.exec(['get', 'name'], err => {
if (err)
throw err
@@ -250,7 +255,7 @@ t.test('profile get <key>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- profile(['get', 'name'], err => {
+ profile.exec(['get', 'name'], err => {
if (err)
throw err
@@ -266,7 +271,7 @@ t.test('profile get <key>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- profile(['get', 'name'], err => {
+ profile.exec(['get', 'name'], err => {
if (err)
throw err
@@ -288,13 +293,14 @@ t.test('profile get multiple args', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
t.test('default output', t => {
- profile(['get', 'name', 'email', 'github'], err => {
+ profile.exec(['get', 'name', 'email', 'github'], err => {
if (err)
throw err
@@ -309,7 +315,7 @@ t.test('profile get multiple args', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- profile(['get', 'name', 'email', 'github'], err => {
+ profile.exec(['get', 'name', 'email', 'github'], err => {
if (err)
throw err
@@ -325,7 +331,7 @@ t.test('profile get multiple args', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- profile(['get', 'name', 'email', 'github'], err => {
+ profile.exec(['get', 'name', 'email', 'github'], err => {
if (err)
throw err
@@ -338,7 +344,7 @@ t.test('profile get multiple args', t => {
})
t.test('comma separated', t => {
- profile(['get', 'name,email,github'], err => {
+ profile.exec(['get', 'name,email,github'], err => {
if (err)
throw err
@@ -374,7 +380,7 @@ t.test('profile set <key> <value>', t => {
})
t.test('no key', t => {
- profile(['set'], err => {
+ profile.exec(['set'], err => {
t.match(
err,
/npm profile set <prop> <value>/,
@@ -385,7 +391,7 @@ t.test('profile set <key> <value>', t => {
})
t.test('no value', t => {
- profile(['set', 'email'], err => {
+ profile.exec(['set', 'email'], err => {
t.match(
err,
/npm profile set <prop> <value>/,
@@ -396,7 +402,7 @@ t.test('profile set <key> <value>', t => {
})
t.test('set password', t => {
- profile(['set', 'password', '1234'], err => {
+ profile.exec(['set', 'password', '1234'], err => {
t.match(
err,
/Do not include your current or new passwords on the command line./,
@@ -407,7 +413,7 @@ t.test('profile set <key> <value>', t => {
})
t.test('unwritable key', t => {
- profile(['set', 'name', 'foo'], err => {
+ profile.exec(['set', 'name', 'foo'], err => {
t.match(
err,
/"name" is not a property we can set./,
@@ -421,12 +427,13 @@ t.test('profile set <key> <value>', t => {
t.test('default output', t => {
t.plan(2)
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile(t),
})
+ const profile = new Profile(npm)
- profile(['set', 'fullname', 'Lorem Ipsum'], err => {
+ profile.exec(['set', 'fullname', 'Lorem Ipsum'], err => {
if (err)
throw err
@@ -443,12 +450,13 @@ t.test('profile set <key> <value>', t => {
npm.flatOptions.json = true
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile(t),
})
+ const profile = new Profile(npm)
- profile(['set', 'fullname', 'Lorem Ipsum'], err => {
+ profile.exec(['set', 'fullname', 'Lorem Ipsum'], err => {
if (err)
throw err
@@ -467,12 +475,13 @@ t.test('profile set <key> <value>', t => {
npm.flatOptions.parseable = true
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile(t),
})
+ const profile = new Profile(npm)
- profile(['set', 'fullname', 'Lorem Ipsum'], err => {
+ profile.exec(['set', 'fullname', 'Lorem Ipsum'], err => {
if (err)
throw err
@@ -513,12 +522,13 @@ t.test('profile set <key> <value>', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['set', 'email', 'foo@npmjs.com'], err => {
+ profile.exec(['set', 'email', 'foo@npmjs.com'], err => {
if (err)
throw err
@@ -576,13 +586,14 @@ t.test('profile set <key> <value>', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['set', 'password'], err => {
+ profile.exec(['set', 'password'], err => {
if (err)
throw err
@@ -643,14 +654,15 @@ t.test('profile set <key> <value>', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
npmlog,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['set', 'password'], err => {
+ profile.exec(['set', 'password'], err => {
if (err)
throw err
@@ -668,7 +680,7 @@ t.test('profile set <key> <value>', t => {
t.test('enable-2fa', t => {
t.test('invalid args', t => {
- profile(['enable-2fa', 'foo', 'bar'], err => {
+ profile.exec(['enable-2fa', 'foo', 'bar'], err => {
t.match(
err,
/npm profile enable-2fa \[auth-and-writes|auth-only\]/,
@@ -679,7 +691,7 @@ t.test('enable-2fa', t => {
})
t.test('invalid two factor auth mode', t => {
- profile(['enable-2fa', 'foo'], err => {
+ profile.exec(['enable-2fa', 'foo'], err => {
t.match(
err,
/Invalid two-factor authentication mode "foo"/,
@@ -692,7 +704,7 @@ t.test('enable-2fa', t => {
t.test('no support for --json output', t => {
npm.flatOptions.json = true
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
t.match(
err.message,
'Enabling two-factor authentication is an interactive ' +
@@ -706,7 +718,7 @@ t.test('enable-2fa', t => {
t.test('no support for --parseable output', t => {
npm.flatOptions.parseable = true
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
t.match(
err.message,
'Enabling two-factor authentication is an interactive ' +
@@ -733,12 +745,13 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
t.match(
err.message,
'Your registry https://registry.npmjs.org/ does ' +
@@ -761,12 +774,13 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
t.match(
err.message,
'Your registry https://registry.npmjs.org/ does ' +
@@ -781,11 +795,12 @@ t.test('enable-2fa', t => {
t.test('no auth found', t => {
npm.config.getCredentialsByURI = () => ({})
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
t.match(
err.message,
'You need to be logged in to registry ' +
@@ -861,13 +876,14 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
if (err)
throw err
@@ -964,14 +980,15 @@ t.test('enable-2fa', t => {
generate: (url, cb) => cb('qrcode'),
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'qrcode-terminal': qrcode,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
if (err)
throw err
@@ -1017,13 +1034,14 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
t.match(
err,
/Unknown error enabling two-factor authentication./,
@@ -1063,13 +1081,14 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-and-writes'], err => {
+ profile.exec(['enable-2fa', 'auth-and-writes'], err => {
if (err)
throw err
@@ -1113,13 +1132,14 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa', 'auth-only'], err => {
+ profile.exec(['enable-2fa', 'auth-only'], err => {
if (err)
throw err
@@ -1163,13 +1183,14 @@ t.test('enable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['enable-2fa'], err => {
+ profile.exec(['enable-2fa'], err => {
if (err)
throw err
@@ -1196,12 +1217,13 @@ t.test('disable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
})
+ const profile = new Profile(npm)
- profile(['disable-2fa'], err => {
+ profile.exec(['disable-2fa'], err => {
if (err)
throw err
@@ -1257,13 +1279,14 @@ t.test('disable-2fa', t => {
})
t.test('default output', t => {
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile(t),
'../../lib/utils/read-user-info.js': readUserInfo(t),
})
+ const profile = new Profile(npm)
- profile(['disable-2fa'], err => {
+ profile.exec(['disable-2fa'], err => {
if (err)
throw err
@@ -1279,13 +1302,14 @@ t.test('disable-2fa', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile(t),
'../../lib/utils/read-user-info.js': readUserInfo(t),
})
+ const profile = new Profile(npm)
- profile(['disable-2fa'], err => {
+ profile.exec(['disable-2fa'], err => {
if (err)
throw err
@@ -1301,13 +1325,14 @@ t.test('disable-2fa', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile(t),
'../../lib/utils/read-user-info.js': readUserInfo(t),
})
+ const profile = new Profile(npm)
- profile(['disable-2fa'], err => {
+ profile.exec(['disable-2fa'], err => {
if (err)
throw err
@@ -1363,13 +1388,14 @@ t.test('disable-2fa', t => {
},
}
- const profile = requireInject('../../lib/profile.js', {
+ const Profile = requireInject('../../lib/profile.js', {
...mocks,
'npm-profile': npmProfile,
'../../lib/utils/read-user-info.js': readUserInfo,
})
+ const profile = new Profile(npm)
- profile(['disable-2fa'], err => {
+ profile.exec(['disable-2fa'], err => {
if (err)
throw err
@@ -1385,7 +1411,7 @@ t.test('disable-2fa', t => {
})
t.test('unknown subcommand', t => {
- profile(['asfd'], err => {
+ profile.exec(['asfd'], err => {
t.match(
err,
/Unknown profile command: asfd/,
@@ -1396,11 +1422,9 @@ t.test('unknown subcommand', t => {
})
t.test('completion', t => {
- const { completion } = profile
-
const testComp = async ({ t, argv, expect, title }) => {
t.resolveMatch(
- completion({ conf: { argv: { remain: argv } } }),
+ profile.completion({ conf: { argv: { remain: argv } } }),
expect,
title
)
@@ -1444,7 +1468,7 @@ t.test('completion', t => {
t.test('npm profile unknown subcommand autocomplete', async t => {
t.rejects(
- completion({ conf: { argv: { remain: ['npm', 'profile', 'asdf'] } } }),
+ profile.completion({ conf: { argv: { remain: ['npm', 'profile', 'asdf'] } } }),
{ message: 'asdf not recognized' }, 'should throw unknown cmd error'
)
t.end()
diff --git a/test/lib/prune.js b/test/lib/prune.js
index 074f4eac6..8cd148806 100644
--- a/test/lib/prune.js
+++ b/test/lib/prune.js
@@ -2,13 +2,7 @@ const { test } = require('tap')
const requireInject = require('require-inject')
test('should prune using Arborist', (t) => {
- const prune = requireInject('../../lib/prune.js', {
- '../../lib/npm.js': {
- prefix: 'foo',
- flatOptions: {
- foo: 'bar',
- },
- },
+ const Prune = requireInject('../../lib/prune.js', {
'@npmcli/arborist': function (args) {
t.ok(args, 'gets options object')
t.ok(args.path, 'gets path option')
@@ -20,7 +14,13 @@ test('should prune using Arborist', (t) => {
t.ok(arb, 'gets arborist tree')
},
})
- prune(null, er => {
+ const prune = new Prune({
+ prefix: 'foo',
+ flatOptions: {
+ foo: 'bar',
+ },
+ })
+ prune.exec(null, er => {
if (er)
throw er
t.ok(true, 'callback is called')
diff --git a/test/lib/publish.js b/test/lib/publish.js
index 5243b5254..0e857fafd 100644
--- a/test/lib/publish.js
+++ b/test/lib/publish.js
@@ -34,19 +34,7 @@ t.test('should publish with libnpmpublish, passing through flatOptions and respe
}, null, 2),
})
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- customValue: true,
- },
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, registry, 'gets credentials for expected registry')
- return { token: 'some.registry.token' }
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
// verify that we do NOT remove publishConfig if it was there originally
// and then removed during the script/pack process
libnpmpack: async () => {
@@ -66,11 +54,24 @@ t.test('should publish with libnpmpublish, passing through flatOptions and respe
},
},
})
+ const publish = new Publish({
+ flatOptions: {
+ customValue: true,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, registry, 'gets credentials for expected registry')
+ return { token: 'some.registry.token' }
+ },
+ },
+ })
- return publish([testDir], (er) => {
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
@@ -85,16 +86,7 @@ t.test('re-loads publishConfig.registry if added during script process', (t) =>
}, null, 2),
})
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, registry, 'gets credentials for expected registry')
- return { token: 'some.registry.token' }
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
libnpmpack: async () => {
fs.writeFileSync(`${testDir}/package.json`, JSON.stringify({
name: 'my-cool-pkg',
@@ -112,11 +104,21 @@ t.test('re-loads publishConfig.registry if added during script process', (t) =>
},
},
})
+ const publish = new Publish({
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, registry, 'gets credentials for expected registry')
+ return { token: 'some.registry.token' }
+ },
+ },
+ })
- return publish([testDir], (er) => {
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
@@ -131,19 +133,7 @@ t.test('if loglevel=info and json, should not output package contents', (t) => {
})
log.level = 'info'
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- json: true,
- },
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, defaults.registry, 'gets credentials for expected registry')
- return { token: 'some.registry.token' }
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
'../../lib/utils/output.js': () => {
t.pass('output is called')
},
@@ -161,11 +151,24 @@ t.test('if loglevel=info and json, should not output package contents', (t) => {
},
},
})
+ const publish = new Publish({
+ flatOptions: {
+ json: true,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, defaults.registry, 'gets credentials for expected registry')
+ return { token: 'some.registry.token' }
+ },
+ },
+ })
- return publish([testDir], (er) => {
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
@@ -180,18 +183,7 @@ t.test('if loglevel=silent and dry-run, should not output package contents or pu
})
log.level = 'silent'
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- dryRun: true,
- },
- config: {
- ...config,
- getCredentialsByURI: () => {
- throw new Error('should not call getCredentialsByURI in dry run')
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
'../../lib/utils/output.js': () => {
throw new Error('should not output in dry run mode')
},
@@ -209,11 +201,23 @@ t.test('if loglevel=silent and dry-run, should not output package contents or pu
},
},
})
+ const publish = new Publish({
+ flatOptions: {
+ dryRun: true,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: () => {
+ throw new Error('should not call getCredentialsByURI in dry run')
+ },
+ },
+ })
- return publish([testDir], (er) => {
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
@@ -228,17 +232,7 @@ t.test('if loglevel=info and dry-run, should not publish, should log package con
})
log.level = 'info'
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- dryRun: true,
- },
- config: {
- ...config,
- getCredentialsByURI: () => {
- throw new Error('should not call getCredentialsByURI in dry run')
- }},
- },
+ const Publish = requireInject('../../lib/publish.js', {
'../../lib/utils/tar.js': {
getContents: () => ({
id: 'someid',
@@ -256,37 +250,52 @@ t.test('if loglevel=info and dry-run, should not publish, should log package con
},
},
})
+ const publish = new Publish({
+ flatOptions: {
+ dryRun: true,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: () => {
+ throw new Error('should not call getCredentialsByURI in dry run')
+ }},
+ })
- return publish([testDir], (er) => {
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
t.test('shows usage with wrong set of arguments', (t) => {
t.plan(1)
- const publish = requireInject('../../lib/publish.js')
+ const Publish = requireInject('../../lib/publish.js')
+ const publish = new Publish({})
- return publish(['a', 'b', 'c'], (er) => t.matchSnapshot(er, 'should print usage'))
+ publish.exec(['a', 'b', 'c'], (er) => {
+ t.matchSnapshot(er, 'should print usage')
+ t.end()
+ })
})
t.test('throws when invalid tag', (t) => {
t.plan(1)
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- defaultTag: '0.0.13',
- },
- config,
+ const Publish = requireInject('../../lib/publish.js')
+ const publish = new Publish({
+ flatOptions: {
+ defaultTag: '0.0.13',
},
+ config,
})
- return publish([], (err) => {
+ publish.exec([], (err) => {
t.match(err, {
message: /Tag name must not be a valid SemVer range: /,
}, 'throws when tag name is a valid SemVer range')
+ t.end()
})
})
@@ -310,16 +319,7 @@ t.test('can publish a tarball', t => {
}, ['package'])
const tarFile = fs.readFileSync(`${testDir}/tarball/package.tgz`)
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, defaults.registry, 'gets credentials for expected registry')
- return { token: 'some.registry.token' }
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
libnpmpublish: {
publish: (manifest, tarData, opts) => {
t.match(manifest, {
@@ -330,63 +330,73 @@ t.test('can publish a tarball', t => {
},
},
})
+ const publish = new Publish({
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, defaults.registry, 'gets credentials for expected registry')
+ return { token: 'some.registry.token' }
+ },
+ },
+ })
- return publish([`${testDir}/tarball/package.tgz`], (er) => {
+ publish.exec([`${testDir}/tarball/package.tgz`], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
-t.test('should check auth for default registry', async t => {
+t.test('should check auth for default registry', t => {
t.plan(2)
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, defaults.registry, 'gets credentials for expected registry')
- return {}
- },
+ const Publish = requireInject('../../lib/publish.js')
+ const publish = new Publish({
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, defaults.registry, 'gets credentials for expected registry')
+ return {}
},
},
})
- return publish([], (err) => {
+ publish.exec([], (err) => {
t.match(err, {
message: 'This command requires you to be logged in.',
code: 'ENEEDAUTH',
}, 'throws when not logged in')
+ t.end()
})
})
-t.test('should check auth for configured registry', async t => {
+t.test('should check auth for configured registry', t => {
t.plan(2)
const registry = 'https://some.registry'
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- registry,
- },
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, registry, 'gets credentials for expected registry')
- return {}
- },
+ const Publish = requireInject('../../lib/publish.js')
+ const publish = new Publish({
+ flatOptions: {
+ registry,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, registry, 'gets credentials for expected registry')
+ return {}
},
},
})
- return publish([], (err) => {
+ publish.exec([], (err) => {
t.match(err, {
message: 'This command requires you to be logged in.',
code: 'ENEEDAUTH',
}, 'throws when not logged in')
+ t.end()
})
})
-t.test('should check auth for scope specific registry', async t => {
+t.test('should check auth for scope specific registry', t => {
t.plan(2)
const registry = 'https://some.registry'
const testDir = t.testdir({
@@ -396,26 +406,26 @@ t.test('should check auth for scope specific registry', async t => {
}, null, 2),
})
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- '@npm:registry': registry,
- },
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, registry, 'gets credentials for expected registry')
- return {}
- },
+ const Publish = requireInject('../../lib/publish.js')
+ const publish = new Publish({
+ flatOptions: {
+ '@npm:registry': registry,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, registry, 'gets credentials for expected registry')
+ return {}
},
},
})
- return publish([testDir], (err) => {
+ publish.exec([testDir], (err) => {
t.match(err, {
message: 'This command requires you to be logged in.',
code: 'ENEEDAUTH',
}, 'throws when not logged in')
+ t.end()
})
})
@@ -429,19 +439,7 @@ t.test('should use auth for scope specific registry', t => {
}, null, 2),
})
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- flatOptions: {
- '@npm:registry': registry,
- },
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, registry, 'gets credentials for expected registry')
- return { token: 'some.registry.token' }
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
libnpmpublish: {
publish: (manifest, tarData, opts) => {
t.ok(opts, 'gets opts object')
@@ -449,10 +447,23 @@ t.test('should use auth for scope specific registry', t => {
},
},
})
- return publish([testDir], (er) => {
+ const publish = new Publish({
+ flatOptions: {
+ '@npm:registry': registry,
+ },
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, registry, 'gets credentials for expected registry')
+ return { token: 'some.registry.token' }
+ },
+ },
+ })
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
@@ -469,16 +480,7 @@ t.test('read registry only from publishConfig', t => {
}, null, 2),
})
- const publish = requireInject('../../lib/publish.js', {
- '../../lib/npm.js': {
- config: {
- ...config,
- getCredentialsByURI: (uri) => {
- t.same(uri, registry, 'gets credentials for expected registry')
- return { token: 'some.registry.token' }
- },
- },
- },
+ const Publish = requireInject('../../lib/publish.js', {
libnpmpublish: {
publish: (manifest, tarData, opts) => {
t.match(manifest, { name: 'my-cool-pkg', version: '1.0.0' }, 'gets manifest')
@@ -486,10 +488,20 @@ t.test('read registry only from publishConfig', t => {
},
},
})
+ const publish = new Publish({
+ config: {
+ ...config,
+ getCredentialsByURI: (uri) => {
+ t.same(uri, registry, 'gets credentials for expected registry')
+ return { token: 'some.registry.token' }
+ },
+ },
+ })
- return publish([testDir], (er) => {
+ publish.exec([testDir], (er) => {
if (er)
throw er
t.pass('got to callback')
+ t.end()
})
})
diff --git a/test/lib/rebuild.js b/test/lib/rebuild.js
index d9df048d9..ee081c087 100644
--- a/test/lib/rebuild.js
+++ b/test/lib/rebuild.js
@@ -13,14 +13,14 @@ const npm = {
prefix: '',
}
const mocks = {
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
'../../lib/utils/usage.js': () => 'usage instructions',
}
-const rebuild = requireInject('../../lib/rebuild.js', mocks)
+const Rebuild = requireInject('../../lib/rebuild.js', mocks)
+const rebuild = new Rebuild(npm)
t.afterEach(cb => {
npm.prefix = ''
@@ -67,7 +67,7 @@ t.test('no args', t => {
npm.prefix = path
- rebuild([], err => {
+ rebuild.exec([], err => {
if (err)
throw err
@@ -115,7 +115,7 @@ t.test('filter by pkg name', t => {
t.throws(() => fs.statSync(aBinFile))
t.throws(() => fs.statSync(bBinFile))
- rebuild(['b'], err => {
+ rebuild.exec(['b'], err => {
if (err)
throw err
@@ -163,7 +163,7 @@ t.test('filter by pkg@<range>', t => {
const bBinFile = resolve(path, 'node_modules/.bin/b')
const nestedBinFile = resolve(path, 'node_modules/a/node_modules/.bin/b')
- rebuild(['b@2'], err => {
+ rebuild.exec(['b@2'], err => {
if (err)
throw err
@@ -203,7 +203,7 @@ t.test('filter by directory', t => {
t.throws(() => fs.statSync(aBinFile))
t.throws(() => fs.statSync(bBinFile))
- rebuild(['file:node_modules/b'], err => {
+ rebuild.exec(['file:node_modules/b'], err => {
if (err)
throw err
@@ -215,7 +215,7 @@ t.test('filter by directory', t => {
})
t.test('filter must be a semver version/range, or directory', t => {
- rebuild(['git+ssh://github.com/npm/arborist'], err => {
+ rebuild.exec(['git+ssh://github.com/npm/arborist'], err => {
t.match(
err,
/Error: `npm rebuild` only supports SemVer version\/range specifiers/,
@@ -245,7 +245,7 @@ t.test('global prefix', t => {
npm.flatOptions.global = true
npm.globalDir = resolve(globalPath, 'lib', 'node_modules')
- rebuild([], err => {
+ rebuild.exec([], err => {
if (err)
throw err
diff --git a/test/lib/repo.js b/test/lib/repo.js
index 3367f7c88..7abda55ca 100644
--- a/test/lib/repo.js
+++ b/test/lib/repo.js
@@ -108,16 +108,17 @@ const pacote = {
// keep a tally of which urls got opened
const opened = {}
-const openUrl = (url, errMsg, cb) => {
+const openUrl = async (npm, url, errMsg, cb) => {
opened[url] = opened[url] || 0
opened[url]++
process.nextTick(cb)
}
-const repo = requireInject('../../lib/repo.js', {
+const Repo = requireInject('../../lib/repo.js', {
pacote,
'../../lib/utils/open-url.js': openUrl,
})
+const repo = new Repo({ flatOptions: {} })
t.test('open repo urls', t => {
const expect = {
@@ -150,7 +151,7 @@ t.test('open repo urls', t => {
t.plan(keys.length)
keys.forEach(pkg => {
t.test(pkg, t => {
- repo([pkg], (er) => {
+ repo.exec([pkg], (er) => {
if (er)
throw er
const url = expect[pkg]
@@ -173,7 +174,7 @@ t.test('fail if cannot figure out repo url', t => {
cases.forEach(pkg => {
t.test(pkg, t => {
- repo([pkg], er => {
+ repo.exec([pkg], er => {
t.match(er, { pkgid: pkg })
t.end()
})
@@ -182,7 +183,7 @@ t.test('fail if cannot figure out repo url', t => {
})
t.test('open default package if none specified', t => {
- repo([], (er) => {
+ repo.exec([], (er) => {
if (er)
throw er
t.equal(opened['https://example.com/thispkg'], 2, 'opened expected url', {opened})
diff --git a/test/lib/restart.js b/test/lib/restart.js
index a19bfd0d4..f29592d9b 100644
--- a/test/lib/restart.js
+++ b/test/lib/restart.js
@@ -1,4 +1,17 @@
const t = require('tap')
-const restart = require('../../lib/restart.js')
-t.isa(restart, Function)
+let runArgs
+const npm = {
+ commands: {
+ 'run-script': (args, cb) => {
+ runArgs = args
+ cb()
+ },
+ },
+}
+const Restart = require('../../lib/restart.js')
+const restart = new Restart(npm)
t.equal(restart.usage, 'npm restart [-- <args>]')
+restart.exec(['foo'], () => {
+ t.match(runArgs, ['restart', 'foo'])
+ t.end()
+})
diff --git a/test/lib/root.js b/test/lib/root.js
index 8c23152b3..e8ccc1106 100644
--- a/test/lib/root.js
+++ b/test/lib/root.js
@@ -5,14 +5,14 @@ test('root', (t) => {
t.plan(3)
const dir = '/root/dir'
- const root = requireInject('../../lib/root.js', {
- '../../lib/npm.js': { dir },
+ const Root = requireInject('../../lib/root.js', {
'../../lib/utils/output.js': (output) => {
t.equal(output, dir, 'prints the correct directory')
},
})
+ const root = new Root({ dir })
- root([], (err) => {
+ root.exec([], (err) => {
t.ifError(err, 'npm root')
t.ok('should have printed directory')
})
diff --git a/test/lib/run-script.js b/test/lib/run-script.js
index 974202aa8..43592d324 100644
--- a/test/lib/run-script.js
+++ b/test/lib/run-script.js
@@ -22,19 +22,29 @@ const npm = {
const output = []
-const npmlog = { level: 'warn' }
-const getRS = windows => requireInject('../../lib/run-script.js', {
- '@npmcli/run-script': Object.assign(async opts => {
- RUN_SCRIPTS.push(opts)
- }, {
- isServerPackage: require('@npmcli/run-script').isServerPackage,
- }),
- npmlog,
- '../../lib/npm.js': npm,
- '../../lib/utils/is-windows-shell.js': windows,
- '../../lib/utils/output.js': (...msg) => output.push(msg),
+t.afterEach(cb => {
+ output.length = 0
+ RUN_SCRIPTS.length = 0
+ npm.flatOptions.json = false
+ npm.flatOptions.parseable = false
+ cb()
})
+const npmlog = { level: 'warn' }
+const getRS = windows => {
+ const RunScript = requireInject('../../lib/run-script.js', {
+ '@npmcli/run-script': Object.assign(async opts => {
+ RUN_SCRIPTS.push(opts)
+ }, {
+ isServerPackage: require('@npmcli/run-script').isServerPackage,
+ }),
+ npmlog,
+ '../../lib/utils/is-windows-shell.js': windows,
+ '../../lib/utils/output.js': (...msg) => output.push(msg),
+ })
+ return new RunScript(npm)
+}
+
const runScript = getRS(false)
const runScriptWin = getRS(true)
@@ -69,109 +79,119 @@ t.test('completion', t => {
t.end()
})
-t.test('fail if no package.json', async t => {
+t.test('fail if no package.json', t => {
+ t.plan(2)
npm.localPrefix = t.testdir()
- await runScript([], er => t.match(er, { code: 'ENOENT' }))
- await runScript(['test'], er => t.match(er, { code: 'ENOENT' }))
+ runScript.exec([], er => t.match(er, { code: 'ENOENT' }))
+ runScript.exec(['test'], er => t.match(er, { code: 'ENOENT' }))
})
-t.test('default env, start, and restart scripts', async t => {
+t.test('default env, start, and restart scripts', t => {
npm.localPrefix = t.testdir({
'package.json': JSON.stringify({ name: 'x', version: '1.2.3' }),
'server.js': 'console.log("hello, world")',
})
- await runScript(['start'], er => {
- if (er)
- throw er
+ t.test('start', t => {
+ runScript.exec(['start'], er => {
+ if (er)
+ throw er
- t.match(RUN_SCRIPTS, [
- {
- path: npm.localPrefix,
- args: [],
- scriptShell: undefined,
- stdio: 'inherit',
- stdioString: true,
- pkg: { name: 'x', version: '1.2.3', _id: 'x@1.2.3', scripts: {}},
- event: 'start',
- },
- ])
+ t.match(RUN_SCRIPTS, [
+ {
+ path: npm.localPrefix,
+ args: [],
+ scriptShell: undefined,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg: { name: 'x', version: '1.2.3', _id: 'x@1.2.3', scripts: {}},
+ event: 'start',
+ },
+ ])
+ t.end()
+ })
})
- RUN_SCRIPTS.length = 0
- await runScript(['env'], er => {
- if (er)
- throw er
+ t.test('env', t => {
+ runScript.exec(['env'], er => {
+ if (er)
+ throw er
- t.match(RUN_SCRIPTS, [
- {
- path: npm.localPrefix,
- args: [],
- scriptShell: undefined,
- stdio: 'inherit',
- stdioString: true,
- pkg: {
- name: 'x',
- version: '1.2.3',
- _id: 'x@1.2.3',
- scripts: {
- env: 'env',
+ t.match(RUN_SCRIPTS, [
+ {
+ path: npm.localPrefix,
+ args: [],
+ scriptShell: undefined,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg: {
+ name: 'x',
+ version: '1.2.3',
+ _id: 'x@1.2.3',
+ scripts: {
+ env: 'env',
+ },
},
+ event: 'env',
},
- event: 'env',
- },
- ])
+ ])
+ t.end()
+ })
})
- RUN_SCRIPTS.length = 0
- await runScriptWin(['env'], er => {
- if (er)
- throw er
+ t.test('windows env', t => {
+ runScriptWin.exec(['env'], er => {
+ if (er)
+ throw er
- t.match(RUN_SCRIPTS, [
- {
- path: npm.localPrefix,
- args: [],
- scriptShell: undefined,
- stdio: 'inherit',
- stdioString: true,
- pkg: { name: 'x',
- version: '1.2.3',
- _id: 'x@1.2.3',
- scripts: {
- env: 'SET',
- } },
- event: 'env',
- },
- ])
+ t.match(RUN_SCRIPTS, [
+ {
+ path: npm.localPrefix,
+ args: [],
+ scriptShell: undefined,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg: { name: 'x',
+ version: '1.2.3',
+ _id: 'x@1.2.3',
+ scripts: {
+ env: 'SET',
+ } },
+ event: 'env',
+ },
+ ])
+ t.end()
+ })
})
- RUN_SCRIPTS.length = 0
- await runScript(['restart'], er => {
- if (er)
- throw er
+ t.test('restart', t => {
+ runScript.exec(['restart'], er => {
+ if (er)
+ throw er
- t.match(RUN_SCRIPTS, [
- {
- path: npm.localPrefix,
- args: [],
- scriptShell: undefined,
- stdio: 'inherit',
- stdioString: true,
- pkg: { name: 'x',
- version: '1.2.3',
- _id: 'x@1.2.3',
- scripts: {
- restart: 'npm stop --if-present && npm start',
- } },
- event: 'restart',
- },
- ])
+ t.match(RUN_SCRIPTS, [
+ {
+ path: npm.localPrefix,
+ args: [],
+ scriptShell: undefined,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg: { name: 'x',
+ version: '1.2.3',
+ _id: 'x@1.2.3',
+ scripts: {
+ restart: 'npm stop --if-present && npm start',
+ } },
+ event: 'restart',
+ },
+ ])
+ t.end()
+ })
})
- RUN_SCRIPTS.length = 0
+ t.end()
})
-t.test('non-default env script', async t => {
+t.test('non-default env script', t => {
npm.localPrefix = t.testdir({
'package.json': JSON.stringify({
name: 'x',
@@ -182,54 +202,59 @@ t.test('non-default env script', async t => {
}),
})
- await runScript(['env'], er => {
- if (er)
- throw er
+ t.test('env', t => {
+ runScript.exec(['env'], er => {
+ if (er)
+ throw er
- t.match(RUN_SCRIPTS, [
- {
- path: npm.localPrefix,
- args: [],
- scriptShell: undefined,
- stdio: 'inherit',
- stdioString: true,
- pkg: {
- name: 'x',
- version: '1.2.3',
- _id: 'x@1.2.3',
- scripts: {
- env: 'hello',
+ t.match(RUN_SCRIPTS, [
+ {
+ path: npm.localPrefix,
+ args: [],
+ scriptShell: undefined,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg: {
+ name: 'x',
+ version: '1.2.3',
+ _id: 'x@1.2.3',
+ scripts: {
+ env: 'hello',
+ },
},
+ event: 'env',
},
- event: 'env',
- },
- ])
+ ])
+ t.end()
+ })
})
- RUN_SCRIPTS.length = 0
- await runScriptWin(['env'], er => {
- if (er)
- throw er
+ t.test('env windows', t => {
+ runScriptWin.exec(['env'], er => {
+ if (er)
+ throw er
- t.match(RUN_SCRIPTS, [
- {
- path: npm.localPrefix,
- args: [],
- scriptShell: undefined,
- stdio: 'inherit',
- stdioString: true,
- pkg: { name: 'x',
- version: '1.2.3',
- _id: 'x@1.2.3',
- scripts: {
- env: 'hello',
+ t.match(RUN_SCRIPTS, [
+ {
+ path: npm.localPrefix,
+ args: [],
+ scriptShell: undefined,
+ stdio: 'inherit',
+ stdioString: true,
+ pkg: { name: 'x',
+ version: '1.2.3',
+ _id: 'x@1.2.3',
+ scripts: {
+ env: 'hello',
+ },
},
+ event: 'env',
},
- event: 'env',
- },
- ])
+ ])
+ t.end()
+ })
})
- RUN_SCRIPTS.length = 0
+ t.end()
})
t.test('try to run missing script', t => {
@@ -238,33 +263,36 @@ t.test('try to run missing script', t => {
scripts: { hello: 'world' },
}),
})
- t.test('no suggestions', async t => {
- await runScript(['notevenclose'], er => {
+ t.test('no suggestions', t => {
+ runScript.exec(['notevenclose'], er => {
t.match(er, {
message: 'missing script: notevenclose',
})
+ t.end()
})
})
- t.test('suggestions', async t => {
- await runScript(['helo'], er => {
+ t.test('suggestions', t => {
+ runScript.exec(['helo'], er => {
t.match(er, {
message: 'missing script: helo\n\nDid you mean this?\n hello',
})
+ t.end()
})
})
- t.test('with --if-present', async t => {
+ t.test('with --if-present', t => {
npm.config.set('if-present', true)
- await runScript(['goodbye'], er => {
+ runScript.exec(['goodbye'], er => {
if (er)
throw er
t.strictSame(RUN_SCRIPTS, [], 'did not try to run anything')
+ t.end()
})
})
t.end()
})
-t.test('run pre/post hooks', async t => {
+t.test('run pre/post hooks', t => {
npm.localPrefix = t.testdir({
'package.json': JSON.stringify({
name: 'x',
@@ -276,7 +304,7 @@ t.test('run pre/post hooks', async t => {
}),
})
- await runScript(['env'], er => {
+ runScript.exec(['env'], er => {
if (er)
throw er
@@ -298,11 +326,11 @@ t.test('run pre/post hooks', async t => {
},
{ event: 'postenv' },
])
+ t.end()
})
- RUN_SCRIPTS.length = 0
})
-t.test('skip pre/post hooks when using ignoreScripts', async t => {
+t.test('skip pre/post hooks when using ignoreScripts', t => {
npm.flatOptions.ignoreScripts = true
npm.localPrefix = t.testdir({
@@ -316,7 +344,7 @@ t.test('skip pre/post hooks when using ignoreScripts', async t => {
}),
})
- await runScript(['env'], er => {
+ runScript.exec(['env'], er => {
if (er)
throw er
@@ -339,13 +367,12 @@ t.test('skip pre/post hooks when using ignoreScripts', async t => {
event: 'env',
},
])
-
+ t.end()
delete npm.flatOptions.ignoreScripts
})
- RUN_SCRIPTS.length = 0
})
-t.test('run silent', async t => {
+t.test('run silent', t => {
npmlog.level = 'silent'
t.teardown(() => {
npmlog.level = 'warn'
@@ -362,7 +389,7 @@ t.test('run silent', async t => {
}),
})
- await runScript(['env'], er => {
+ runScript.exec(['env'], er => {
if (er)
throw er
@@ -391,11 +418,11 @@ t.test('run silent', async t => {
stdio: 'inherit',
},
])
+ t.end()
})
- RUN_SCRIPTS.length = 0
})
-t.test('list scripts', async t => {
+t.test('list scripts', t => {
const scripts = {
test: 'exit 2',
start: 'node server.js',
@@ -411,55 +438,62 @@ t.test('list scripts', async t => {
}),
})
- await runScript([], er => {
- if (er)
- throw er
+ t.test('no args', t => {
+ runScript.exec([], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['Lifecycle scripts included in x:'],
+ [' test\n exit 2'],
+ [' start\n node server.js'],
+ [' stop\n node kill-server.js'],
+ ['\navailable via `npm run-script`:'],
+ [' preenv\n echo before the env'],
+ [' postenv\n echo after the env'],
+ ], 'basic report')
+ t.end()
+ })
})
- t.strictSame(output, [
- ['Lifecycle scripts included in x:'],
- [' test\n exit 2'],
- [' start\n node server.js'],
- [' stop\n node kill-server.js'],
- ['\navailable via `npm run-script`:'],
- [' preenv\n echo before the env'],
- [' postenv\n echo after the env'],
- ], 'basic report')
- output.length = 0
- npmlog.level = 'silent'
- await runScript([], er => {
- if (er)
- throw er
+ t.test('silent', t => {
+ npmlog.level = 'silent'
+ runScript.exec([], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [])
+ t.end()
+ })
})
- t.strictSame(output, [])
- npmlog.level = 'warn'
-
- npm.flatOptions.json = true
- await runScript([], er => {
- if (er)
- throw er
+ t.test('warn json', t => {
+ npmlog.level = 'warn'
+ npm.flatOptions.json = true
+ runScript.exec([], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [[JSON.stringify(scripts, 0, 2)]], 'json report')
+ t.end()
+ })
})
- t.strictSame(output, [[JSON.stringify(scripts, 0, 2)]], 'json report')
- output.length = 0
- npm.flatOptions.json = false
- npm.flatOptions.parseable = true
- await runScript([], er => {
- if (er)
- throw er
+ t.test('parseable', t => {
+ npm.flatOptions.parseable = true
+ runScript.exec([], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['test:exit 2'],
+ ['start:node server.js'],
+ ['stop:node kill-server.js'],
+ ['preenv:echo before the env'],
+ ['postenv:echo after the env'],
+ ])
+ t.end()
+ })
})
- t.strictSame(output, [
- ['test:exit 2'],
- ['start:node server.js'],
- ['stop:node kill-server.js'],
- ['preenv:echo before the env'],
- ['postenv:echo after the env'],
- ])
- output.length = 0
- npm.flatOptions.parseable = false
+ t.end()
})
-t.test('list scripts when no scripts', async t => {
+t.test('list scripts when no scripts', t => {
npm.localPrefix = t.testdir({
'package.json': JSON.stringify({
name: 'x',
@@ -467,15 +501,15 @@ t.test('list scripts when no scripts', async t => {
}),
})
- await runScript([], er => {
+ runScript.exec([], er => {
if (er)
throw er
+ t.strictSame(output, [], 'nothing to report')
+ t.end()
})
- t.strictSame(output, [], 'nothing to report')
- output.length = 0
})
-t.test('list scripts, only commands', async t => {
+t.test('list scripts, only commands', t => {
npm.localPrefix = t.testdir({
'package.json': JSON.stringify({
name: 'x',
@@ -484,18 +518,18 @@ t.test('list scripts, only commands', async t => {
}),
})
- await runScript([], er => {
+ runScript.exec([], er => {
if (er)
throw er
+ t.strictSame(output, [
+ ['Lifecycle scripts included in x:'],
+ [' preversion\n echo doing the version dance'],
+ ])
+ t.end()
})
- t.strictSame(output, [
- ['Lifecycle scripts included in x:'],
- [' preversion\n echo doing the version dance'],
- ])
- output.length = 0
})
-t.test('list scripts, only non-commands', async t => {
+t.test('list scripts, only non-commands', t => {
npm.localPrefix = t.testdir({
'package.json': JSON.stringify({
name: 'x',
@@ -504,13 +538,13 @@ t.test('list scripts, only non-commands', async t => {
}),
})
- await runScript([], er => {
+ runScript.exec([], er => {
if (er)
throw er
+ t.strictSame(output, [
+ ['Scripts available in x via `npm run-script`:'],
+ [' glorp\n echo doing the glerp glop'],
+ ])
+ t.end()
})
- t.strictSame(output, [
- ['Scripts available in x via `npm run-script`:'],
- [' glorp\n echo doing the glerp glop'],
- ])
- output.length = 0
})
diff --git a/test/lib/search.js b/test/lib/search.js
index 1dba1250e..59c59f3b9 100644
--- a/test/lib/search.js
+++ b/test/lib/search.js
@@ -23,7 +23,6 @@ const libnpmsearch = {
const mocks = {
npmlog,
libnpmsearch,
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
@@ -37,10 +36,11 @@ t.afterEach(cb => {
cb()
})
-const search = requireInject('../../lib/search.js', mocks)
+const Search = requireInject('../../lib/search.js', mocks)
+const search = new Search(npm)
t.test('no args', t => {
- search([], err => {
+ search.exec([], err => {
t.match(
err,
/search must be called with arguments/,
@@ -59,12 +59,13 @@ t.test('search <name>', t => {
},
}
- const search = requireInject('../../lib/search.js', {
+ const Search = requireInject('../../lib/search.js', {
...mocks,
libnpmsearch,
})
+ const search = new Search(npm)
- search(['libnpm'], err => {
+ search.exec(['libnpm'], err => {
if (err)
throw err
@@ -93,12 +94,13 @@ t.test('search <name> --searchexclude --searchopts', t => {
},
}
- const search = requireInject('../../lib/search.js', {
+ const Search = requireInject('../../lib/search.js', {
...mocks,
libnpmsearch,
})
+ const search = new Search(npm)
- search(['foo'], err => {
+ search.exec(['foo'], err => {
if (err)
throw err
@@ -146,12 +148,13 @@ t.test('empty search results', t => {
},
}
- const search = requireInject('../../lib/search.js', {
+ const Search = requireInject('../../lib/search.js', {
...mocks,
libnpmsearch,
})
+ const search = new Search(npm)
- search(['foo'], err => {
+ search.exec(['foo'], err => {
if (err)
throw err
@@ -172,12 +175,13 @@ t.test('search api response error', t => {
},
}
- const search = requireInject('../../lib/search.js', {
+ const Search = requireInject('../../lib/search.js', {
...mocks,
libnpmsearch,
})
+ const search = new Search(npm)
- search(['foo'], err => {
+ search.exec(['foo'], err => {
t.match(
err,
/ERR/,
diff --git a/test/lib/set-script.js b/test/lib/set-script.js
index 196fd3d3e..7a057c503 100644
--- a/test/lib/set-script.js
+++ b/test/lib/set-script.js
@@ -1,54 +1,50 @@
const test = require('tap')
const requireInject = require('require-inject')
-const setScriptDefault = require('../../lib/set-script.js')
const parseJSON = require('json-parse-even-better-errors')
-test.type(setScriptDefault, 'function', 'command is function')
-test.equal(setScriptDefault.usage, 'npm set-script [<script>] [<command>]', 'usage matches')
test.test('fails on invalid arguments', (t) => {
- const setScript = requireInject('../../lib/set-script.js', {
- fs: {},
+ const SetScript = requireInject('../../lib/set-script.js', {
npmlog: {},
})
+ const setScript = new SetScript({})
t.plan(3)
- setScript(['arg1'], (fail) => t.match(fail, /Expected 2 arguments: got 1/))
- setScript(['arg1', 'arg2', 'arg3'], (fail) => t.match(fail, /Expected 2 arguments: got 3/))
- setScript(['arg1', 'arg2', 'arg3', 'arg4'], (fail) => t.match(fail, /Expected 2 arguments: got 4/))
+ setScript.exec(['arg1'], (fail) => t.match(fail, /Expected 2 arguments: got 1/))
+ setScript.exec(['arg1', 'arg2', 'arg3'], (fail) => t.match(fail, /Expected 2 arguments: got 3/))
+ setScript.exec(['arg1', 'arg2', 'arg3', 'arg4'], (fail) => t.match(fail, /Expected 2 arguments: got 4/))
})
test.test('fails if run in postinstall script', (t) => {
- var originalVar = process.env.npm_lifecycle_event
+ const originalVar = process.env.npm_lifecycle_event
process.env.npm_lifecycle_event = 'postinstall'
- const setScript = requireInject('../../lib/set-script.js', {
- fs: {},
+ const SetScript = requireInject('../../lib/set-script.js', {
npmlog: {},
})
t.plan(1)
- setScript(['arg1', 'arg2'], (fail) => t.equal(fail.toString(), 'Error: Scripts can’t set from the postinstall script'))
+ const setScript = new SetScript({})
+ setScript.exec(['arg1', 'arg2'], (fail) => t.equal(fail.toString(), 'Error: Scripts can’t set from the postinstall script'))
process.env.npm_lifecycle_event = originalVar
})
test.test('fails when package.json not found', (t) => {
- const setScript = requireInject('../../lib/set-script.js', {
- '../../lib/npm.js': {
- localPrefix: 'IDONTEXIST',
- },
- })
+ const SetScript = requireInject('../../lib/set-script.js')
+ const setScript = new SetScript({})
t.plan(1)
- setScript(['arg1', 'arg2'], (fail) => t.match(fail, /package.json not found/))
+ setScript.exec(['arg1', 'arg2'], (fail) => t.match(fail, /package.json not found/))
})
test.test('fails on invalid JSON', (t) => {
- const setScript = requireInject('../../lib/set-script.js', {
+ const SetScript = requireInject('../../lib/set-script.js', {
fs: {
+ readFile: () => {}, // read-package-json-fast explodes w/o this
readFileSync: (name, charcode) => {
return 'iamnotjson'
},
},
})
+ const setScript = new SetScript({})
t.plan(1)
- setScript(['arg1', 'arg2'], (fail) => t.match(fail, /Invalid package.json: JSONParseError/))
+ setScript.exec(['arg1', 'arg2'], (fail) => t.match(fail, /Invalid package.json: JSONParseError/))
})
test.test('creates scripts object', (t) => {
var mockFile = ''
- const setScript = requireInject('../../lib/set-script.js', {
+ const SetScript = requireInject('../../lib/set-script.js', {
fs: {
readFileSync: (name, charcode) => {
return '{}'
@@ -64,15 +60,16 @@ test.test('creates scripts object', (t) => {
}
},
})
+ const setScript = new SetScript({})
t.plan(2)
- setScript(['arg1', 'arg2'], (error) => {
+ setScript.exec(['arg1', 'arg2'], (error) => {
t.equal(error, undefined)
t.assert(parseJSON(mockFile), {scripts: {arg1: 'arg2'}})
})
})
test.test('warns before overwriting', (t) => {
var warningListened = ''
- const setScript = requireInject('../../lib/set-script.js', {
+ const SetScript = requireInject('../../lib/set-script.js', {
fs: {
readFileSync: (name, charcode) => {
return JSON.stringify({
@@ -95,15 +92,16 @@ test.test('warns before overwriting', (t) => {
},
},
})
+ const setScript = new SetScript({})
t.plan(2)
- setScript(['arg1', 'arg2'], (error) => {
+ setScript.exec(['arg1', 'arg2'], (error) => {
t.equal(error, undefined, 'no error')
t.equal(warningListened, 'Script "arg1" was overwritten')
})
})
test.test('provided indentation and eol is used', (t) => {
var mockFile = ''
- const setScript = requireInject('../../lib/set-script.js', {
+ const SetScript = requireInject('../../lib/set-script.js', {
fs: {
readFileSync: (name, charcode) => {
return '{}'
@@ -119,8 +117,9 @@ test.test('provided indentation and eol is used', (t) => {
}
},
})
+ const setScript = new SetScript({})
t.plan(3)
- setScript(['arg1', 'arg2'], (error) => {
+ setScript.exec(['arg1', 'arg2'], (error) => {
t.equal(error, undefined)
t.equal(mockFile.split('\r\n').length > 1, true)
t.equal(mockFile.split('\r\n').every((value) => !value.startsWith(' ') || value.startsWith(' '.repeat(6))), true)
@@ -128,7 +127,7 @@ test.test('provided indentation and eol is used', (t) => {
})
test.test('goes to default when undefined indent and eol provided', (t) => {
var mockFile = ''
- const setScript = requireInject('../../lib/set-script.js', {
+ const SetScript = requireInject('../../lib/set-script.js', {
fs: {
readFileSync: (name, charcode) => {
return '{}'
@@ -144,8 +143,9 @@ test.test('goes to default when undefined indent and eol provided', (t) => {
}
},
})
+ const setScript = new SetScript({})
t.plan(3)
- setScript(['arg1', 'arg2'], (error) => {
+ setScript.exec(['arg1', 'arg2'], (error) => {
t.equal(error, undefined)
t.equal(mockFile.split('\n').length > 1, true)
t.equal(mockFile.split('\n').every((value) => !value.startsWith(' ') || value.startsWith(' ')), true)
diff --git a/test/lib/set.js b/test/lib/set.js
index aeb239e9c..3b38fdc27 100644
--- a/test/lib/set.js
+++ b/test/lib/set.js
@@ -1,4 +1,4 @@
-const { test } = require('tap')
+const t = require('tap')
const requireInject = require('require-inject')
let configArgs = null
@@ -6,24 +6,23 @@ const npm = {
commands: {
config: (args, cb) => {
configArgs = args
- return cb()
+ cb()
},
},
}
-const set = requireInject('../../lib/set.js', {
- '../../lib/npm.js': npm,
-})
+const Set = requireInject('../../lib/set.js')
+const set = new Set(npm)
-test('npm set - no args', t => {
- return set([], (err) => {
+t.test('npm set - no args', t => {
+ set.exec([], (err) => {
t.match(err, /npm set/, 'prints usage')
t.end()
})
})
-test('npm set', t => {
- return set(['email', 'me@me.me'], (err) => {
+t.test('npm set', t => {
+ set.exec(['email', 'me@me.me'], (err) => {
if (err)
throw err
diff --git a/test/lib/shrinkwrap.js b/test/lib/shrinkwrap.js
index 51fd7931a..dc4bc3b22 100644
--- a/test/lib/shrinkwrap.js
+++ b/test/lib/shrinkwrap.js
@@ -31,7 +31,6 @@ const mocks = {
return tree
}
},
- '../../lib/npm.js': npm,
'../../lib/utils/usage.js': () => 'usage instructions',
}
@@ -80,13 +79,14 @@ t.test('no args', t => {
},
}
- const shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
...mocks,
npmlog,
'@npmcli/arborist': Arborist,
})
+ const shrinkwrap = new Shrinkwrap(npm)
- shrinkwrap([], err => {
+ shrinkwrap.exec([], err => {
if (err)
throw err
})
@@ -134,13 +134,14 @@ t.test('no virtual tree', t => {
},
}
- const shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
...mocks,
npmlog,
'@npmcli/arborist': Arborist,
})
+ const shrinkwrap = new Shrinkwrap(npm)
- shrinkwrap([], err => {
+ shrinkwrap.exec([], err => {
if (err)
throw err
})
@@ -194,14 +195,15 @@ t.test('existing package-json file', t => {
},
}
- const shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
...mocks,
fs,
npmlog,
'@npmcli/arborist': Arborist,
})
+ const shrinkwrap = new Shrinkwrap(npm)
- shrinkwrap([], err => {
+ shrinkwrap.exec([], err => {
if (err)
throw err
})
@@ -248,13 +250,14 @@ t.test('update shrinkwrap file version', t => {
},
}
- const shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
...mocks,
npmlog,
'@npmcli/arborist': Arborist,
})
+ const shrinkwrap = new Shrinkwrap(npm)
- shrinkwrap([], err => {
+ shrinkwrap.exec([], err => {
if (err)
throw err
})
@@ -301,24 +304,26 @@ t.test('update to date shrinkwrap file', t => {
},
}
- const shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
...mocks,
npmlog,
'@npmcli/arborist': Arborist,
})
+ const shrinkwrap = new Shrinkwrap(npm)
- shrinkwrap([], err => {
+ shrinkwrap.exec([], err => {
if (err)
throw err
})
})
t.test('shrinkwrap --global', t => {
- const shrinkwrap = requireInject('../../lib/shrinkwrap.js', mocks)
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', mocks)
npm.flatOptions.global = true
+ const shrinkwrap = new Shrinkwrap(npm)
- shrinkwrap([], err => {
+ shrinkwrap.exec([], err => {
t.match(
err,
/does not work for global packages/,
@@ -330,8 +335,11 @@ t.test('shrinkwrap --global', t => {
})
t.test('works without fs.promises', async t => {
- t.doesNotThrow(() => requireInject('../../lib/shrinkwrap.js', {
- ...mocks,
- fs: { ...fs, promises: null },
- }))
+ t.doesNotThrow(() => {
+ const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', {
+ ...mocks,
+ fs: { ...fs, promises: null },
+ })
+ new Shrinkwrap(npm)
+ })
})
diff --git a/test/lib/star.js b/test/lib/star.js
index ea5e07b94..64efd9ef8 100644
--- a/test/lib/star.js
+++ b/test/lib/star.js
@@ -10,7 +10,6 @@ const npmlog = { error: noop, info: noop, verbose: noop }
const mocks = {
npmlog,
'npm-registry-fetch': npmFetch,
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
@@ -18,7 +17,8 @@ const mocks = {
'../../lib/utils/usage.js': () => 'usage instructions',
}
-const star = requireInject('../../lib/star.js', mocks)
+const Star = requireInject('../../lib/star.js', mocks)
+const star = new Star(npm)
t.afterEach(cb => {
npm.config = { get () {} }
@@ -29,7 +29,7 @@ t.afterEach(cb => {
})
t.test('no args', t => {
- star([], err => {
+ star.exec([], err => {
t.match(
err,
/usage instructions/,
@@ -56,7 +56,7 @@ t.test('star a package', t => {
t.equal(msg, 'starring', 'should use expected msg')
t.equal(id, pkgName, 'should use expected id')
}
- star([pkgName], err => {
+ star.exec([pkgName], err => {
if (err)
throw err
t.equal(
@@ -84,7 +84,7 @@ t.test('unstar a package', t => {
t.equal(msg, 'unstarring', 'should use expected msg')
t.equal(id, pkgName, 'should use expected id')
}
- star([pkgName], err => {
+ star.exec([pkgName], err => {
if (err)
throw err
t.equal(
@@ -99,7 +99,7 @@ t.test('unicode', async t => {
t.test('star a package', t => {
npm.flatOptions.unicode = true
npmFetch.json = async (uri, opts) => ({})
- star(['pkg'], err => {
+ star.exec(['pkg'], err => {
if (err)
throw err
t.equal(
@@ -115,7 +115,7 @@ t.test('unicode', async t => {
npm.flatOptions.unicode = true
npm.config.get = key => key === 'star.unstar'
npmFetch.json = async (uri, opts) => ({})
- star(['pkg'], err => {
+ star.exec(['pkg'], err => {
if (err)
throw err
t.equal(
@@ -129,11 +129,12 @@ t.test('unicode', async t => {
})
t.test('logged out user', t => {
- const star = requireInject('../../lib/star.js', {
+ const Star = requireInject('../../lib/star.js', {
...mocks,
'../../lib/utils/get-identity.js': async () => undefined,
})
- star(['@npmcli/arborist'], err => {
+ const star = new Star(npm)
+ star.exec(['@npmcli/arborist'], err => {
t.match(
err,
/You need to be logged in/,
diff --git a/test/lib/stars.js b/test/lib/stars.js
index ff636a5e5..383b5adf4 100644
--- a/test/lib/stars.js
+++ b/test/lib/stars.js
@@ -10,7 +10,6 @@ const npmlog = { warn: noop }
const mocks = {
npmlog,
'npm-registry-fetch': npmFetch,
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result = [result, ...msg].join('\n')
},
@@ -18,7 +17,8 @@ const mocks = {
'../../lib/utils/usage.js': () => 'usage instructions',
}
-const stars = requireInject('../../lib/stars.js', mocks)
+const Stars = requireInject('../../lib/stars.js', mocks)
+const stars = new Stars(npm)
t.afterEach(cb => {
npm.config = { get () {} }
@@ -43,7 +43,7 @@ t.test('no args', t => {
}
}
- stars([], err => {
+ stars.exec([], err => {
if (err)
throw err
@@ -67,7 +67,7 @@ t.test('npm star <user>', t => {
}
}
- stars(['ruyadorno'], err => {
+ stars.exec(['ruyadorno'], err => {
if (err)
throw err
@@ -97,7 +97,7 @@ t.test('unauthorized request', t => {
)
}
- stars([], err => {
+ stars.exec([], err => {
t.match(
err,
/Not logged in/,
@@ -121,7 +121,7 @@ t.test('unexpected error', t => {
throw new Error('Should not output extra warning msgs')
}
- stars([], err => {
+ stars.exec([], err => {
t.match(
err,
/ERROR/,
@@ -144,7 +144,7 @@ t.test('no pkg starred', t => {
)
}
- stars([], err => {
+ stars.exec([], err => {
if (err)
throw err
})
diff --git a/test/lib/start.js b/test/lib/start.js
index 4f599223d..9a3328309 100644
--- a/test/lib/start.js
+++ b/test/lib/start.js
@@ -1,4 +1,17 @@
const t = require('tap')
-const start = require('../../lib/start.js')
-t.isa(start, Function)
+let runArgs
+const npm = {
+ commands: {
+ 'run-script': (args, cb) => {
+ runArgs = args
+ cb()
+ },
+ },
+}
+const Start = require('../../lib/start.js')
+const start = new Start(npm)
t.equal(start.usage, 'npm start [-- <args>]')
+start.exec(['foo'], () => {
+ t.match(runArgs, ['start', 'foo'])
+ t.end()
+})
diff --git a/test/lib/stop.js b/test/lib/stop.js
index 4e26703c9..e6cb193b6 100644
--- a/test/lib/stop.js
+++ b/test/lib/stop.js
@@ -1,4 +1,17 @@
const t = require('tap')
-const stop = require('../../lib/stop.js')
-t.isa(stop, Function)
+let runArgs
+const npm = {
+ commands: {
+ 'run-script': (args, cb) => {
+ runArgs = args
+ cb()
+ },
+ },
+}
+const Stop = require('../../lib/stop.js')
+const stop = new Stop(npm)
t.equal(stop.usage, 'npm stop [-- <args>]')
+stop.exec(['foo'], () => {
+ t.match(runArgs, ['stop', 'foo'])
+ t.end()
+})
diff --git a/test/lib/team.js b/test/lib/team.js
index 9edaf58ee..a26459725 100644
--- a/test/lib/team.js
+++ b/test/lib/team.js
@@ -14,7 +14,6 @@ const npm = { flatOptions: {} }
const mocks = {
libnpmteam,
'cli-columns': a => a.join(' '),
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
@@ -28,10 +27,11 @@ t.afterEach(cb => {
cb()
})
-const team = requireInject('../../lib/team.js', mocks)
+const Team = requireInject('../../lib/team.js', mocks)
+const team = new Team(npm)
t.test('no args', t => {
- team([], err => {
+ team.exec([], err => {
t.match(
err,
'usage instructions',
@@ -43,7 +43,7 @@ t.test('no args', t => {
t.test('team add <scope:team> <user>', t => {
t.test('default output', t => {
- team(['add', '@npmcli:developers', 'foo'], err => {
+ team.exec(['add', '@npmcli:developers', 'foo'], err => {
if (err)
throw err
@@ -55,7 +55,7 @@ t.test('team add <scope:team> <user>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- team(['add', '@npmcli:developers', 'foo'], err => {
+ team.exec(['add', '@npmcli:developers', 'foo'], err => {
if (err)
throw err
@@ -70,7 +70,7 @@ t.test('team add <scope:team> <user>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- team(['add', '@npmcli:developers', 'foo'], err => {
+ team.exec(['add', '@npmcli:developers', 'foo'], err => {
if (err)
throw err
@@ -90,7 +90,7 @@ t.test('team add <scope:team> <user>', t => {
t.test('--silent', t => {
npm.flatOptions.silent = true
- team(['add', '@npmcli:developers', 'foo'], err => {
+ team.exec(['add', '@npmcli:developers', 'foo'], err => {
if (err)
throw err
@@ -104,7 +104,7 @@ t.test('team add <scope:team> <user>', t => {
t.test('team create <scope:team>', t => {
t.test('default output', t => {
- team(['create', '@npmcli:newteam'], err => {
+ team.exec(['create', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -116,7 +116,7 @@ t.test('team create <scope:team>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- team(['create', '@npmcli:newteam'], err => {
+ team.exec(['create', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -131,7 +131,7 @@ t.test('team create <scope:team>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- team(['create', '@npmcli:newteam'], err => {
+ team.exec(['create', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -150,7 +150,7 @@ t.test('team create <scope:team>', t => {
t.test('--silent', t => {
npm.flatOptions.silent = true
- team(['create', '@npmcli:newteam'], err => {
+ team.exec(['create', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -164,7 +164,7 @@ t.test('team create <scope:team>', t => {
t.test('team destroy <scope:team>', t => {
t.test('default output', t => {
- team(['destroy', '@npmcli:newteam'], err => {
+ team.exec(['destroy', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -176,7 +176,7 @@ t.test('team destroy <scope:team>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- team(['destroy', '@npmcli:newteam'], err => {
+ team.exec(['destroy', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -188,7 +188,7 @@ t.test('team destroy <scope:team>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- team(['destroy', '@npmcli:newteam'], err => {
+ team.exec(['destroy', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -207,7 +207,7 @@ t.test('team destroy <scope:team>', t => {
t.test('--silent', t => {
npm.flatOptions.silent = true
- team(['destroy', '@npmcli:newteam'], err => {
+ team.exec(['destroy', '@npmcli:newteam'], err => {
if (err)
throw err
@@ -230,13 +230,14 @@ t.test('team ls <scope>', t => {
},
}
- const team = requireInject('../../lib/team.js', {
+ const Team = requireInject('../../lib/team.js', {
...mocks,
libnpmteam,
})
+ const team = new Team(npm)
t.test('default output', t => {
- team(['ls', '@npmcli'], err => {
+ team.exec(['ls', '@npmcli'], err => {
if (err)
throw err
@@ -248,7 +249,7 @@ t.test('team ls <scope>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- team(['ls', '@npmcli'], err => {
+ team.exec(['ls', '@npmcli'], err => {
if (err)
throw err
@@ -260,7 +261,7 @@ t.test('team ls <scope>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- team(['ls', '@npmcli'], err => {
+ team.exec(['ls', '@npmcli'], err => {
if (err)
throw err
@@ -280,7 +281,7 @@ t.test('team ls <scope>', t => {
t.test('--silent', t => {
npm.flatOptions.silent = true
- team(['ls', '@npmcli'], err => {
+ team.exec(['ls', '@npmcli'], err => {
if (err)
throw err
@@ -296,12 +297,13 @@ t.test('team ls <scope>', t => {
},
}
- const team = requireInject('../../lib/team.js', {
+ const Team = requireInject('../../lib/team.js', {
...mocks,
libnpmteam,
})
+ const team = new Team(npm)
- team(['ls', '@npmcli'], err => {
+ team.exec(['ls', '@npmcli'], err => {
if (err)
throw err
@@ -317,12 +319,13 @@ t.test('team ls <scope>', t => {
},
}
- const team = requireInject('../../lib/team.js', {
+ const Team = requireInject('../../lib/team.js', {
...mocks,
libnpmteam,
})
+ const team = new Team(npm)
- team(['ls', '@npmcli'], err => {
+ team.exec(['ls', '@npmcli'], err => {
if (err)
throw err
@@ -340,13 +343,14 @@ t.test('team ls <scope:team>', t => {
return ['nlf', 'ruyadorno', 'darcyclarke', 'isaacs']
},
}
- const team = requireInject('../../lib/team.js', {
+ const Team = requireInject('../../lib/team.js', {
...mocks,
libnpmteam,
})
+ const team = new Team(npm)
t.test('default output', t => {
- team(['ls', '@npmcli:developers'], err => {
+ team.exec(['ls', '@npmcli:developers'], err => {
if (err)
throw err
@@ -358,7 +362,7 @@ t.test('team ls <scope:team>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- team(['ls', '@npmcli:developers'], err => {
+ team.exec(['ls', '@npmcli:developers'], err => {
if (err)
throw err
@@ -370,7 +374,7 @@ t.test('team ls <scope:team>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- team(['ls', '@npmcli:developers'], err => {
+ team.exec(['ls', '@npmcli:developers'], err => {
if (err)
throw err
@@ -391,7 +395,7 @@ t.test('team ls <scope:team>', t => {
t.test('--silent', t => {
npm.flatOptions.silent = true
- team(['ls', '@npmcli:developers'], err => {
+ team.exec(['ls', '@npmcli:developers'], err => {
if (err)
throw err
@@ -407,12 +411,13 @@ t.test('team ls <scope:team>', t => {
},
}
- const team = requireInject('../../lib/team.js', {
+ const Team = requireInject('../../lib/team.js', {
...mocks,
libnpmteam,
})
+ const team = new Team(npm)
- team(['ls', '@npmcli:developers'], err => {
+ team.exec(['ls', '@npmcli:developers'], err => {
if (err)
throw err
@@ -428,12 +433,13 @@ t.test('team ls <scope:team>', t => {
},
}
- const team = requireInject('../../lib/team.js', {
+ const Team = requireInject('../../lib/team.js', {
...mocks,
libnpmteam,
})
+ const team = new Team(npm)
- team(['ls', '@npmcli:developers'], err => {
+ team.exec(['ls', '@npmcli:developers'], err => {
if (err)
throw err
@@ -447,7 +453,7 @@ t.test('team ls <scope:team>', t => {
t.test('team rm <scope:team> <user>', t => {
t.test('default output', t => {
- team(['rm', '@npmcli:newteam', 'foo'], err => {
+ team.exec(['rm', '@npmcli:newteam', 'foo'], err => {
if (err)
throw err
@@ -459,7 +465,7 @@ t.test('team rm <scope:team> <user>', t => {
t.test('--parseable', t => {
npm.flatOptions.parseable = true
- team(['rm', '@npmcli:newteam', 'foo'], err => {
+ team.exec(['rm', '@npmcli:newteam', 'foo'], err => {
if (err)
throw err
@@ -471,7 +477,7 @@ t.test('team rm <scope:team> <user>', t => {
t.test('--json', t => {
npm.flatOptions.json = true
- team(['rm', '@npmcli:newteam', 'foo'], err => {
+ team.exec(['rm', '@npmcli:newteam', 'foo'], err => {
if (err)
throw err
@@ -491,7 +497,7 @@ t.test('team rm <scope:team> <user>', t => {
t.test('--silent', t => {
npm.flatOptions.silent = true
- team(['rm', '@npmcli:newteam', 'foo'], err => {
+ team.exec(['rm', '@npmcli:newteam', 'foo'], err => {
if (err)
throw err
diff --git a/test/lib/test.js b/test/lib/test.js
index 6f4a7395d..f6f3d7afb 100644
--- a/test/lib/test.js
+++ b/test/lib/test.js
@@ -1,7 +1,7 @@
const t = require('tap')
const requireInject = require('require-inject')
let RUN_ARGS = null
-const npmock = {
+const npm = {
commands: {
'run-script': (args, cb) => {
RUN_ARGS = args
@@ -9,15 +9,14 @@ const npmock = {
},
},
}
-const test = requireInject('../../lib/test.js', {
- '../../lib/npm.js': npmock,
-})
+const Test = requireInject('../../lib/test.js')
+const test = new Test(npm)
t.test('run a test', t => {
- test([], (er) => {
+ test.exec([], (er) => {
t.strictSame(RUN_ARGS, ['test'], 'added "test" to the args')
})
- test(['hello', 'world'], (er) => {
+ test.exec(['hello', 'world'], (er) => {
t.strictSame(RUN_ARGS, ['test', 'hello', 'world'], 'added positional args')
})
@@ -26,13 +25,13 @@ t.test('run a test', t => {
})
const otherErr = new Error('should see this')
- npmock.commands['run-script'] = (args, cb) => cb(lcErr)
- test([], (er) => {
+ npm.commands['run-script'] = (args, cb) => cb(lcErr)
+ test.exec([], (er) => {
t.equal(er, 'Test failed. See above for more details.')
})
- npmock.commands['run-script'] = (args, cb) => cb(otherErr)
- test([], (er) => {
+ npm.commands['run-script'] = (args, cb) => cb(otherErr)
+ test.exec([], (er) => {
t.match(er, { message: 'should see this' })
})
diff --git a/test/lib/token.js b/test/lib/token.js
index 6ab841f49..412d2746b 100644
--- a/test/lib/token.js
+++ b/test/lib/token.js
@@ -2,15 +2,13 @@ const { test } = require('tap')
const requireInject = require('require-inject')
const mocks = {
- npm: {},
profile: {},
output: () => {},
log: {},
readUserInfo: {},
}
-const tokenMock = requireInject('../../lib/token.js', {
- '../../lib/npm.js': mocks.npm,
+const Token = requireInject('../../lib/token.js', {
'../../lib/utils/output.js': (...args) => mocks.output(...args),
'../../lib/utils/otplease.js': (opts, fn) => {
return Promise.resolve().then(() => fn(opts))
@@ -19,36 +17,42 @@ const tokenMock = requireInject('../../lib/token.js', {
'npm-profile': mocks.profile,
npmlog: mocks.log,
})
+const token = new Token({})
const tokenWithMocks = (mockRequests) => {
for (const mod in mockRequests) {
- if (typeof mockRequests[mod] === 'function')
- mocks[mod] = mockRequests[mod]
- else {
- for (const key in mockRequests[mod])
- mocks[mod][key] = mockRequests[mod][key]
+ if (mod !== 'npm') {
+ if (typeof mockRequests[mod] === 'function')
+ mocks[mod] = mockRequests[mod]
+ else {
+ for (const key in mockRequests[mod])
+ mocks[mod][key] = mockRequests[mod][key]
+ }
}
}
const reset = () => {
for (const mod in mockRequests) {
- if (typeof mockRequests[mod] === 'function')
- mocks[mod] = () => {}
- else {
- for (const key in mockRequests[mod])
- delete mocks[mod][key]
+ if (mod !== 'npm') {
+ if (typeof mockRequests[mod] === 'function')
+ mocks[mod] = () => {}
+ else {
+ for (const key in mockRequests[mod])
+ delete mocks[mod][key]
+ }
}
}
}
- return [tokenMock, reset]
+ const token = new Token(mockRequests.npm || {})
+ return [token, reset]
}
test('completion', (t) => {
t.plan(5)
const testComp = (argv, expect) => {
- t.resolveMatch(tokenMock.completion({ conf: { argv: { remain: argv } } }), expect, argv.join(' '))
+ t.resolveMatch(token.completion({ conf: { argv: { remain: argv } } }), expect, argv.join(' '))
}
testComp(['npm', 'token'], ['list', 'revoke', 'create'])
@@ -57,7 +61,7 @@ test('completion', (t) => {
testComp(['npm', 'token', 'create'], [])
t.rejects(
- tokenMock.completion({ conf: { argv: { remain: ['npm', 'token', 'foobar'] } } }),
+ token.completion({ conf: { argv: { remain: ['npm', 'token', 'foobar'] } } }),
{ message: 'foobar not recognize' }
)
})
@@ -77,7 +81,7 @@ test('token foobar', (t) => {
t.tearDown(reset)
- tokenMock(['foobar'], (err) => {
+ token.exec(['foobar'], (err) => {
t.match(err.message, 'foobar is not a recognized subcommand')
})
})
@@ -145,7 +149,7 @@ test('token list', (t) => {
t.tearDown(reset)
- token([], (err) => {
+ token.exec([], (err) => {
t.ifError(err, 'npm token list')
})
})
@@ -199,7 +203,7 @@ test('token list json output', (t) => {
t.tearDown(reset)
- token(['list'], (err) => {
+ token.exec(['list'], (err) => {
t.ifError(err, 'npm token list')
})
})
@@ -267,7 +271,7 @@ test('token list parseable output', (t) => {
t.tearDown(reset)
- token(['list'], (err) => {
+ token.exec(['list'], (err) => {
t.ifError(err, 'npm token list')
})
})
@@ -320,7 +324,7 @@ test('token revoke', (t) => {
t.tearDown(reset)
- token(['rm', 'abcd'], (err) => {
+ token.exec(['rm', 'abcd'], (err) => {
t.ifError(err, 'npm token rm')
})
})
@@ -372,7 +376,7 @@ test('token revoke multiple tokens', (t) => {
t.tearDown(reset)
- token(['revoke', 'abcd', 'efgh'], (err) => {
+ token.exec(['revoke', 'abcd', 'efgh'], (err) => {
t.ifError(err, 'npm token rm')
})
})
@@ -424,7 +428,7 @@ test('token revoke json output', (t) => {
t.tearDown(reset)
- token(['delete', 'abcd'], (err) => {
+ token.exec(['delete', 'abcd'], (err) => {
t.ifError(err, 'npm token rm')
})
})
@@ -474,7 +478,7 @@ test('token revoke parseable output', (t) => {
t.tearDown(reset)
- token(['remove', 'abcd'], (err) => {
+ token.exec(['remove', 'abcd'], (err) => {
t.ifError(err, 'npm token rm')
})
})
@@ -524,7 +528,7 @@ test('token revoke by token', (t) => {
t.tearDown(reset)
- token(['rm', 'efgh5678'], (err) => {
+ token.exec(['rm', 'efgh5678'], (err) => {
t.ifError(err, 'npm token rm')
})
})
@@ -544,7 +548,7 @@ test('token revoke requires an id', (t) => {
t.tearDown(reset)
- token(['rm'], (err) => {
+ token.exec(['rm'], (err) => {
t.match(err.message, '`<tokenKey>` argument is required')
})
})
@@ -589,7 +593,7 @@ test('token revoke ambiguous id errors', (t) => {
t.tearDown(reset)
- token(['rm', 'abcd'], (err) => {
+ token.exec(['rm', 'abcd'], (err) => {
t.match(err.message, 'Token ID "abcd" was ambiguous')
})
})
@@ -633,7 +637,7 @@ test('token revoke unknown id errors', (t) => {
t.tearDown(reset)
- token(['rm', 'efgh'], (err) => {
+ token.exec(['rm', 'efgh'], (err) => {
t.match(err.message, 'Unknown token id or value "efgh".')
})
})
@@ -697,7 +701,7 @@ test('token create', (t) => {
t.tearDown(reset)
- token(['create'], (err) => {
+ token.exec(['create'], (err) => {
t.ifError(err, 'npm token create')
})
})
@@ -756,7 +760,7 @@ test('token create json output', (t) => {
t.tearDown(reset)
- token(['create'], (err) => {
+ token.exec(['create'], (err) => {
t.ifError(err, 'npm token create')
})
})
@@ -822,7 +826,7 @@ test('token create parseable output', (t) => {
t.tearDown(reset)
- token(['create'], (err) => {
+ token.exec(['create'], (err) => {
t.ifError(err, 'npm token create')
})
})
@@ -856,7 +860,7 @@ test('token create ipv6 cidr', (t) => {
t.tearDown(reset)
- token(['create'], (err) => {
+ token.exec(['create'], (err) => {
t.equal(err.message, 'CIDR whitelist can only contain IPv4 addresses, ::1/128 is IPv6', 'returns correct error')
t.equal(err.code, 'EINVALIDCIDR')
})
@@ -891,7 +895,7 @@ test('token create invalid cidr', (t) => {
t.tearDown(reset)
- token(['create'], (err) => {
+ token.exec(['create'], (err) => {
t.equal(err.message, 'CIDR whitelist contains invalid CIDR entry: apple/cider', 'returns correct error')
t.equal(err.code, 'EINVALIDCIDR')
})
diff --git a/test/lib/uninstall.js b/test/lib/uninstall.js
index 69040c0f2..c62b59950 100644
--- a/test/lib/uninstall.js
+++ b/test/lib/uninstall.js
@@ -12,12 +12,12 @@ const npm = {
localPrefix: '',
}
const mocks = {
- '../../lib/npm.js': npm,
'../../lib/utils/reify-finish.js': () => Promise.resolve(),
'../../lib/utils/usage.js': () => 'usage instructions',
}
-const uninstall = requireInject('../../lib/uninstall.js', mocks)
+const Uninstall = requireInject('../../lib/uninstall.js', mocks)
+const uninstall = new Uninstall(npm)
t.afterEach(cb => {
npm.globalDir = ''
@@ -87,7 +87,7 @@ t.test('remove single installed lib', t => {
npm.flatOptions.prefix = path
- uninstall(['b'], err => {
+ uninstall.exec(['b'], err => {
if (err)
throw err
@@ -150,7 +150,7 @@ t.test('remove multiple installed libs', t => {
npm.flatOptions.prefix = path
- uninstall(['b'], err => {
+ uninstall.exec(['b'], err => {
if (err)
throw err
@@ -165,7 +165,7 @@ t.test('no args local', t => {
npm.flatOptions.prefix = path
- uninstall([], err => {
+ uninstall.exec([], err => {
t.match(
err,
/Must provide a package name to remove/,
@@ -201,7 +201,7 @@ t.test('no args global', t => {
const a = resolve(path, 'lib/node_modules/a')
t.ok(() => fs.statSync(a))
- uninstall([], err => {
+ uninstall.exec([], err => {
if (err)
throw err
@@ -218,7 +218,7 @@ t.test('no args global but no package.json', t => {
npm.localPrefix = path
npm.flatOptions.global = true
- uninstall([], err => {
+ uninstall.exec([], err => {
t.match(
err,
'usage instructions',
@@ -232,16 +232,17 @@ t.test('no args global but no package.json', t => {
t.test('unknown error reading from localPrefix package.json', t => {
const path = t.testdir({})
- const uninstall = requireInject('../../lib/uninstall.js', {
+ const Uninstall = requireInject('../../lib/uninstall.js', {
...mocks,
'read-package-json-fast': () => Promise.reject(new Error('ERR')),
})
+ const uninstall = new Uninstall(npm)
npm.prefix = path
npm.localPrefix = path
npm.flatOptions.global = true
- uninstall([], err => {
+ uninstall.exec([], err => {
t.match(
err,
/ERR/,
diff --git a/test/lib/unpublish.js b/test/lib/unpublish.js
index c1fbed57e..80a879cb6 100644
--- a/test/lib/unpublish.js
+++ b/test/lib/unpublish.js
@@ -18,7 +18,6 @@ const mocks = {
'npm-package-arg': noop,
'npm-registry-fetch': { json: noop },
'read-package-json': cb => cb(),
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
result += msg.join('\n')
},
@@ -78,7 +77,7 @@ t.test('no args --force', t => {
},
}
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
npmlog,
libnpmpublish,
@@ -88,8 +87,9 @@ t.test('no args --force', t => {
version: '1.0.0',
}),
})
+ const unpublish = new Unpublish(npm)
- unpublish([], err => {
+ unpublish.exec([], err => {
if (err)
throw err
@@ -104,15 +104,16 @@ t.test('no args --force', t => {
t.test('no args --force missing package.json', t => {
npm.flatOptions.force = true
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
'read-package-json': (path, cb) => cb(Object.assign(
new Error('ENOENT'),
{ code: 'ENOENT' }
)),
})
+ const unpublish = new Unpublish(npm)
- unpublish([], err => {
+ unpublish.exec([], err => {
t.match(
err,
/usage instructions/,
@@ -125,12 +126,13 @@ t.test('no args --force missing package.json', t => {
t.test('no args --force unknown error reading package.json', t => {
npm.flatOptions.force = true
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
'read-package-json': (path, cb) => cb(new Error('ERR')),
})
+ const unpublish = new Unpublish(npm)
- unpublish([], err => {
+ unpublish.exec([], err => {
t.match(
err,
/ERR/,
@@ -141,11 +143,12 @@ t.test('no args --force unknown error reading package.json', t => {
})
t.test('no args', t => {
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
})
+ const unpublish = new Unpublish(npm)
- unpublish([], err => {
+ unpublish.exec([], err => {
t.match(
err,
/Refusing to delete entire project/,
@@ -156,11 +159,12 @@ t.test('no args', t => {
})
t.test('too many args', t => {
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
})
+ const unpublish = new Unpublish(npm)
- unpublish(['a', 'b'], err => {
+ unpublish.exec(['a', 'b'], err => {
t.match(
err,
/usage instructions/,
@@ -206,14 +210,15 @@ t.test('unpublish <pkg>@version', t => {
},
}
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
npmlog,
libnpmpublish,
'npm-package-arg': npa,
})
+ const unpublish = new Unpublish(npm)
- unpublish(['pkg@1.0.0'], err => {
+ unpublish.exec(['pkg@1.0.0'], err => {
if (err)
throw err
@@ -235,15 +240,16 @@ t.test('no version found in package.json', t => {
npa.resolve = () => ''
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
'npm-package-arg': npa,
'read-package-json': (path, cb) => cb(null, {
name: 'pkg',
}),
})
+ const unpublish = new Unpublish(npm)
- unpublish([], err => {
+ unpublish.exec([], err => {
if (err)
throw err
@@ -259,7 +265,7 @@ t.test('no version found in package.json', t => {
t.test('unpublish <pkg> --force no version set', t => {
npm.flatOptions.force = true
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
'npm-package-arg': () => ({
name: 'pkg',
@@ -267,8 +273,9 @@ t.test('unpublish <pkg> --force no version set', t => {
type: 'tag',
}),
})
+ const unpublish = new Unpublish(npm)
- unpublish(['pkg'], err => {
+ unpublish.exec(['pkg'], err => {
if (err)
throw err
@@ -292,12 +299,13 @@ t.test('silent', t => {
npa.resolve = () => ''
- const unpublish = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
'npm-package-arg': npa,
})
+ const unpublish = new Unpublish(npm)
- unpublish(['pkg@1.0.0'], err => {
+ unpublish.exec(['pkg@1.0.0'], err => {
if (err)
throw err
@@ -312,13 +320,15 @@ t.test('silent', t => {
t.test('completion', async t => {
const testComp =
- async (t, { completion, argv, partialWord, expect, title }) => {
- const res = await completion({conf: {argv: {remain: argv}}, partialWord})
+ async (t, { unpublish, argv, partialWord, expect, title }) => {
+ const res = await unpublish.completion(
+ {conf: {argv: {remain: argv}}, partialWord}
+ )
t.strictSame(res, expect, title || argv.join(' '))
}
t.test('completing with multiple versions from the registry', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
libnpmaccess: {
async lsPackages () {
@@ -341,9 +351,10 @@ t.test('completion', async t => {
},
},
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: 'pkg',
expect: [
@@ -355,7 +366,7 @@ t.test('completion', async t => {
})
t.test('no versions retrieved', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
libnpmaccess: {
async lsPackages () {
@@ -374,9 +385,10 @@ t.test('completion', async t => {
},
},
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: 'pkg',
expect: [
@@ -387,7 +399,7 @@ t.test('completion', async t => {
})
t.test('packages starting with same letters', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
libnpmaccess: {
async lsPackages () {
@@ -400,9 +412,10 @@ t.test('completion', async t => {
},
'npm-package-arg': require('npm-package-arg'),
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: 'pkg',
expect: [
@@ -414,7 +427,7 @@ t.test('completion', async t => {
})
t.test('no packages retrieved', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
libnpmaccess: {
async lsPackages () {
@@ -422,9 +435,10 @@ t.test('completion', async t => {
},
},
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: 'pkg',
expect: [],
@@ -433,7 +447,7 @@ t.test('completion', async t => {
})
t.test('no pkg name to complete', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
libnpmaccess: {
async lsPackages () {
@@ -444,9 +458,10 @@ t.test('completion', async t => {
},
},
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: undefined,
expect: ['pkg', 'bar'],
@@ -455,7 +470,7 @@ t.test('completion', async t => {
})
t.test('no pkg names retrieved from user account', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
libnpmaccess: {
async lsPackages () {
@@ -463,9 +478,10 @@ t.test('completion', async t => {
},
},
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: 'pkg',
expect: [],
@@ -474,13 +490,14 @@ t.test('completion', async t => {
})
t.test('logged out user', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', {
+ const Unpublish = requireInject('../../lib/unpublish.js', {
...mocks,
'../../lib/utils/get-identity.js': () => Promise.reject(new Error('ERR')),
})
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish'],
partialWord: 'pkg',
expect: [],
@@ -488,10 +505,11 @@ t.test('completion', async t => {
})
t.test('too many args', async t => {
- const { completion } = requireInject('../../lib/unpublish.js', mocks)
+ const Unpublish = requireInject('../../lib/unpublish.js', mocks)
+ const unpublish = new Unpublish(npm)
await testComp(t, {
- completion,
+ unpublish,
argv: ['npm', 'unpublish', 'foo'],
partialWord: undefined,
expect: [],
diff --git a/test/lib/unstar.js b/test/lib/unstar.js
index 63b2028a1..3f3487176 100644
--- a/test/lib/unstar.js
+++ b/test/lib/unstar.js
@@ -4,24 +4,30 @@ const t = require('tap')
t.test('unstar', t => {
t.plan(3)
- const unstar = requireInject('../../lib/unstar.js', {
- '../../lib/npm.js': {
- config: {
- set: (key, value) => {
- t.equal(key, 'star.unstar', 'should set unstar config value')
- t.equal(value, true, 'should set a truthy value')
- },
- },
- commands: {
- star: (args, cb) => {
- t.deepEqual(args, ['pkg'], 'should forward packages')
- cb()
- },
+ class Star {
+ constructor (npm) {
+ this.npm = npm
+ }
+
+ exec (args, cb) {
+ t.deepEqual(args, ['pkg'], 'should forward packages')
+ cb()
+ }
+ }
+ const Unstar = requireInject('../../lib/unstar.js', {
+ '../../lib/star.js': Star,
+ })
+
+ const unstar = new Unstar({
+ config: {
+ set: (key, value) => {
+ t.equal(key, 'star.unstar', 'should set unstar config value')
+ t.equal(value, true, 'should set a truthy value')
},
},
})
- unstar(['pkg'], err => {
+ unstar.exec(['pkg'], err => {
if (err)
throw err
})
diff --git a/test/lib/update.js b/test/lib/update.js
index 993fbbab5..15195573f 100644
--- a/test/lib/update.js
+++ b/test/lib/update.js
@@ -16,7 +16,6 @@ const mocks = {
'@npmcli/arborist': class {
reify () {}
},
- '../../lib/npm.js': npm,
'../../lib/utils/reify-finish.js': noop,
'../../lib/utils/usage.js': () => 'usage instructions',
}
@@ -47,15 +46,16 @@ t.test('no args', t => {
}
}
- const update = requireInject('../../lib/update.js', {
+ const Update = requireInject('../../lib/update.js', {
...mocks,
- '../../lib/utils/reify-finish.js': (arb) => {
+ '../../lib/utils/reify-finish.js': (npm, arb) => {
t.isLike(arb, Arborist, 'should reify-finish with arborist instance')
},
'@npmcli/arborist': Arborist,
})
+ const update = new Update(npm)
- update([], err => {
+ update.exec([], err => {
if (err)
throw err
})
@@ -80,15 +80,16 @@ t.test('with args', t => {
}
}
- const update = requireInject('../../lib/update.js', {
+ const Update = requireInject('../../lib/update.js', {
...mocks,
- '../../lib/utils/reify-finish.js': (arb) => {
+ '../../lib/utils/reify-finish.js': (npm, arb) => {
t.isLike(arb, Arborist, 'should reify-finish with arborist instance')
},
'@npmcli/arborist': Arborist,
})
+ const update = new Update(npm)
- update(['ipt'], err => {
+ update.exec(['ipt'], err => {
if (err)
throw err
})
@@ -100,7 +101,7 @@ t.test('update --depth=<number>', t => {
npm.prefix = '/project/a'
npm.flatOptions.depth = 1
- const update = requireInject('../../lib/update.js', {
+ const Update = requireInject('../../lib/update.js', {
...mocks,
npmlog: {
warn: (title, msg) => {
@@ -113,8 +114,9 @@ t.test('update --depth=<number>', t => {
},
},
})
+ const update = new Update(npm)
- update([], err => {
+ update.exec([], err => {
if (err)
throw err
})
@@ -150,12 +152,13 @@ t.test('update --global', t => {
reify () {}
}
- const update = requireInject('../../lib/update.js', {
+ const Update = requireInject('../../lib/update.js', {
...mocks,
'@npmcli/arborist': Arborist,
})
+ const update = new Update(npm)
- update([], err => {
+ update.exec([], err => {
if (err)
throw err
})
diff --git a/test/lib/utils/audit-error.js b/test/lib/utils/audit-error.js
index cc5f4c006..ea7c84373 100644
--- a/test/lib/utils/audit-error.js
+++ b/test/lib/utils/audit-error.js
@@ -12,7 +12,6 @@ const npm = {
const OUTPUT = []
const output = (...msg) => OUTPUT.push(msg)
const auditError = requireInject('../../../lib/utils/audit-error.js', {
- '../../../lib/npm.js': npm,
'../../../lib/utils/output.js': output,
})
@@ -25,7 +24,7 @@ t.afterEach(cb => {
t.test('no error, not audit command', t => {
npm.command = 'install'
- t.equal(auditError({}), false, 'no error')
+ t.equal(auditError(npm, {}), false, 'no error')
t.strictSame(OUTPUT, [], 'no output')
t.strictSame(LOGS, [], 'no warnings')
t.end()
@@ -33,7 +32,7 @@ t.test('no error, not audit command', t => {
t.test('error, not audit command', t => {
npm.command = 'install'
- t.equal(auditError({
+ t.equal(auditError(npm, {
error: {
message: 'message',
body: Buffer.from('body'),
@@ -53,7 +52,7 @@ t.test('error, not audit command', t => {
t.test('error, audit command, not json', t => {
npm.command = 'audit'
npm.flatOptions.json = false
- t.throws(() => auditError({
+ t.throws(() => auditError(npm, {
error: {
message: 'message',
body: Buffer.from('body'),
@@ -74,7 +73,7 @@ t.test('error, audit command, not json', t => {
t.test('error, audit command, json', t => {
npm.command = 'audit'
npm.flatOptions.json = true
- t.throws(() => auditError({
+ t.throws(() => auditError(npm, {
error: {
message: 'message',
body: { response: 'body' },
diff --git a/test/lib/utils/completion/installed-deep.js b/test/lib/utils/completion/installed-deep.js
index bd61ab428..0e80a5a19 100644
--- a/test/lib/utils/completion/installed-deep.js
+++ b/test/lib/utils/completion/installed-deep.js
@@ -12,22 +12,21 @@ const _flatOptions = {
},
}
const p = '../../../../lib/utils/completion/installed-deep.js'
-const installedDeep = requireInject(p, {
- '../../../../lib/npm.js': {
- flatOptions: _flatOptions,
- get prefix () {
- return _flatOptions.prefix
- },
- get globalDir () {
- return globalDir
- },
- config: {
- get (key) {
- return _flatOptions[key]
- },
+const installedDeep = requireInject(p)
+const npm = {
+ flatOptions: _flatOptions,
+ get prefix () {
+ return _flatOptions.prefix
+ },
+ get globalDir () {
+ return globalDir
+ },
+ config: {
+ get (key) {
+ return _flatOptions[key]
},
},
-})
+}
const fixture = {
'package.json': JSON.stringify({
@@ -154,7 +153,7 @@ test('get list of package names', async t => {
prefix = resolve(fix, 'local')
globalDir = resolve(fix, 'global/node_modules')
- const res = await installedDeep(null)
+ const res = await installedDeep(npm, null)
t.deepEqual(
res,
[
@@ -181,7 +180,7 @@ test('get list of package names as global', async t => {
_flatOptions.global = true
- const res = await installedDeep(null)
+ const res = await installedDeep(npm, null)
t.deepEqual(
res,
[
@@ -206,7 +205,7 @@ test('limit depth', async t => {
_flatOptions.depth = 0
- const res = await installedDeep(null)
+ const res = await installedDeep(npm, null)
t.deepEqual(
res,
[
@@ -235,7 +234,7 @@ test('limit depth as global', async t => {
_flatOptions.global = true
_flatOptions.depth = 0
- const res = await installedDeep(null)
+ const res = await installedDeep(npm, null)
t.deepEqual(
res,
[
diff --git a/test/lib/utils/completion/installed-shallow.js b/test/lib/utils/completion/installed-shallow.js
index 1da68810b..1067a50ac 100644
--- a/test/lib/utils/completion/installed-shallow.js
+++ b/test/lib/utils/completion/installed-shallow.js
@@ -5,9 +5,7 @@ const t = require('tap')
const { resolve } = require('path')
const p = '../../../../lib/utils/completion/installed-shallow.js'
-const installed = requireInject(p, {
- '../../../../lib/npm.js': npm,
-})
+const installed = requireInject(p)
t.test('global not set, include globals with -g', async t => {
const dir = t.testdir({
@@ -32,7 +30,7 @@ t.test('global not set, include globals with -g', async t => {
npm.localDir = resolve(dir, 'local/node_modules')
flatOptions.global = false
const opt = { conf: { argv: { remain: [] } } }
- const res = await installed(opt)
+ const res = await installed(npm, opt)
t.strictSame(res.sort(), [
'@scope/y -g',
'x -g',
@@ -65,7 +63,7 @@ t.test('global set, include globals and not locals', async t => {
npm.localDir = resolve(dir, 'local/node_modules')
flatOptions.global = true
const opt = { conf: { argv: { remain: [] } } }
- const res = await installed(opt)
+ const res = await installed(npm, opt)
t.strictSame(res.sort(), [
'@scope/y',
'x',
@@ -96,7 +94,7 @@ t.test('more than 3 items in argv, skip it', async t => {
npm.localDir = resolve(dir, 'local/node_modules')
flatOptions.global = false
const opt = { conf: { argv: { remain: [1, 2, 3, 4, 5, 6] } } }
- const res = await installed(opt)
+ const res = await installed(npm, opt)
t.strictSame(res, null)
t.end()
})
diff --git a/test/lib/utils/get-identity.js b/test/lib/utils/get-identity.js
index 8a4de8835..cc713b337 100644
--- a/test/lib/utils/get-identity.js
+++ b/test/lib/utils/get-identity.js
@@ -3,12 +3,10 @@ const requireInject = require('require-inject')
test('throws ENOREGISTRY when no registry option is provided', async (t) => {
t.plan(2)
- const getIdentity = requireInject('../../../lib/utils/get-identity.js', {
- '../../../lib/npm.js': {},
- })
+ const getIdentity = requireInject('../../../lib/utils/get-identity.js')
try {
- await getIdentity()
+ await getIdentity({})
} catch (err) {
t.equal(err.code, 'ENOREGISTRY', 'assigns the appropriate error code')
t.equal(err.message, 'No registry specified.', 'returns the correct error message')
@@ -18,17 +16,16 @@ test('throws ENOREGISTRY when no registry option is provided', async (t) => {
test('returns username from uri when provided', async (t) => {
t.plan(1)
- const getIdentity = requireInject('../../../lib/utils/get-identity.js', {
- '../../../lib/npm.js': {
- config: {
- getCredentialsByURI: () => {
- return { username: 'foo' }
- },
+ const getIdentity = requireInject('../../../lib/utils/get-identity.js')
+ const npm = {
+ config: {
+ getCredentialsByURI: () => {
+ return { username: 'foo' }
},
},
- })
+ }
- const identity = await getIdentity({ registry: 'https://registry.npmjs.org' })
+ const identity = await getIdentity(npm, { registry: 'https://registry.npmjs.org' })
t.equal(identity, 'foo', 'returns username from uri')
})
@@ -41,11 +38,6 @@ test('calls registry whoami when token is provided', async (t) => {
}
const getIdentity = requireInject('../../../lib/utils/get-identity.js', {
- '../../../lib/npm.js': {
- config: {
- getCredentialsByURI: () => options,
- },
- },
'npm-registry-fetch': {
json: (path, opts) => {
t.equal(path, '/-/whoami', 'calls whoami')
@@ -54,8 +46,13 @@ test('calls registry whoami when token is provided', async (t) => {
},
},
})
+ const npm = {
+ config: {
+ getCredentialsByURI: () => options,
+ },
+ }
- const identity = await getIdentity(options)
+ const identity = await getIdentity(npm, options)
t.equal(identity, 'foo', 'fetched username from registry')
})
@@ -68,11 +65,6 @@ test('throws ENEEDAUTH when response does not include a username', async (t) =>
}
const getIdentity = requireInject('../../../lib/utils/get-identity.js', {
- '../../../lib/npm.js': {
- config: {
- getCredentialsByURI: () => options,
- },
- },
'npm-registry-fetch': {
json: (path, opts) => {
t.equal(path, '/-/whoami', 'calls whoami')
@@ -81,9 +73,14 @@ test('throws ENEEDAUTH when response does not include a username', async (t) =>
},
},
})
+ const npm = {
+ config: {
+ getCredentialsByURI: () => options,
+ },
+ }
try {
- await getIdentity(options)
+ await getIdentity(npm, options)
} catch (err) {
t.equal(err.code, 'ENEEDAUTH', 'throws correct error code')
}
@@ -92,15 +89,15 @@ test('throws ENEEDAUTH when response does not include a username', async (t) =>
test('throws ENEEDAUTH when neither username nor token is configured', async (t) => {
t.plan(1)
const getIdentity = requireInject('../../../lib/utils/get-identity.js', {
- '../../../lib/npm.js': {
- config: {
- getCredentialsByURI: () => ({}),
- },
- },
})
+ const npm = {
+ config: {
+ getCredentialsByURI: () => ({}),
+ },
+ }
try {
- await getIdentity({ registry: 'https://registry.npmjs.org' })
+ await getIdentity(npm, { registry: 'https://registry.npmjs.org' })
} catch (err) {
t.equal(err.code, 'ENEEDAUTH', 'throws correct error code')
}
diff --git a/test/lib/utils/lifecycle-cmd.js b/test/lib/utils/lifecycle-cmd.js
index 3928c1b26..2f1f693f2 100644
--- a/test/lib/utils/lifecycle-cmd.js
+++ b/test/lib/utils/lifecycle-cmd.js
@@ -1,13 +1,19 @@
const t = require('tap')
-const lifecycleCmd = require('../../../lib/utils/lifecycle-cmd.js')
+const LifecycleCmd = require('../../../lib/utils/lifecycle-cmd.js')
+let runArgs = null
const npm = {
commands: {
- 'run-script': (args, cb) => cb(null, 'called npm.commands.run'),
+ 'run-script': (args, cb) => {
+ runArgs = args
+ cb(null, 'called npm.commands.run')
+ },
},
}
t.test('create a lifecycle command', t => {
- const cmd = lifecycleCmd(npm, 'asdf')
- cmd(['some', 'args'], (er, result) => {
+ const cmd = new LifecycleCmd(npm, 'test-stage')
+ t.match(cmd.usage, /test-stage/)
+ cmd.exec(['some', 'args'], (er, result) => {
+ t.same(runArgs, ['test-stage', 'some', 'args'])
t.strictSame(result, 'called npm.commands.run')
t.end()
})
diff --git a/test/lib/utils/npm-usage.js b/test/lib/utils/npm-usage.js
index 72504b90c..dbbde947c 100644
--- a/test/lib/utils/npm-usage.js
+++ b/test/lib/utils/npm-usage.js
@@ -1,127 +1,121 @@
const t = require('tap')
-const deref = require('../../../lib/utils/deref-command.js')
-const npm = {
- argv: [],
- deref,
- config: {
- _options: {
- viewer: null,
- long: false,
- userconfig: '/some/config/file/.npmrc',
- },
- get: k => {
- if (npm.config._options[k] === undefined)
- throw new Error('unknown config')
- return npm.config._options[k]
- },
- set: (k, v) => {
- npm.config._options[k] = v
- },
- },
- log: {},
- version: '{VERSION}',
-}
-
const OUTPUT = []
const output = (...msg) => OUTPUT.push(msg)
-
-const { dirname } = require('path')
-const basedir = dirname(dirname(dirname(__dirname)))
-t.cleanSnapshot = str => str.split(basedir).join('{BASEDIR}')
- .split(require('../../../package.json').version).join('{VERSION}')
-
const requireInject = require('require-inject')
const usage = requireInject('../../../lib/utils/npm-usage.js', {
- '../../../lib/npm.js': npm,
'../../../lib/utils/output.js': output,
})
+const npm = requireInject('../../../lib/npm.js')
-t.test('basic usage', t => {
- usage()
- t.equal(OUTPUT.length, 1)
- t.equal(OUTPUT[0].length, 1)
- t.matchSnapshot(OUTPUT[0][0])
- OUTPUT.length = 0
- t.end()
-})
-
-t.test('with browser', t => {
- npm.config.set('viewer', 'browser')
- usage()
- t.equal(OUTPUT.length, 1)
- t.equal(OUTPUT[0].length, 1)
- t.matchSnapshot(OUTPUT[0][0])
- OUTPUT.length = 0
- npm.config.set('viewer', null)
- t.end()
-})
+t.test('usage', t => {
+ t.afterEach((cb) => {
+ npm.config.set('viewer', null)
+ npm.config.set('long', false)
+ npm.config.set('userconfig', '/some/config/file/.npmrc')
+ cb()
+ })
+ const { dirname } = require('path')
+ const basedir = dirname(dirname(dirname(__dirname)))
+ t.cleanSnapshot = str => str.split(basedir).join('{BASEDIR}')
+ .split(require('../../../package.json').version).join('{VERSION}')
-t.test('with long', t => {
- npm.config.set('long', true)
- usage()
- t.equal(OUTPUT.length, 1)
- t.equal(OUTPUT[0].length, 1)
- t.matchSnapshot(OUTPUT[0][0])
- OUTPUT.length = 0
- npm.config.set('long', false)
- t.end()
-})
+ npm.load(err => {
+ if (err)
+ throw err
-t.test('did you mean?', t => {
- npm.argv.push('unistnall')
- usage()
- t.equal(OUTPUT.length, 2)
- t.equal(OUTPUT[0].length, 1)
- t.equal(OUTPUT[1].length, 1)
- t.matchSnapshot(OUTPUT[0][0])
- t.matchSnapshot(OUTPUT[1][0])
- OUTPUT.length = 0
- npm.argv.length = 0
- t.end()
-})
+ npm.config.set('viewer', null)
+ npm.config.set('long', false)
+ npm.config.set('userconfig', '/some/config/file/.npmrc')
-t.test('did you mean?', t => {
- npm.argv.push('unistnall')
- const { exitCode } = process
- t.teardown(() => {
- if (t.passing())
- process.exitCode = exitCode
- })
- // make sure it fails when invalid
- usage(false)
- t.equal(process.exitCode, 1)
- OUTPUT.length = 0
- npm.argv.length = 0
- t.end()
-})
+ t.test('basic usage', t => {
+ usage(npm)
+ t.equal(OUTPUT.length, 1)
+ t.equal(OUTPUT[0].length, 1)
+ t.matchSnapshot(OUTPUT[0][0])
+ OUTPUT.length = 0
+ t.end()
+ })
-t.test('set process.stdout.columns', t => {
- const { columns } = process.stdout
- t.teardown(() => {
- Object.defineProperty(process.stdout, 'columns', {
- value: columns,
- enumerable: true,
- configurable: true,
- writable: true,
+ t.test('with browser', t => {
+ npm.config.set('viewer', 'browser')
+ usage(npm)
+ t.equal(OUTPUT.length, 1)
+ t.equal(OUTPUT[0].length, 1)
+ t.matchSnapshot(OUTPUT[0][0])
+ OUTPUT.length = 0
+ npm.config.set('viewer', null)
+ t.end()
})
- })
- const cases = [0, 90]
- for (const cols of cases) {
- t.test(`columns=${cols}`, t => {
- Object.defineProperty(process.stdout, 'columns', {
- value: cols,
- enumerable: true,
- configurable: true,
- writable: true,
- })
- usage()
+
+ t.test('with long', t => {
+ npm.config.set('long', true)
+ usage(npm)
t.equal(OUTPUT.length, 1)
t.equal(OUTPUT[0].length, 1)
t.matchSnapshot(OUTPUT[0][0])
OUTPUT.length = 0
+ npm.config.set('long', false)
+ t.end()
+ })
+
+ t.test('did you mean?', t => {
+ npm.argv.push('unistnall')
+ usage(npm)
+ t.equal(OUTPUT.length, 2)
+ t.equal(OUTPUT[0].length, 1)
+ t.equal(OUTPUT[1].length, 1)
+ t.matchSnapshot(OUTPUT[0][0])
+ t.matchSnapshot(OUTPUT[1][0])
+ OUTPUT.length = 0
+ npm.argv.length = 0
t.end()
})
- }
- t.end()
+
+ t.test('did you mean?', t => {
+ npm.argv.push('unistnall')
+ const { exitCode } = process
+ t.teardown(() => {
+ if (t.passing())
+ process.exitCode = exitCode
+ })
+ // make sure it fails when invalid
+ usage(npm, false)
+ t.equal(process.exitCode, 1)
+ OUTPUT.length = 0
+ npm.argv.length = 0
+ t.end()
+ })
+
+ t.test('set process.stdout.columns', t => {
+ const { columns } = process.stdout
+ t.teardown(() => {
+ Object.defineProperty(process.stdout, 'columns', {
+ value: columns,
+ enumerable: true,
+ configurable: true,
+ writable: true,
+ })
+ })
+ const cases = [0, 90]
+ for (const cols of cases) {
+ t.test(`columns=${cols}`, t => {
+ Object.defineProperty(process.stdout, 'columns', {
+ value: cols,
+ enumerable: true,
+ configurable: true,
+ writable: true,
+ })
+ usage(npm)
+ t.equal(OUTPUT.length, 1)
+ t.equal(OUTPUT[0].length, 1)
+ t.matchSnapshot(OUTPUT[0][0])
+ OUTPUT.length = 0
+ t.end()
+ })
+ }
+ t.end()
+ })
+ t.end()
+ })
})
diff --git a/test/lib/utils/open-url.js b/test/lib/utils/open-url.js
index ce1783dad..e8ab8f15a 100644
--- a/test/lib/utils/open-url.js
+++ b/test/lib/utils/open-url.js
@@ -27,59 +27,49 @@ const opener = (url, opts, cb) => {
}
const openUrl = requireInject('../../../lib/utils/open-url.js', {
- '../../../lib/npm.js': npm,
'../../../lib/utils/output.js': output,
opener,
})
-test('opens a url', (t) => {
+test('opens a url', async (t) => {
t.teardown(() => {
openerUrl = null
openerOpts = null
OUTPUT.length = 0
})
- openUrl('https://www.npmjs.com', 'npm home', (err) => {
- if (err)
- throw err
-
- t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url')
- t.same(openerOpts, { command: null }, 'passed command as null (the default)')
- t.same(OUTPUT, [], 'printed no output')
- t.done()
- })
+ await openUrl(npm, 'https://www.npmjs.com', 'npm home')
+ t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url')
+ t.same(openerOpts, { command: null }, 'passed command as null (the default)')
+ t.same(OUTPUT, [], 'printed no output')
})
-test('returns error for non-https and non-file url', (t) => {
+test('returns error for non-https and non-file url', async (t) => {
t.teardown(() => {
openerUrl = null
openerOpts = null
OUTPUT.length = 0
})
- openUrl('ftp://www.npmjs.com', 'npm home', (err) => {
- t.match(err, /Invalid URL/, 'got the correct error')
- t.equal(openerUrl, null, 'did not open')
- t.same(openerOpts, null, 'did not open')
- t.same(OUTPUT, [], 'printed no output')
- t.done()
- })
+ t.rejects(openUrl(npm, 'ftp://www.npmjs.com', 'npm home'), /Invalid URL/, 'got the correct error')
+ t.equal(openerUrl, null, 'did not open')
+ t.same(openerOpts, null, 'did not open')
+ t.same(OUTPUT, [], 'printed no output')
+ t.done()
})
-test('returns error for non-parseable url', (t) => {
+test('returns error for non-parseable url', async (t) => {
t.teardown(() => {
openerUrl = null
openerOpts = null
OUTPUT.length = 0
})
- openUrl('git+ssh://user@host:repo.git', 'npm home', (err) => {
- t.match(err, /Invalid URL/, 'got the correct error')
- t.equal(openerUrl, null, 'did not open')
- t.same(openerOpts, null, 'did not open')
- t.same(OUTPUT, [], 'printed no output')
- t.done()
- })
+ t.rejects(openUrl(npm, 'git+ssh://user@host:repo.git', 'npm home'), /Invalid URL/, 'got the correct error')
+ t.equal(openerUrl, null, 'did not open')
+ t.same(openerOpts, null, 'did not open')
+ t.same(OUTPUT, [], 'printed no output')
+ t.done()
})
-test('opens a url with the given browser', (t) => {
+test('opens a url with the given browser', async (t) => {
npm.config.set('browser', 'chrome')
t.teardown(() => {
openerUrl = null
@@ -87,18 +77,14 @@ test('opens a url with the given browser', (t) => {
OUTPUT.length = 0
npm.config.set('browser', true)
})
- openUrl('https://www.npmjs.com', 'npm home', (err) => {
- if (err)
- throw err
-
- t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url')
- t.same(openerOpts, { command: 'chrome' }, 'passed the given browser as command')
- t.same(OUTPUT, [], 'printed no output')
- t.done()
- })
+ await openUrl(npm, 'https://www.npmjs.com', 'npm home')
+ t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url')
+ t.same(openerOpts, { command: 'chrome' }, 'passed the given browser as command')
+ t.same(OUTPUT, [], 'printed no output')
+ t.done()
})
-test('prints where to go when browser is disabled', (t) => {
+test('prints where to go when browser is disabled', async (t) => {
npm.config.set('browser', false)
t.teardown(() => {
openerUrl = null
@@ -106,20 +92,16 @@ test('prints where to go when browser is disabled', (t) => {
OUTPUT.length = 0
npm.config.set('browser', true)
})
- openUrl('https://www.npmjs.com', 'npm home', (err) => {
- if (err)
- throw err
-
- t.equal(openerUrl, null, 'did not open')
- t.same(openerOpts, null, 'did not open')
- t.equal(OUTPUT.length, 1, 'got one logged message')
- t.equal(OUTPUT[0].length, 1, 'logged message had one value')
- t.matchSnapshot(OUTPUT[0][0], 'printed expected message')
- t.done()
- })
+ await openUrl(npm, 'https://www.npmjs.com', 'npm home')
+ t.equal(openerUrl, null, 'did not open')
+ t.same(openerOpts, null, 'did not open')
+ t.equal(OUTPUT.length, 1, 'got one logged message')
+ t.equal(OUTPUT[0].length, 1, 'logged message had one value')
+ t.matchSnapshot(OUTPUT[0][0], 'printed expected message')
+ t.done()
})
-test('prints where to go when browser is disabled and json is enabled', (t) => {
+test('prints where to go when browser is disabled and json is enabled', async (t) => {
npm.config.set('browser', false)
npm.config.set('json', true)
t.teardown(() => {
@@ -129,20 +111,16 @@ test('prints where to go when browser is disabled and json is enabled', (t) => {
npm.config.set('browser', true)
npm.config.set('json', false)
})
- openUrl('https://www.npmjs.com', 'npm home', (err) => {
- if (err)
- throw err
-
- t.equal(openerUrl, null, 'did not open')
- t.same(openerOpts, null, 'did not open')
- t.equal(OUTPUT.length, 1, 'got one logged message')
- t.equal(OUTPUT[0].length, 1, 'logged message had one value')
- t.matchSnapshot(OUTPUT[0][0], 'printed expected message')
- t.done()
- })
+ await openUrl(npm, 'https://www.npmjs.com', 'npm home')
+ t.equal(openerUrl, null, 'did not open')
+ t.same(openerOpts, null, 'did not open')
+ t.equal(OUTPUT.length, 1, 'got one logged message')
+ t.equal(OUTPUT[0].length, 1, 'logged message had one value')
+ t.matchSnapshot(OUTPUT[0][0], 'printed expected message')
+ t.done()
})
-test('prints where to go when given browser does not exist', (t) => {
+test('prints where to go when given browser does not exist', async (t) => {
npm.config.set('browser', 'firefox')
openerResult = Object.assign(new Error('failed'), { code: 'ENOENT' })
t.teardown(() => {
@@ -151,15 +129,24 @@ test('prints where to go when given browser does not exist', (t) => {
OUTPUT.length = 0
npm.config.set('browser', true)
})
- openUrl('https://www.npmjs.com', 'npm home', (err) => {
- if (err)
- throw err
+ await openUrl(npm, 'https://www.npmjs.com', 'npm home')
+ t.equal(openerUrl, 'https://www.npmjs.com', 'tried to open the correct url')
+ t.same(openerOpts, { command: 'firefox' }, 'tried to use the correct browser')
+ t.equal(OUTPUT.length, 1, 'got one logged message')
+ t.equal(OUTPUT[0].length, 1, 'logged message had one value')
+ t.matchSnapshot(OUTPUT[0][0], 'printed expected message')
+ t.done()
+})
- t.equal(openerUrl, 'https://www.npmjs.com', 'tried to open the correct url')
- t.same(openerOpts, { command: 'firefox' }, 'tried to use the correct browser')
- t.equal(OUTPUT.length, 1, 'got one logged message')
- t.equal(OUTPUT[0].length, 1, 'logged message had one value')
- t.matchSnapshot(OUTPUT[0][0], 'printed expected message')
- t.done()
+test('handles unknown opener error', async (t) => {
+ npm.config.set('browser', 'firefox')
+ openerResult = Object.assign(new Error('failed'), { code: 'ENOBRIAN' })
+ t.teardown(() => {
+ openerUrl = null
+ openerOpts = null
+ OUTPUT.length = 0
+ npm.config.set('browser', true)
})
+ t.rejects(openUrl(npm, 'https://www.npmjs.com', 'npm home'), 'failed', 'got the correct error')
+ t.done()
})
diff --git a/test/lib/utils/read-local-package.js b/test/lib/utils/read-local-package.js
index 33a408eb5..9ae21f7d6 100644
--- a/test/lib/utils/read-local-package.js
+++ b/test/lib/utils/read-local-package.js
@@ -10,11 +10,10 @@ const _flatOptions = {
},
}
-const readLocalPackageName = requireInject('../../../lib/utils/read-local-package.js', {
- '../../../lib/npm.js': {
- flatOptions: _flatOptions,
- },
-})
+const readLocalPackageName = requireInject('../../../lib/utils/read-local-package.js')
+const npm = {
+ flatOptions: _flatOptions,
+}
test('read local package.json', async (t) => {
prefix = t.testdir({
@@ -23,7 +22,7 @@ test('read local package.json', async (t) => {
version: '1.0.0',
}),
})
- const packageName = await readLocalPackageName()
+ const packageName = await readLocalPackageName(npm)
t.equal(
packageName,
'my-local-package',
@@ -38,7 +37,7 @@ test('read local scoped-package.json', async (t) => {
version: '1.0.0',
}),
})
- const packageName = await readLocalPackageName()
+ const packageName = await readLocalPackageName(npm)
t.equal(
packageName,
'@my-scope/my-local-package',
@@ -49,7 +48,7 @@ test('read local scoped-package.json', async (t) => {
test('read using --global', async (t) => {
prefix = t.testdir({})
_flatOptions.global = true
- const packageName = await readLocalPackageName()
+ const packageName = await readLocalPackageName(npm)
t.equal(
packageName,
undefined,
diff --git a/test/lib/utils/reify-finish.js b/test/lib/utils/reify-finish.js
index 39277f21a..7ff5146a6 100644
--- a/test/lib/utils/reify-finish.js
+++ b/test/lib/utils/reify-finish.js
@@ -32,13 +32,12 @@ const fs = {
const reifyFinish = requireInject('../../../lib/utils/reify-finish.js', {
fs,
- '../../../lib/npm.js': npm,
'../../../lib/utils/reify-output.js': reifyOutput,
})
t.test('should not write if not global', async t => {
expectWrite = false
- await reifyFinish({
+ await reifyFinish(npm, {
options: { global: false },
actualTree: {},
})
@@ -46,7 +45,7 @@ t.test('should not write if not global', async t => {
t.test('should not write if no global npm module', async t => {
expectWrite = false
- await reifyFinish({
+ await reifyFinish(npm, {
options: { global: true },
actualTree: {
inventory: new Map(),
@@ -56,7 +55,7 @@ t.test('should not write if no global npm module', async t => {
t.test('should not write if builtin conf had load error', async t => {
expectWrite = false
- await reifyFinish({
+ await reifyFinish(npm, {
options: { global: true },
actualTree: {
inventory: new Map([['node_modules/npm', {}]]),
@@ -68,7 +67,7 @@ t.test('should write if everything above passes', async t => {
expectWrite = true
delete builtinConfMock.loadError
const path = t.testdir()
- await reifyFinish({
+ await reifyFinish(npm, {
options: { global: true },
actualTree: {
inventory: new Map([['node_modules/npm', {path}]]),
diff --git a/test/lib/utils/reify-output.js b/test/lib/utils/reify-output.js
index f88f072e1..e41eabcb8 100644
--- a/test/lib/utils/reify-output.js
+++ b/test/lib/utils/reify-output.js
@@ -9,7 +9,7 @@ t.cleanSnapshot = str => str.replace(/in [0-9]+m?s/g, 'in {TIME}')
const settings = {
fund: true,
}
-const npmock = {
+const npm = {
started: Date.now(),
flatOptions: settings,
}
@@ -17,7 +17,6 @@ const getReifyOutput = tester =>
requireInject(
'../../../lib/utils/reify-output.js',
{
- '../../../lib/npm.js': npmock,
'../../../lib/utils/output.js': tester,
}
)
@@ -32,7 +31,7 @@ t.test('missing info', (t) => {
)
)
- reifyOutput({
+ reifyOutput(npm, {
actualTree: {
children: [],
},
@@ -52,7 +51,7 @@ t.test('even more missing info', t => {
)
)
- reifyOutput({
+ reifyOutput(npm, {
actualTree: {
children: [],
},
@@ -73,7 +72,7 @@ t.test('single package', (t) => {
}
)
- reifyOutput({
+ reifyOutput(npm, {
// a report with an error is the same as no report at all, if
// the command is not 'audit'
auditReport: {
@@ -118,7 +117,7 @@ t.test('no message when funding config is false', (t) => {
}
)
- reifyOutput({
+ reifyOutput(npm, {
actualTree: {
name: 'foo',
package: {
@@ -160,7 +159,7 @@ t.test('print appropriate message for many packages', (t) => {
}
)
- reifyOutput({
+ reifyOutput(npm, {
actualTree: {
name: 'foo',
package: {
@@ -212,7 +211,7 @@ t.test('no output when silent', t => {
})
t.teardown(() => log.level = 'warn')
log.level = 'silent'
- reifyOutput({
+ reifyOutput(npm, {
actualTree: { inventory: { size: 999 }, children: [] },
auditReport: {
toJSON: () => {
@@ -243,7 +242,7 @@ t.test('packages changed message', t => {
// return a test function that builds up the mock and snapshots output
const testCase = (t, added, removed, changed, audited, json, command) => {
settings.json = json
- npmock.command = command
+ npm.command = command
const mock = {
actualTree: {
inventory: { size: audited, has: () => true },
@@ -276,7 +275,7 @@ t.test('packages changed message', t => {
mock.diff.children.push({ action: 'CHANGE', actual, ideal })
}
output.length = 0
- reifyOutput(mock)
+ reifyOutput(npm, mock)
t.matchSnapshot(output.join('\n'), JSON.stringify({
added,
removed,
@@ -316,7 +315,7 @@ t.test('added packages should be looked up within returned tree', t => {
out => t.matchSnapshot(out)
)
- reifyOutput({
+ reifyOutput(npm, {
actualTree: {
name: 'foo',
inventory: {
@@ -337,7 +336,7 @@ t.test('added packages should be looked up within returned tree', t => {
out => t.matchSnapshot(out)
)
- reifyOutput({
+ reifyOutput(npm, {
actualTree: {
name: 'foo',
inventory: {
diff --git a/test/lib/version.js b/test/lib/version.js
index a69953bb8..e0e07f517 100644
--- a/test/lib/version.js
+++ b/test/lib/version.js
@@ -14,7 +14,6 @@ const npm = {
}
const mocks = {
libnpmversion: noop,
- '../../lib/npm.js': npm,
'../../lib/utils/output.js': (...msg) => {
for (const m of msg)
result.push(m)
@@ -22,7 +21,8 @@ const mocks = {
'../../lib/utils/usage.js': () => 'usage instructions',
}
-const version = requireInject('../../lib/version.js', mocks)
+const Version = requireInject('../../lib/version.js', mocks)
+const version = new Version(npm)
const _processVersions = process.versions
t.afterEach(cb => {
@@ -43,7 +43,7 @@ t.test('no args', t => {
npm.prefix = prefix
Object.defineProperty(process, 'versions', { value: { node: '1.0.0' } })
- version([], err => {
+ version.exec([], err => {
if (err)
throw err
@@ -62,7 +62,7 @@ t.test('no args', t => {
})
t.test('too many args', t => {
- version(['foo', 'bar'], err => {
+ version.exec(['foo', 'bar'], err => {
t.match(
err,
'usage instructions',
@@ -74,10 +74,8 @@ t.test('too many args', t => {
})
t.test('completion', async t => {
- const { completion } = version
-
const testComp = async (argv, expect) => {
- const res = await completion({ conf: { argv: { remain: argv } } })
+ const res = await version.completion({ conf: { argv: { remain: argv } } })
t.strictSame(res, expect, argv.join(' '))
}
@@ -100,7 +98,7 @@ t.test('failure reading package.json', t => {
const prefix = t.testdir({})
npm.prefix = prefix
- version([], err => {
+ version.exec([], err => {
if (err)
throw err
@@ -123,7 +121,7 @@ t.test('--json option', t => {
npm.prefix = prefix
Object.defineProperty(process, 'versions', { value: {} })
- version([], err => {
+ version.exec([], err => {
if (err)
throw err
t.deepEqual(
@@ -136,7 +134,7 @@ t.test('--json option', t => {
})
t.test('with one arg', t => {
- const version = requireInject('../../lib/version.js', {
+ const Version = requireInject('../../lib/version.js', {
...mocks,
libnpmversion: (arg, opts) => {
t.equal(arg, 'major', 'should forward expected value')
@@ -152,8 +150,9 @@ t.test('with one arg', t => {
return '4.0.0'
},
})
+ const version = new Version(npm)
- version(['major'], err => {
+ version.exec(['major'], err => {
if (err)
throw err
t.same(result, ['v4.0.0'], 'outputs the new version prefixed by the tagVersionPrefix')
diff --git a/test/lib/view.js b/test/lib/view.js
index 9419ab7ec..1363a5b9f 100644
--- a/test/lib/view.js
+++ b/test/lib/view.js
@@ -238,98 +238,98 @@ const packument = (nv, opts) => {
t.beforeEach(cleanLogs)
t.test('should log package info', t => {
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- global: false,
- },
- },
+ const View = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
-
- const viewJson = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- json: true,
- },
+ const view = new View({
+ flatOptions: {
+ global: false,
},
+ })
+
+ const ViewJson = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
-
- const viewUnicode = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- global: false,
- unicode: true,
- },
+ const viewJson = new ViewJson({
+ flatOptions: {
+ json: true,
},
+ })
+
+ const ViewUnicode = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
+ const viewUnicode = new ViewUnicode({
+ flatOptions: {
+ global: false,
+ unicode: true,
+ },
+ })
t.test('package with license, bugs, repository and other fields', t => {
- view(['green@1.0.0'], () => {
+ view.exec(['green@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with more than 25 deps', t => {
- view(['black@1.0.0'], () => {
+ view.exec(['black@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with maintainers info as object', t => {
- view(['pink@1.0.0'], () => {
+ view.exec(['pink@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with homepage', t => {
- view(['orange@1.0.0'], () => {
+ view.exec(['orange@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with no versions', t => {
- view(['brown'], () => {
+ view.exec(['brown'], () => {
t.equals(logs, '', 'no info to display')
t.end()
})
})
t.test('package with no repo or homepage', t => {
- view(['blue@1.0.0'], () => {
+ view.exec(['blue@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with no modified time', t => {
- viewUnicode(['cyan@1.0.0'], () => {
+ viewUnicode.exec(['cyan@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with --json and semver range', t => {
- viewJson(['cyan@^1.0.0'], () => {
+ viewJson.exec(['cyan@^1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('package with --json and no versions', t => {
- viewJson(['brown'], () => {
+ viewJson.exec(['brown'], () => {
t.equals(logs, '', 'no info to display')
t.end()
})
@@ -346,28 +346,28 @@ t.test('should log info of package in current working dir', t => {
}, null, 2),
})
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- prefix: testDir,
- flatOptions: {
- defaultTag: '1.0.0',
- global: false,
- },
- },
+ const View = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
+ const view = new View({
+ prefix: testDir,
+ flatOptions: {
+ defaultTag: '1.0.0',
+ global: false,
+ },
+ })
t.test('specific version', t => {
- view(['.@1.0.0'], () => {
+ view.exec(['.@1.0.0'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('non-specific version', t => {
- view(['.'], () => {
+ view.exec(['.'], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -377,87 +377,87 @@ t.test('should log info of package in current working dir', t => {
})
t.test('should log info by field name', t => {
- const viewJson = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- json: true,
- global: false,
- },
- },
+ const ViewJson = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
-
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- global: false,
- },
+ const viewJson = new ViewJson({
+ flatOptions: {
+ json: true,
+ global: false,
},
+ })
+
+ const View = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
+ const view = new View({
+ flatOptions: {
+ global: false,
+ },
+ })
t.test('readme', t => {
- view(['yellow@1.0.0', 'readme'], () => {
+ view.exec(['yellow@1.0.0', 'readme'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('several fields', t => {
- viewJson(['yellow@1.0.0', 'name', 'version', 'foo[bar]'], () => {
+ viewJson.exec(['yellow@1.0.0', 'name', 'version', 'foo[bar]'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('several fields with several versions', t => {
- view(['yellow@1.x.x', 'author'], () => {
+ view.exec(['yellow@1.x.x', 'author'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('nested field with brackets', t => {
- viewJson(['orange@1.0.0', 'dist[shasum]'], () => {
+ viewJson.exec(['orange@1.0.0', 'dist[shasum]'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('maintainers with email', t => {
- viewJson(['yellow@1.0.0', 'maintainers', 'name'], () => {
+ viewJson.exec(['yellow@1.0.0', 'maintainers', 'name'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('maintainers with url', t => {
- viewJson(['pink@1.0.0', 'maintainers'], () => {
+ viewJson.exec(['pink@1.0.0', 'maintainers'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('unknown nested field ', t => {
- view(['yellow@1.0.0', 'dist.foobar'], () => {
+ view.exec(['yellow@1.0.0', 'dist.foobar'], () => {
t.equals(logs, '', 'no info to display')
t.end()
})
})
t.test('array field - 1 element', t => {
- view(['purple@1.0.0', 'maintainers.name'], () => {
+ view.exec(['purple@1.0.0', 'maintainers.name'], () => {
t.matchSnapshot(logs)
t.end()
})
})
t.test('array field - 2 elements', t => {
- view(['yellow@1.x.x', 'maintainers.name'], () => {
+ view.exec(['yellow@1.x.x', 'maintainers.name'], () => {
t.matchSnapshot(logs)
t.end()
})
@@ -467,14 +467,13 @@ t.test('should log info by field name', t => {
})
t.test('throw error if global mode', (t) => {
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- global: true,
- },
+ const View = requireInject('../../lib/view.js')
+ const view = new View({
+ flatOptions: {
+ global: true,
},
})
- view([], (err) => {
+ view.exec([], (err) => {
t.equals(err.message, 'Cannot use view command in global mode.')
t.end()
})
@@ -483,15 +482,14 @@ t.test('throw error if global mode', (t) => {
t.test('throw ENOENT error if package.json misisng', (t) => {
const testDir = t.testdir({})
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- prefix: testDir,
- flatOptions: {
- global: false,
- },
+ const View = requireInject('../../lib/view.js')
+ const view = new View({
+ prefix: testDir,
+ flatOptions: {
+ global: false,
},
})
- view([], (err) => {
+ view.exec([], (err) => {
t.match(err, { code: 'ENOENT' })
t.end()
})
@@ -502,15 +500,14 @@ t.test('throw EJSONPARSE error if package.json not json', (t) => {
'package.json': 'not json, nope, not even a little bit!',
})
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- prefix: testDir,
- flatOptions: {
- global: false,
- },
+ const View = requireInject('../../lib/view.js')
+ const view = new View({
+ prefix: testDir,
+ flatOptions: {
+ global: false,
},
})
- view([], (err) => {
+ view.exec([], (err) => {
t.match(err, { code: 'EJSONPARSE' })
t.end()
})
@@ -521,50 +518,49 @@ t.test('throw error if package.json has no name', (t) => {
'package.json': '{}',
})
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- prefix: testDir,
- flatOptions: {
- global: false,
- },
+ const View = requireInject('../../lib/view.js')
+ const view = new View({
+ prefix: testDir,
+ flatOptions: {
+ global: false,
},
})
- view([], (err) => {
+ view.exec([], (err) => {
t.equals(err.message, 'Invalid package.json, no "name" field')
t.end()
})
})
t.test('throws when unpublished', (t) => {
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- defaultTag: '1.0.1',
- global: false,
- },
- },
+ const View = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
- view(['red'], (err) => {
+ const view = new View({
+ flatOptions: {
+ defaultTag: '1.0.1',
+ global: false,
+ },
+ })
+ view.exec(['red'], (err) => {
t.equals(err.code, 'E404')
t.end()
})
})
t.test('completion', async t => {
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- defaultTag: '1.0.1',
- global: false,
- },
- },
+ const View = requireInject('../../lib/view.js', {
pacote: {
packument,
},
})
+ const view = new View({
+ flatOptions: {
+ defaultTag: '1.0.1',
+ global: false,
+ },
+ })
const res = await view.completion({
conf: { argv: { remain: ['npm', 'view', 'green@1.0.0'] } },
})
@@ -573,11 +569,10 @@ t.test('completion', async t => {
})
t.test('no registry completion', async t => {
- const view = requireInject('../../lib/view.js', {
- '../../lib/npm.js': {
- flatOptions: {
- defaultTag: '1.0.1',
- },
+ const View = requireInject('../../lib/view.js')
+ const view = new View({
+ flatOptions: {
+ defaultTag: '1.0.1',
},
})
const res = await view.completion({conf: { argv: { remain: ['npm', 'view'] } } })
diff --git a/test/lib/whoami.js b/test/lib/whoami.js
index d54814db3..3d9618ffa 100644
--- a/test/lib/whoami.js
+++ b/test/lib/whoami.js
@@ -3,15 +3,15 @@ const requireInject = require('require-inject')
test('whoami', (t) => {
t.plan(3)
- const whoami = requireInject('../../lib/whoami.js', {
+ const Whoami = requireInject('../../lib/whoami.js', {
'../../lib/utils/get-identity.js': () => Promise.resolve('foo'),
- '../../lib/npm.js': { flatOptions: {} },
'../../lib/utils/output.js': (output) => {
t.equal(output, 'foo', 'should output the username')
},
})
+ const whoami = new Whoami({ flatOptions: {} })
- whoami([], (err) => {
+ whoami.exec([], (err) => {
t.ifError(err, 'npm whoami')
t.ok('should successfully print username')
})
@@ -19,15 +19,15 @@ test('whoami', (t) => {
test('whoami json', (t) => {
t.plan(3)
- const whoami = requireInject('../../lib/whoami.js', {
+ const Whoami = requireInject('../../lib/whoami.js', {
'../../lib/utils/get-identity.js': () => Promise.resolve('foo'),
- '../../lib/npm.js': { flatOptions: { json: true } },
'../../lib/utils/output.js': (output) => {
t.equal(output, '"foo"', 'should output the username as json')
},
})
+ const whoami = new Whoami({ flatOptions: { json: true } })
- whoami([], (err) => {
+ whoami.exec([], (err) => {
t.ifError(err, 'npm whoami')
t.ok('should successfully print username as json')
})