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:
-rw-r--r--lib/utils/config/definition.js157
-rw-r--r--lib/utils/config/definitions.js2014
-rw-r--r--lib/utils/config/describe-all.js16
-rw-r--r--lib/utils/config/flatten.js32
-rw-r--r--lib/utils/config/index.js52
-rw-r--r--tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js264
-rw-r--r--tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js153
-rw-r--r--tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js1349
-rw-r--r--tap-snapshots/test-lib-utils-config-index.js-TAP.test.js127
-rw-r--r--test/lib/utils/config/definition.js150
-rw-r--r--test/lib/utils/config/definitions.js697
-rw-r--r--test/lib/utils/config/describe-all.js6
-rw-r--r--test/lib/utils/config/flatten.js34
-rw-r--r--test/lib/utils/config/index.js24
14 files changed, 5075 insertions, 0 deletions
diff --git a/lib/utils/config/definition.js b/lib/utils/config/definition.js
new file mode 100644
index 000000000..b6323a512
--- /dev/null
+++ b/lib/utils/config/definition.js
@@ -0,0 +1,157 @@
+// class that describes a config key we know about
+// this keeps us from defining a config key and not
+// providing a default, description, etc.
+//
+// TODO: some kind of categorization system, so we can
+// say "these are for registry access", "these are for
+// version resolution" etc.
+
+const required = [
+ 'type',
+ 'description',
+ 'default',
+ 'key',
+]
+
+const allowed = [
+ 'default',
+ 'type',
+ 'description',
+ 'flatten',
+ 'short',
+ 'typeDescription',
+ 'defaultDescription',
+ 'deprecated',
+ 'key',
+]
+
+const {
+ typeDefs: {
+ semver: { type: semver },
+ Umask: { type: Umask },
+ url: { type: url },
+ path: { type: path },
+ },
+} = require('@npmcli/config')
+
+class Definition {
+ constructor (key, def) {
+ this.key = key
+ Object.assign(this, def)
+ this.validate()
+ if (!this.defaultDescription)
+ this.defaultDescription = describeValue(this.default)
+ if (!this.typeDescription)
+ this.typeDescription = describeType(this.type)
+ }
+
+ validate () {
+ for (const req of required) {
+ if (!Object.prototype.hasOwnProperty.call(this, req))
+ throw new Error(`config lacks ${req}: ${this.key}`)
+ }
+ if (!this.key)
+ throw new Error(`config lacks key: ${this.key}`)
+ for (const field of Object.keys(this)) {
+ if (!allowed.includes(field))
+ throw new Error(`config defines unknown field ${field}: ${this.key}`)
+ }
+ }
+
+ // a textual description of this config, suitable for help output
+ describe () {
+ const description = unindent(this.description)
+ const deprecated = !this.deprecated ? ''
+ : `* DEPRECATED: ${unindent(this.deprecated)}\n`
+ return wrapAll(`#### \`${this.key}\`
+
+* Default: ${unindent(this.defaultDescription)}
+* Type: ${unindent(this.typeDescription)}
+${deprecated}
+${description}
+`)
+ }
+}
+
+const describeType = type => {
+ if (Array.isArray(type)) {
+ const descriptions = type
+ .filter(t => t !== Array)
+ .map(t => describeType(t))
+
+ // [a] => "a"
+ // [a, b] => "a or b"
+ // [a, b, c] => "a, b, or c"
+ // [a, Array] => "a (can be set multiple times)"
+ // [a, Array, b] => "a or b (can be set multiple times)"
+ const last = descriptions.length > 1 ? [descriptions.pop()] : []
+ const oxford = descriptions.length > 1 ? ', or ' : ' or '
+ const words = [descriptions.join(', ')].concat(last).join(oxford)
+ const multiple = type.includes(Array) ? ' (can be set multiple times)'
+ : ''
+ return `${words}${multiple}`
+ }
+
+ // Note: these are not quite the same as the description printed
+ // when validation fails. In that case, we want to give the user
+ // a bit more information to help them figure out what's wrong.
+ switch (type) {
+ case String:
+ return 'String'
+ case Number:
+ return 'Number'
+ case Umask:
+ return 'Octal numeric string in range 0000..0777 (0..511)'
+ case Boolean:
+ return 'Boolean'
+ case Date:
+ return 'Date'
+ case path:
+ return 'Path'
+ case semver:
+ return 'SemVer string'
+ case url:
+ return 'URL'
+ default:
+ return describeValue(type)
+ }
+}
+
+// if it's a string, quote it. otherwise, just cast to string.
+const describeValue = val =>
+ typeof val === 'string' ? JSON.stringify(val) : String(val)
+
+const unindent = s => {
+ // get the first \n followed by a bunch of spaces, and pluck off
+ // that many spaces from the start of every line.
+ const match = s.match(/\n +/)
+ return !match ? s.trim() : s.split(match[0]).join('\n').trim()
+}
+
+const wrap = (s) => {
+ const cols = Math.min(Math.max(20, process.stdout.columns) || 80, 80) - 5
+ return unindent(s).split(/[ \n]+/).reduce((left, right) => {
+ const last = left.split('\n').pop()
+ const join = last.length && last.length + right.length > cols ? '\n' : ' '
+ return left + join + right
+ })
+}
+
+const wrapAll = s => {
+ let inCodeBlock = false
+ return s.split('\n\n').map(block => {
+ if (inCodeBlock || block.startsWith('```')) {
+ inCodeBlock = !block.endsWith('```')
+ return block
+ }
+
+ if (block.charAt(0) === '*') {
+ return '* ' + block.substr(1).trim().split('\n* ').map(li => {
+ return wrap(li).replace(/\n/g, '\n ')
+ }).join('\n* ')
+ } else
+ return wrap(block)
+ }).join('\n\n')
+}
+
+module.exports = Definition
diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js
new file mode 100644
index 000000000..1ad3a166b
--- /dev/null
+++ b/lib/utils/config/definitions.js
@@ -0,0 +1,2014 @@
+const definitions = {}
+module.exports = definitions
+
+const Definition = require('./definition.js')
+
+const { version: npmVersion } = require('../../../package.json')
+const ciDetect = require('@npmcli/ci-detect')
+const ciName = ciDetect()
+const querystring = require('querystring')
+const isWindows = require('../is-windows.js')
+const { join } = require('path')
+
+// used by cafile flattening to flatOptions.ca
+const fs = require('fs')
+const maybeReadFile = file => {
+ try {
+ return fs.readFileSync(file, 'utf8')
+ } catch (er) {
+ if (er.code !== 'ENOENT')
+ throw er
+ return null
+ }
+}
+
+const editor = process.env.EDITOR ||
+ process.env.VISUAL ||
+ (isWindows ? 'notepad.exe' : 'vi')
+
+const shell = isWindows ? process.env.ComSpec || 'cmd'
+ : process.env.SHELL || 'sh'
+
+const { tmpdir, networkInterfaces } = require('os')
+const getLocalAddresses = () => {
+ try {
+ return Object.values(networkInterfaces()).map(
+ int => int.map(({ address }) => address)
+ ).reduce((set, addrs) => set.concat(addrs), [null])
+ } catch (e) {
+ return [null]
+ }
+}
+
+const unicode = /UTF-?8$/i.test(
+ process.env.LC_ALL ||
+ process.env.LC_CTYPE ||
+ process.env.LANG
+)
+
+// use LOCALAPPDATA on Windows, if set
+// https://github.com/npm/cli/pull/899
+const cacheRoot = (isWindows && process.env.LOCALAPPDATA) || '~'
+const cacheExtra = isWindows ? 'npm-cache' : '.npm'
+const cache = `${cacheRoot}/${cacheExtra}`
+
+const Config = require('@npmcli/config')
+
+// TODO: refactor these type definitions so that they are less
+// weird to pull out of the config module.
+// TODO: use better type definition/validation API, nopt's is so weird.
+const {
+ typeDefs: {
+ semver: { type: semver },
+ Umask: { type: Umask },
+ url: { type: url },
+ path: { type: path },
+ },
+} = Config
+
+const define = (key, def) => {
+ /* istanbul ignore if - this should never happen, prevents mistakes below */
+ if (definitions[key])
+ throw new Error(`defining key more than once: ${key}`)
+ definitions[key] = new Definition(key, def)
+}
+
+// basic flattening function, just copy it over camelCase
+const flatten = (key, obj, flatOptions) => {
+ const camel = key.replace(/-([a-z])/g, (_0, _1) => _1.toUpperCase())
+ flatOptions[camel] = obj[key]
+}
+
+// TODO:
+// Instead of having each definition provide a flatten method,
+// provide the (?list of?) flat option field(s?) that it impacts.
+// When that config is set, we mark the relevant flatOption fields
+// dirty. Then, a getter for that field defines how we actually
+// set it.
+//
+// So, `save-dev`, `save-optional`, `save-prod`, et al would indicate
+// that they affect the `saveType` flat option. Then the config.flat
+// object has a `get saveType () { ... }` that looks at the "real"
+// config settings from files etc and returns the appropriate value.
+//
+// Getters will also (maybe?) give us a hook to audit flat option
+// usage, so we can document and group these more appropriately.
+//
+// This will be a problem with cases where we currently do:
+// const opts = { ...npm.flatOptions, foo: 'bar' }, but we can maybe
+// instead do `npm.config.set('foo', 'bar')` prior to passing the
+// config object down where it needs to go.
+//
+// This way, when we go hunting for "where does saveType come from anyway!?"
+// while fixing some Arborist bug, we won't have to hunt through too
+// many places.
+
+// Define all config keys we know about
+
+define('_auth', {
+ default: null,
+ type: [null, String],
+ description: `
+ A basic-auth string to use when authenticating against the npm registry.
+
+ Warning: This should generally not be set via a command-line option. It
+ is safer to use a registry-provided authentication bearer token stored in
+ the ~/.npmrc file by running \`npm login\`.
+ `,
+})
+
+define('access', {
+ default: null,
+ defaultDescription: `
+ 'restricted' for scoped packages, 'public' for unscoped packages
+ `,
+ type: [null, 'restricted', 'public'],
+ description: `
+ When publishing scoped packages, the access level defaults to
+ \`restricted\`. If you want your scoped package to be publicly viewable
+ (and installable) set \`--access=public\`. The only valid values for
+ \`access\` are \`public\` and \`restricted\`. Unscoped packages _always_
+ have an access level of \`public\`.
+ `,
+ flatten,
+})
+
+define('all', {
+ default: false,
+ type: Boolean,
+ short: 'a',
+ description: `
+ When running \`npm outdated\` and \`npm ls\`, setting \`--all\` will show
+ all outdated or installed packages, rather than only those directly
+ depended upon by the current project.
+ `,
+ flatten,
+})
+
+define('allow-same-version', {
+ default: false,
+ type: Boolean,
+ description: `
+ Prevents throwing an error when \`npm version\` is used to set the new
+ version to the same value as the current version.
+ `,
+ flatten,
+})
+
+define('also', {
+ default: null,
+ type: [null, 'dev', 'development'],
+ description: `
+ When set to \`dev\` or \`development\`, this is an alias for
+ \`--include=dev\`.
+ `,
+ deprecated: 'Please use --include=dev instead.',
+ flatten (key, obj, flatOptions) {
+ if (!/^dev(elopment)?$/.test(obj.also))
+ return
+
+ // add to include, and call the omit flattener
+ obj.include = obj.include || []
+ obj.include.push('dev')
+ definitions.omit.flatten('omit', obj, flatOptions)
+ },
+})
+
+define('always-auth', {
+ default: false,
+ type: Boolean,
+ description: `
+ Force npm to always require authentication when accessing the registry,
+ even for \`GET\` requests.
+ `,
+ flatten,
+})
+
+define('audit', {
+ default: true,
+ type: Boolean,
+ description: `
+ When "true" submit audit reports alongside \`npm install\` runs to the
+ default registry and all registries configured for scopes. See the
+ documentation for [\`npm audit\`](/commands/npm-audit) for details on
+ what is submitted.
+ `,
+ flatten,
+})
+
+define('audit-level', {
+ default: null,
+ type: ['low', 'moderate', 'high', 'critical', 'none', null],
+ description: `
+ The minimum level of vulnerability for \`npm audit\` to exit with
+ a non-zero exit code.
+ `,
+ flatten,
+})
+
+define('auth-type', {
+ default: 'legacy',
+ type: ['legacy', 'sso', 'saml', 'oauth'],
+ deprecated: `
+ This method of SSO/SAML/OAuth is deprecated and will be removed in
+ a future version of npm in favor of web-based login.
+ `,
+ description: `
+ What authentication strategy to use with \`adduser\`/\`login\`.
+ `,
+ flatten,
+})
+
+define('before', {
+ default: null,
+ type: [null, Date],
+ description: `
+ If passed to \`npm install\`, will rebuild the npm tree such that only
+ versions that were available **on or before** the \`--before\` time get
+ installed. If there's no versions available for the current set of
+ direct dependencies, the command will error.
+
+ If the requested version is a \`dist-tag\` and the given tag does not
+ pass the \`--before\` filter, the most recent version less than or equal
+ to that tag will be used. For example, \`foo@latest\` might install
+ \`foo@1.2\` even though \`latest\` is \`2.0\`.
+ `,
+ flatten,
+})
+
+define('bin-links', {
+ default: true,
+ type: Boolean,
+ description: `
+ Tells npm to create symlinks (or \`.cmd\` shims on Windows) for package
+ executables.
+
+ Set to false to have it not do this. This can be used to work around the
+ fact that some file systems don't support symlinks, even on ostensibly
+ Unix systems.
+ `,
+ flatten,
+})
+
+define('browser', {
+ default: null,
+ defaultDescription: `
+ OS X: \`"open"\`, Windows: \`"start"\`, Others: \`"xdg-open"\`
+ `,
+ type: [null, Boolean, String],
+ description: `
+ The browser that is called by npm commands to open websites.
+
+ Set to \`false\` to suppress browser behavior and instead print urls to
+ terminal.
+
+ Set to \`true\` to use default system URL opener.
+ `,
+ flatten,
+})
+
+define('ca', {
+ default: null,
+ type: [null, String, Array],
+ description: `
+ The Certificate Authority signing certificate that is trusted for SSL
+ connections to the registry. Values should be in PEM format (Windows
+ calls it "Base-64 encoded X.509 (.CER)") with newlines replaced by the
+ string "\\n". For example:
+
+ \`\`\`ini
+ ca="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
+ \`\`\`
+
+ Set to \`null\` to only allow "known" registrars, or to a specific CA
+ cert to trust only that specific signing authority.
+
+ Multiple CAs can be trusted by specifying an array of certificates:
+
+ \`\`\`ini
+ ca[]="..."
+ ca[]="..."
+ \`\`\`
+
+ See also the \`strict-ssl\` config.
+ `,
+ flatten,
+})
+
+define('cache', {
+ default: cache,
+ defaultDescription: `
+ Windows: \`%LocalAppData%\\npm-cache\`, Posix: \`~/.npm\`
+ `,
+ type: path,
+ description: `
+ The location of npm's cache directory. See [\`npm
+ cache\`](/commands/npm-cache)
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.cache = join(obj.cache, '_cacache')
+ },
+})
+
+define('cache-max', {
+ default: Infinity,
+ type: Number,
+ description: `
+ \`--cache-max=0\` is an alias for \`--prefer-online\`
+ `,
+ deprecated: `
+ This option has been deprecated in favor of \`--prefer-online\`
+ `,
+ flatten (key, obj, flatOptions) {
+ if (obj[key] <= 0)
+ flatOptions.preferOnline = true
+ },
+})
+
+define('cache-min', {
+ default: 0,
+ type: Number,
+ description: `
+ \`--cache-min=9999 (or bigger)\` is an alias for \`--prefer-offline\`.
+ `,
+ deprecated: `
+ This option has been deprecated in favor of \`--prefer-offline\`.
+ `,
+ flatten (key, obj, flatOptions) {
+ if (obj[key] >= 9999)
+ flatOptions.preferOffline = true
+ },
+})
+
+define('cafile', {
+ default: null,
+ type: path,
+ description: `
+ A path to a file containing one or multiple Certificate Authority signing
+ certificates. Similar to the \`ca\` setting, but allows for multiple
+ CA's, as well as for the CA information to be stored in a file on disk.
+ `,
+ flatten (key, obj, flatOptions) {
+ // always set to null in defaults
+ if (!obj.cafile)
+ return
+
+ const raw = maybeReadFile(obj.cafile)
+ if (!raw)
+ return
+
+ const delim = '-----END CERTIFICATE-----'
+ flatOptions.ca = raw.replace(/\r\n/g, '\n').split(delim)
+ .filter(section => section.trim())
+ .map(section => section.trimLeft() + delim)
+ },
+})
+
+define('call', {
+ default: '',
+ type: String,
+ short: 'c',
+ description: `
+ Optional companion option for \`npm exec\`, \`npx\` that allows for
+ specifying a custom command to be run along with the installed packages.
+
+ \`\`\`bash
+ npm exec --package yo --package generator-node --call "yo node"
+ \`\`\`
+ `,
+ flatten,
+})
+
+define('cert', {
+ default: null,
+ type: [null, String],
+ description: `
+ A client certificate to pass when accessing the registry. Values should
+ be in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with
+ newlines replaced by the string "\\n". For example:
+
+ \`\`\`ini
+ cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
+ \`\`\`
+
+ It is _not_ the path to a certificate file (and there is no "certfile"
+ option).
+ `,
+ flatten,
+})
+
+define('ci-name', {
+ default: ciName || null,
+ defaultDescription: `
+ The name of the current CI system, or \`null\` when not on a known CI
+ platform.
+ `,
+ type: [null, String],
+ description: `
+ The name of a continuous integration system. If not set explicitly, npm
+ will detect the current CI environment using the
+ [\`@npmcli/ci-detect\`](http://npm.im/@npmcli/ci-detect) module.
+ `,
+ flatten,
+})
+
+define('cidr', {
+ default: null,
+ type: [null, String, Array],
+ description: `
+ This is a list of CIDR address to be used when configuring limited access
+ tokens with the \`npm token create\` command.
+ `,
+ flatten,
+})
+
+define('color', {
+ default: !process.env.NO_COLOR || process.env.NO_COLOR === '0',
+ defaultDescription: `
+ true unless the NO_COLOR environ is set to something other than '0'
+ `,
+ type: ['always', Boolean],
+ description: `
+ If false, never shows colors. If \`"always"\` then always shows colors.
+ If true, then only prints color codes for tty file descriptors.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.color = !obj.color ? false
+ : obj.color === 'always' ? true
+ : process.stdout.isTTY
+ },
+})
+
+define('commit-hooks', {
+ default: true,
+ type: Boolean,
+ description: `
+ Run git commit hooks when using the \`npm version\` command.
+ `,
+ flatten,
+})
+
+define('depth', {
+ default: null,
+ defaultDescription: `
+ \`Infinity\` if \`--all\` is set, otherwise \`1\`
+ `,
+ type: [null, Number],
+ description: `
+ The depth to go when recursing packages for \`npm ls\`.
+
+ If not set, \`npm ls\` will show only the immediate dependencies of the
+ root project. If \`--all\` is set, then npm will show all dependencies
+ by default.
+ `,
+ flatten,
+})
+
+define('description', {
+ default: true,
+ type: Boolean,
+ description: `
+ Show the description in \`npm search\`
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.search = flatOptions.search || { limit: 20 }
+ flatOptions.search[key] = obj[key]
+ },
+})
+
+define('diff', {
+ default: [],
+ type: [String, Array],
+ description: `
+ Define arguments to compare in \`npm diff\`.
+ `,
+ flatten,
+})
+
+define('diff-ignore-all-space', {
+ default: false,
+ type: Boolean,
+ description: `
+ Ignore whitespace when comparing lines in \`npm diff\`.
+ `,
+ flatten,
+})
+
+define('diff-name-only', {
+ default: false,
+ type: Boolean,
+ description: `
+ Prints only filenames when using \`npm diff\`.
+ `,
+ flatten,
+})
+
+define('diff-no-prefix', {
+ default: false,
+ type: Boolean,
+ description: `
+ Do not show any source or destination prefix in \`npm diff\` output.
+
+ Note: this causes \`npm diff\` to ignore the \`--diff-src-prefix\` and
+ \`--diff-dst-prefix\` configs.
+ `,
+ flatten,
+})
+
+define('diff-dst-prefix', {
+ default: 'b/',
+ type: String,
+ description: `
+ Destination prefix to be used in \`npm diff\` output.
+ `,
+ flatten,
+})
+
+define('diff-src-prefix', {
+ default: 'a/',
+ type: String,
+ description: `
+ Source prefix to be used in \`npm diff\` output.
+ `,
+ flatten,
+})
+
+define('diff-text', {
+ default: false,
+ type: Boolean,
+ description: `
+ Treat all files as text in \`npm diff\`.
+ `,
+ flatten,
+})
+
+define('diff-unified', {
+ default: 3,
+ type: Number,
+ description: `
+ The number of lines of context to print in \`npm diff\`.
+ `,
+ flatten,
+})
+
+define('dry-run', {
+ default: false,
+ type: Boolean,
+ description: `
+ Indicates that you don't want npm to make any changes and that it should
+ only report what it would have done. This can be passed into any of the
+ commands that modify your local installation, eg, \`install\`,
+ \`update\`, \`dedupe\`, \`uninstall\`, as well as \`pack\` and
+ \`publish\`.
+
+ Note: This is NOT honored by other network related commands, eg
+ \`dist-tags\`, \`owner\`, etc.
+ `,
+ flatten,
+})
+
+define('editor', {
+ default: editor,
+ defaultDescription: `
+ The EDITOR or VISUAL environment variables, or 'notepad.exe' on Windows,
+ or 'vim' on Unix systems
+ `,
+ type: String,
+ description: `
+ The command to run for \`npm edit\` and \`npm config edit\`.
+ `,
+ flatten,
+})
+
+define('engine-strict', {
+ default: false,
+ type: Boolean,
+ description: `
+ If set to true, then npm will stubbornly refuse to install (or even
+ consider installing) any package that claims to not be compatible with
+ the current Node.js version.
+
+ This can be overridden by setting the \`--force\` flag.
+ `,
+ flatten,
+})
+
+define('fetch-retries', {
+ default: 2,
+ type: Number,
+ description: `
+ The "retries" config for the \`retry\` module to use when fetching
+ packages from the registry.
+
+ npm will retry idempotent read requests to the registry in the case
+ of network failures or 5xx HTTP errors.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.retry = flatOptions.retry || {}
+ flatOptions.retry.retries = obj[key]
+ },
+})
+
+define('fetch-retry-factor', {
+ default: 10,
+ type: Number,
+ description: `
+ The "factor" config for the \`retry\` module to use when fetching
+ packages.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.retry = flatOptions.retry || {}
+ flatOptions.retry.factor = obj[key]
+ },
+})
+
+define('fetch-retry-maxtimeout', {
+ default: 60000,
+ defaultDescription: '60000 (1 minute)',
+ type: Number,
+ description: `
+ The "maxTimeout" config for the \`retry\` module to use when fetching
+ packages.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.retry = flatOptions.retry || {}
+ flatOptions.retry.maxTimeout = obj[key]
+ },
+})
+
+define('fetch-retry-mintimeout', {
+ default: 10000,
+ defaultDescription: '10000 (10 seconds)',
+ type: Number,
+ description: `
+ The "minTimeout" config for the \`retry\` module to use when fetching
+ packages.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.retry = flatOptions.retry || {}
+ flatOptions.retry.minTimeout = obj[key]
+ },
+})
+
+define('fetch-timeout', {
+ default: 5 * 60 * 1000,
+ defaultDescription: `${5 * 60 * 1000} (5 minutes)`,
+ type: Number,
+ description: `
+ The maximum amount of time to wait for HTTP requests to complete.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.timeout = obj[key]
+ },
+})
+
+define('force', {
+ default: false,
+ type: Boolean,
+ short: 'f',
+ description: `
+ Removes various protections against unfortunate side effects, common
+ mistakes, unnecessary performance degradation, and malicious input.
+
+ * Allow clobbering non-npm files in global installs.
+ * Allow the \`npm version\` command to work on an unclean git repository.
+ * Allow deleting the cache folder with \`npm cache clean\`.
+ * Allow installing packages that have an \`engines\` declaration
+ requiring a different version of npm.
+ * Allow installing packages that have an \`engines\` declaration
+ requiring a different version of \`node\`, even if \`--engine-strict\`
+ is enabled.
+ * Allow \`npm audit fix\` to install modules outside your stated
+ dependency range (including SemVer-major changes).
+ * Allow unpublishing all versions of a published package.
+ * Allow conflicting peerDependencies to be installed in the root project.
+
+ If you don't have a clear idea of what you want to do, it is strongly
+ recommended that you do not use this option!
+ `,
+ flatten,
+})
+
+define('foreground-scripts', {
+ default: false,
+ type: Boolean,
+ description: `
+ Run all build scripts (ie, \`preinstall\`, \`install\`, and
+ \`postinstall\`) scripts for installed packages in the foreground
+ process, sharing standard input, output, and error with the main npm
+ process.
+
+ Note that this will generally make installs run slower, and be much
+ noisier, but can be useful for debugging.
+ `,
+ flatten,
+})
+
+define('format-package-lock', {
+ default: true,
+ type: Boolean,
+ description: `
+ Format \`package-lock.json\` or \`npm-shrinkwrap.json\` as a human
+ readable file.
+ `,
+ flatten,
+})
+
+define('fund', {
+ default: true,
+ type: Boolean,
+ description: `
+ When "true" displays the message at the end of each \`npm install\`
+ acknowledging the number of dependencies looking for funding.
+ See [\`npm fund\`](/commands/npm-fund) for details.
+ `,
+ flatten,
+})
+
+define('git', {
+ default: 'git',
+ type: String,
+ description: `
+ The command to use for git commands. If git is installed on the
+ computer, but is not in the \`PATH\`, then set this to the full path to
+ the git binary.
+ `,
+ flatten,
+})
+
+define('git-tag-version', {
+ default: true,
+ type: Boolean,
+ description: `
+ Tag the commit when using the \`npm version\` command.
+ `,
+ flatten,
+})
+
+define('global', {
+ default: false,
+ type: Boolean,
+ short: 'g',
+ description: `
+ Operates in "global" mode, so that packages are installed into the
+ \`prefix\` folder instead of the current working directory. See
+ [folders](/configuring-npm/folders) for more on the differences in
+ behavior.
+
+ * packages are installed into the \`{prefix}/lib/node_modules\` folder,
+ instead of the current working directory.
+ * bin files are linked to \`{prefix}/bin\`
+ * man pages are linked to \`{prefix}/share/man\`
+ `,
+ flatten,
+})
+
+define('global-style', {
+ default: false,
+ type: Boolean,
+ description: `
+ Causes npm to install the package into your local \`node_modules\` folder
+ with the same layout it uses with the global \`node_modules\` folder.
+ Only your direct dependencies will show in \`node_modules\` and
+ everything they depend on will be flattened in their \`node_modules\`
+ folders. This obviously will eliminate some deduping. If used with
+ \`legacy-bundling\`, \`legacy-bundling\` will be preferred.
+ `,
+ flatten,
+})
+
+// the globalconfig has its default defined outside of this module
+define('globalconfig', {
+ type: path,
+ default: '',
+ defaultDescription: `
+ The global --prefix setting plus 'etc/npmrc'. For example,
+ '/usr/local/etc/npmrc'
+ `,
+ description: `
+ The config file to read for global config options.
+ `,
+ flatten,
+})
+
+define('heading', {
+ default: 'npm',
+ type: String,
+ description: `
+ The string that starts all the debugging log output.
+ `,
+ flatten,
+})
+
+define('https-proxy', {
+ default: null,
+ type: [null, url],
+ description: `
+ A proxy to use for outgoing https requests. If the \`HTTPS_PROXY\` or
+ \`https_proxy\` or \`HTTP_PROXY\` or \`http_proxy\` environment variables
+ are set, proxy settings will be honored by the underlying
+ \`make-fetch-happen\` library.
+ `,
+ flatten,
+})
+
+define('if-present', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, npm will not exit with an error code when \`run-script\` is
+ invoked for a script that isn't defined in the \`scripts\` section of
+ \`package.json\`. This option can be used when it's desirable to
+ optionally run a script when it's present and fail if the script fails.
+ This is useful, for example, when running scripts that may only apply for
+ some builds in an otherwise generic CI setup.
+ `,
+ flatten,
+})
+
+define('ignore-scripts', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, npm does not run scripts specified in package.json files.
+ `,
+ flatten,
+})
+
+define('include', {
+ default: [],
+ type: [Array, 'prod', 'dev', 'optional', 'peer'],
+ description: `
+ Option that allows for defining which types of dependencies to install.
+
+ This is the inverse of \`--omit=<type>\`.
+
+ Dependency types specified in \`--include\` will not be omitted,
+ regardless of the order in which omit/include are specified on the
+ command-line.
+ `,
+ flatten (key, obj, flatOptions) {
+ // just call the omit flattener, it reads from obj.include
+ definitions.omit.flatten('omit', obj, flatOptions)
+ },
+})
+
+define('include-staged', {
+ default: false,
+ type: Boolean,
+ description: `
+ Allow installing "staged" published packages, as defined by [npm RFC PR
+ #92](https://github.com/npm/rfcs/pull/92).
+
+ This is experimental, and not implemented by the npm public registry.
+ `,
+ flatten,
+})
+
+define('init-author-email', {
+ default: '',
+ type: String,
+ description: `
+ The value \`npm init\` should use by default for the package author's
+ email.
+ `,
+})
+
+define('init-author-name', {
+ default: '',
+ type: String,
+ description: `
+ The value \`npm init\` should use by default for the package author's name.
+ `,
+})
+
+define('init-author-url', {
+ default: '',
+ type: ['', url],
+ description: `
+ The value \`npm init\` should use by default for the package author's homepage.
+ `,
+})
+
+define('init-license', {
+ default: 'ISC',
+ type: String,
+ description: `
+ The value \`npm init\` should use by default for the package license.
+ `,
+})
+
+define('init-module', {
+ default: '~/.npm-init.js',
+ type: path,
+ description: `
+ A module that will be loaded by the \`npm init\` command. See the
+ documentation for the
+ [init-package-json](https://github.com/npm/init-package-json) module for
+ more information, or [npm init](/commands/npm-init).
+ `,
+})
+
+define('init-version', {
+ default: '1.0.0',
+ type: semver,
+ description: `
+ The value that \`npm init\` should use by default for the package
+ version number, if not already set in package.json.
+ `,
+})
+
+// these "aliases" are historically supported in .npmrc files, unfortunately
+// They should be removed in a future npm version.
+define('init.author.email', {
+ default: '',
+ type: String,
+ deprecated: `
+ Use \`--init-author-email\` instead.`,
+ description: `
+ Alias for \`--init-author-email\`
+ `,
+})
+
+define('init.author.name', {
+ default: '',
+ type: String,
+ deprecated: `
+ Use \`--init-author-name\` instead.
+ `,
+ description: `
+ Alias for \`--init-author-name\`
+ `,
+})
+
+define('init.author.url', {
+ default: '',
+ type: ['', url],
+ deprecated: `
+ Use \`--init-author-url\` instead.
+ `,
+ description: `
+ Alias for \`--init-author-url\`
+ `,
+})
+
+define('init.license', {
+ default: 'ISC',
+ type: String,
+ deprecated: `
+ Use \`--init-license\` instead.
+ `,
+ description: `
+ Alias for \`--init-license\`
+ `,
+})
+
+define('init.module', {
+ default: '~/.npm-init.js',
+ type: path,
+ deprecated: `
+ Use \`--init-module\` instead.
+ `,
+ description: `
+ Alias for \`--init-module\`
+ `,
+})
+
+define('init.version', {
+ default: '1.0.0',
+ type: semver,
+ deprecated: `
+ Use \`--init-version\` instead.
+ `,
+ description: `
+ Alias for \`--init-version\`
+ `,
+})
+
+define('json', {
+ default: false,
+ type: Boolean,
+ description: `
+ Whether or not to output JSON data, rather than the normal output.
+
+ This feature is currently experimental, and the output data structures
+ for many commands is either not implemented in JSON yet, or subject to
+ change. Only the output from \`npm ls --json\` and \`npm search --json\`
+ are currently valid.
+ `,
+ flatten,
+})
+
+define('key', {
+ default: null,
+ type: [null, String],
+ description: `
+ A client key to pass when accessing the registry. Values should be in
+ PEM format with newlines replaced by the string "\\n". For example:
+
+ \`\`\`ini
+ key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----"
+ \`\`\`
+
+ It is _not_ the path to a key file (and there is no "keyfile" option).
+ `,
+ flatten,
+})
+
+define('legacy-bundling', {
+ default: false,
+ type: Boolean,
+ description: `
+ Causes npm to install the package such that versions of npm prior to 1.4,
+ such as the one included with node 0.8, can install the package. This
+ eliminates all automatic deduping. If used with \`global-style\` this
+ option will be preferred.
+ `,
+ flatten,
+})
+
+define('legacy-peer-deps', {
+ default: false,
+ type: Boolean,
+ description: `
+ Causes npm to completely ignore \`peerDependencies\` when building a
+ package tree, as in npm versions 3 through 6.
+
+ If a package cannot be installed because of overly strict
+ \`peerDependencies\` that collide, it provides a way to move forward
+ resolving the situation.
+
+ This differs from \`--omit=peer\`, in that \`--omit=peer\` will avoid
+ unpacking \`peerDependencies\` on disk, but will still design a tree such
+ that \`peerDependencies\` _could_ be unpacked in a correct place.
+
+ Use of \`legacy-peer-deps\` is not recommended, as it will not enforce
+ the \`peerDependencies\` contract that meta-dependencies may rely on.
+ `,
+ flatten,
+})
+
+define('link', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, then local installs will link if there is a suitable globally
+ installed package.
+
+ Note that this means that local installs can cause things to be installed
+ into the global space at the same time. The link is only done if one of
+ the two conditions are met:
+
+ * The package is not already installed globally, or
+ * the globally installed version is identical to the version that is
+ being installed locally.
+ `,
+})
+
+define('local-address', {
+ default: null,
+ type: getLocalAddresses(),
+ typeDescription: 'IP Address',
+ description: `
+ The IP address of the local interface to use when making connections to
+ the npm registry. Must be IPv4 in versions of Node prior to 0.12.
+ `,
+ flatten,
+})
+
+define('loglevel', {
+ default: 'notice',
+ type: [
+ 'silent',
+ 'error',
+ 'warn',
+ 'notice',
+ 'http',
+ 'timing',
+ 'info',
+ 'verbose',
+ 'silly',
+ ],
+ description: `
+ What level of logs to report. On failure, *all* logs are written to
+ \`npm-debug.log\` in the current working directory.
+
+ Any logs of a higher level than the setting are shown. The default is
+ "notice".
+ `,
+})
+
+define('logs-max', {
+ default: 10,
+ type: Number,
+ description: `
+ The maximum number of log files to store.
+ `,
+})
+
+define('long', {
+ default: false,
+ type: Boolean,
+ short: 'l',
+ description: `
+ Show extended information in \`npm ls\` and \`npm search\`.
+ `,
+})
+
+define('maxsockets', {
+ default: Infinity,
+ type: Number,
+ description: `
+ The maximum number of connections to use per origin (protocol/host/port
+ combination).
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.maxSockets = obj[key]
+ },
+})
+
+define('message', {
+ default: '%s',
+ type: String,
+ short: 'm',
+ description: `
+ Commit message which is used by \`npm version\` when creating version commit.
+
+ Any "%s" in the message will be replaced with the version number.
+ `,
+ flatten,
+})
+
+define('node-options', {
+ default: null,
+ type: [null, String],
+ description: `
+ Options to pass through to Node.js via the \`NODE_OPTIONS\` environment
+ variable. This does not impact how npm itself is executed but it does
+ impact how lifecycle scripts are called.
+ `,
+})
+
+define('node-version', {
+ default: process.version,
+ defaultDescription: 'Node.js `process.version` value',
+ type: semver,
+ description: `
+ The node version to use when checking a package's \`engines\` setting.
+ `,
+ flatten,
+})
+
+define('noproxy', {
+ default: '',
+ defaultDescription: `
+ The value of the NO_PROXY environment variable
+ `,
+ type: [String, Array],
+ description: `
+ Domain extensions that should bypass any proxies.
+
+ Also accepts a comma-delimited string.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.noProxy = obj[key].join(',')
+ },
+})
+
+define('npm-version', {
+ default: npmVersion,
+ defaultDescription: 'Output of `npm --version`',
+ type: semver,
+ description: `
+ The npm version to use when checking a package's \`engines\` setting.
+ `,
+ flatten,
+})
+
+define('offline', {
+ default: false,
+ type: Boolean,
+ description: `
+ Force offline mode: no network requests will be done during install. To allow
+ the CLI to fill in missing cache data, see \`--prefer-offline\`.
+ `,
+ flatten,
+})
+
+define('omit', {
+ default: process.env.NODE_ENV === 'production' ? ['dev'] : [],
+ defaultDescription: `
+ 'dev' if the NODE_ENV environment variable is set to 'production',
+ otherwise empty.
+ `,
+ type: [Array, 'dev', 'optional', 'peer'],
+ description: `
+ Dependency types to omit from the installation tree on disk.
+
+ Note that these dependencies _are_ still resolved and added to the
+ \`package-lock.json\` or \`npm-shrinkwrap.json\` file. They are just
+ not physically installed on disk.
+
+ If a package type appears in both the \`--include\` and \`--omit\`
+ lists, then it will be included.
+
+ If the resulting omit list includes \`'dev'\`, then the \`NODE_ENV\`
+ environment variable will be set to \`'production'\` for all lifecycle
+ scripts.
+ `,
+ flatten (key, obj, flatOptions) {
+ const include = obj.include || []
+ const omit = flatOptions.omit || []
+ flatOptions.omit = omit.concat(obj[key])
+ .filter(type => type && !include.includes(type))
+ },
+})
+
+define('only', {
+ default: null,
+ type: [null, 'prod', 'production'],
+ deprecated: `
+ Use \`--omit=dev\` to omit dev dependencies from the install.
+ `,
+ description: `
+ When set to \`prod\` or \`production\`, this is an alias for
+ \`--omit=dev\`.
+ `,
+ flatten (key, obj, flatOptions) {
+ const value = obj[key]
+ if (!/^prod(uction)?$/.test(value))
+ return
+
+ obj.omit = obj.omit || []
+ obj.omit.push('dev')
+ definitions.omit.flatten('omit', obj, flatOptions)
+ },
+})
+
+define('optional', {
+ default: null,
+ type: [null, Boolean],
+ deprecated: `
+ Use \`--omit=optional\` to exclude optional dependencies, or
+ \`--include=optional\` to include them.
+
+ Default value does install optional deps unless otherwise omitted.
+ `,
+ description: `
+ Alias for --include=optional or --omit=optional
+ `,
+ flatten (key, obj, flatOptions) {
+ const value = obj[key]
+ if (value === null)
+ return
+ else if (value === true) {
+ obj.include = obj.include || []
+ obj.include.push('optional')
+ } else {
+ obj.omit = obj.omit || []
+ obj.omit.push('optional')
+ }
+ definitions.omit.flatten('omit', obj, flatOptions)
+ },
+})
+
+define('otp', {
+ default: null,
+ type: [null, String],
+ description: `
+ This is a one-time password from a two-factor authenticator. It's needed
+ when publishing or changing package permissions with \`npm access\`.
+
+ If not set, and a registry response fails with a challenge for a one-time
+ password, npm will prompt on the command line for one.
+ `,
+ flatten,
+})
+
+define('package', {
+ default: [],
+ type: [String, Array],
+ description: `
+ The package to install for [\`npm exec\`](/commands/npm-exec)
+ `,
+ flatten,
+})
+
+define('package-lock', {
+ default: true,
+ type: Boolean,
+ description: `
+ If set to false, then ignore \`package-lock.json\` files when installing.
+ This will also prevent _writing_ \`package-lock.json\` if \`save\` is
+ true.
+
+ When package package-locks are disabled, automatic pruning of extraneous
+ modules will also be disabled. To remove extraneous modules with
+ package-locks disabled use \`npm prune\`.
+ `,
+ flatten,
+})
+
+define('package-lock-only', {
+ default: false,
+ type: Boolean,
+ description: `
+ If set to true, it will update only the \`package-lock.json\`, instead of
+ checking \`node_modules\` and downloading dependencies.
+ `,
+ flatten,
+})
+
+define('parseable', {
+ default: false,
+ type: Boolean,
+ short: 'p',
+ description: `
+ Output parseable results from commands that write to standard output. For
+ \`npm search\`, this will be tab-separated table format.
+ `,
+ flatten,
+})
+
+define('prefer-offline', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, staleness checks for cached data will be bypassed, but missing
+ data will be requested from the server. To force full offline mode, use
+ \`--offline\`.
+ `,
+ flatten,
+})
+
+define('prefer-online', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, staleness checks for cached data will be forced, making the CLI
+ look for updates immediately even for fresh package data.
+ `,
+ flatten,
+})
+
+// `prefix` has its default defined outside of this module
+define('prefix', {
+ type: path,
+ short: 'C',
+ default: '',
+ defaultDescription: `
+ In global mode, the folder where the node executable is installed. In
+ local mode, the nearest parent folder containing either a package.json
+ file or a node_modules folder.
+ `,
+ description: `
+ The location to install global items. If set on the command line, then
+ it forces non-global commands to run in the specified folder.
+ `,
+})
+
+define('preid', {
+ default: '',
+ type: String,
+ description: `
+ The "prerelease identifier" to use as a prefix for the "prerelease" part
+ of a semver. Like the \`rc\` in \`1.2.0-rc.8\`.
+ `,
+ flatten,
+})
+
+define('production', {
+ default: false,
+ type: Boolean,
+ deprecated: 'Use `--omit=dev` instead.',
+ description: 'Alias for `--omit=dev`',
+ flatten (key, obj, flatOptions) {
+ const value = obj[key]
+ if (!value)
+ return
+
+ obj.omit = obj.omit || []
+ obj.omit.push('dev')
+ definitions.omit.flatten('omit', obj, flatOptions)
+ },
+})
+
+define('progress', {
+ default: !ciName,
+ defaultDescription: `
+ \`true\` unless running in a known CI system
+ `,
+ type: Boolean,
+ description: `
+ When set to \`true\`, npm will display a progress bar during time
+ intensive operations, if \`process.stderr\` is a TTY.
+
+ Set to \`false\` to suppress the progress bar.
+ `,
+})
+
+define('proxy', {
+ default: null,
+ type: [null, false, url], // allow proxy to be disabled explicitly
+ description: `
+ A proxy to use for outgoing http requests. If the \`HTTP_PROXY\` or
+ \`http_proxy\` environment variables are set, proxy settings will be
+ honored by the underlying \`request\` library.
+ `,
+ flatten,
+})
+
+define('read-only', {
+ default: false,
+ type: Boolean,
+ description: `
+ This is used to mark a token as unable to publish when configuring
+ limited access tokens with the \`npm token create\` command.
+ `,
+ flatten,
+})
+
+define('rebuild-bundle', {
+ default: true,
+ type: Boolean,
+ description: `
+ Rebuild bundled dependencies after installation.
+ `,
+ flatten,
+})
+
+define('registry', {
+ default: 'https://registry.npmjs.org/',
+ type: url,
+ description: `
+ The base URL of the npm registry.
+ `,
+ flatten,
+})
+
+define('save', {
+ default: true,
+ type: Boolean,
+ short: 'S',
+ description: `
+ Save installed packages to a package.json file as dependencies.
+
+ When used with the \`npm rm\` command, removes the dependency from
+ package.json.
+ `,
+ flatten,
+})
+
+define('save-bundle', {
+ default: false,
+ type: Boolean,
+ short: 'B',
+ description: `
+ If a package would be saved at install time by the use of \`--save\`,
+ \`--save-dev\`, or \`--save-optional\`, then also put it in the
+ \`bundleDependencies\` list.
+
+ Ignore if \`--save-peer\` is set, since peerDependencies cannot be bundled.
+ `,
+ flatten (key, obj, flatOptions) {
+ // XXX update arborist to just ignore it if resulting saveType is peer
+ // otherwise this won't have the expected effect:
+ //
+ // npm config set save-peer true
+ // npm i foo --save-bundle --save-prod <-- should bundle
+ flatOptions.saveBundle = obj['save-bundle'] && !obj['save-peer']
+ },
+})
+
+// XXX: We should really deprecate all these `--save-blah` switches
+// in favor of a single `--save-type` option. The unfortunate shortcut
+// we took for `--save-peer --save-optional` being `--save-type=peerOptional`
+// makes this tricky, and likely a breaking change.
+
+define('save-dev', {
+ default: false,
+ type: Boolean,
+ short: 'D',
+ description: `
+ Save installed packages to a package.json file as \`devDependencies\`.
+ `,
+ flatten (key, obj, flatOptions) {
+ if (!obj[key]) {
+ if (flatOptions.saveType === 'dev')
+ delete flatOptions.saveType
+ return
+ }
+
+ flatOptions.saveType = 'dev'
+ },
+})
+
+define('save-exact', {
+ default: false,
+ type: Boolean,
+ short: 'E',
+ description: `
+ Dependencies saved to package.json will be configured with an exact
+ version rather than using npm's default semver range operator.
+ `,
+ flatten,
+})
+
+define('save-optional', {
+ default: false,
+ type: Boolean,
+ short: 'O',
+ description: `
+ Save installed packages to a package.json file as
+ \`optionalDependencies\`.
+ `,
+ flatten (key, obj, flatOptions) {
+ if (!obj[key]) {
+ if (flatOptions.saveType === 'optional')
+ delete flatOptions.saveType
+ else if (flatOptions.saveType === 'peerOptional')
+ flatOptions.saveType = 'peer'
+ return
+ }
+
+ if (flatOptions.saveType === 'peerOptional')
+ return
+
+ if (flatOptions.saveType === 'peer')
+ flatOptions.saveType = 'peerOptional'
+ else
+ flatOptions.saveType = 'optional'
+ },
+})
+
+define('save-peer', {
+ default: false,
+ type: Boolean,
+ description: `
+ Save installed packages. to a package.json file as \`peerDependencies\`
+ `,
+ flatten (key, obj, flatOptions) {
+ if (!obj[key]) {
+ if (flatOptions.saveType === 'peer')
+ delete flatOptions.saveType
+ else if (flatOptions.saveType === 'peerOptional')
+ flatOptions.saveType = 'optional'
+ return
+ }
+
+ if (flatOptions.saveType === 'peerOptional')
+ return
+
+ if (flatOptions.saveType === 'optional')
+ flatOptions.saveType = 'peerOptional'
+ else
+ flatOptions.saveType = 'peer'
+ },
+})
+
+define('save-prefix', {
+ default: '^',
+ type: String,
+ description: `
+ Configure how versions of packages installed to a package.json file via
+ \`--save\` or \`--save-dev\` get prefixed.
+
+ For example if a package has version \`1.2.3\`, by default its version is
+ set to \`^1.2.3\` which allows minor upgrades for that package, but after
+ \`npm config set save-prefix='~'\` it would be set to \`~1.2.3\` which
+ only allows patch upgrades.
+ `,
+ flatten,
+})
+
+define('save-prod', {
+ default: false,
+ type: Boolean,
+ short: 'P',
+ description: `
+ Save installed packages into \`dependencies\` specifically. This is
+ useful if a package already exists in \`devDependencies\` or
+ \`optionalDependencies\`, but you want to move it to be a non-optional
+ production dependency.
+
+ This is the default behavior if \`--save\` is true, and neither
+ \`--save-dev\` or \`--save-optional\` are true.
+ `,
+ flatten (key, obj, flatOptions) {
+ if (!obj[key]) {
+ if (flatOptions.saveType === 'prod')
+ delete flatOptions.saveType
+ return
+ }
+
+ flatOptions.saveType = 'prod'
+ },
+})
+
+define('scope', {
+ default: '',
+ defaultDescription: `
+ the scope of the current project, if any, or ""
+ `,
+ type: String,
+ description: `
+ Associate an operation with a scope for a scoped registry.
+
+ Useful when logging in to a private registry for the first time:
+
+ \`\`\`bash
+ npm login --scope=@mycorp --registry=https://registry.mycorp.com
+ \`\`\`
+
+ This will cause \`@mycorp\` to be mapped to the registry for future
+ installation of packages specified according to the pattern
+ \`@mycorp/package\`.
+ `,
+ flatten (key, obj, flatOptions) {
+ const value = obj[key]
+ flatOptions.projectScope = value && !/^@/.test(value) ? `@${value}` : value
+ },
+})
+
+define('script-shell', {
+ default: null,
+ defaultDescription: `
+ '/bin/sh' on POSIX systems, 'cmd.exe' on Windows
+ `,
+ type: [null, String],
+ description: `
+ The shell to use for scripts run with the \`npm run\` command.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.scriptShell = obj[key] || undefined
+ },
+})
+
+define('searchexclude', {
+ default: '',
+ type: String,
+ description: `
+ Space-separated options that limit the results from search.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.search = flatOptions.search || { limit: 20 }
+ flatOptions.search.exclude = obj[key]
+ },
+})
+
+define('searchlimit', {
+ default: 20,
+ type: Number,
+ description: `
+ Number of items to limit search results to. Will not apply at all to
+ legacy searches.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.search = flatOptions.search || {}
+ flatOptions.search.limit = obj[key]
+ },
+})
+
+define('searchopts', {
+ default: '',
+ type: String,
+ description: `
+ Space-separated options that are always passed to search.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.search = flatOptions.search || { limit: 20 }
+ flatOptions.search.opts = querystring.parse(obj[key])
+ },
+})
+
+define('searchstaleness', {
+ default: 15 * 60,
+ type: Number,
+ description: `
+ The age of the cache, in seconds, before another registry request is made
+ if using legacy search endpoint.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.search = flatOptions.search || { limit: 20 }
+ flatOptions.search.staleness = obj[key]
+ },
+})
+
+define('shell', {
+ default: shell,
+ defaultDescription: `
+ SHELL environment variable, or "bash" on Posix, or "cmd.exe" on Windows
+ `,
+ type: String,
+ description: `
+ The shell to run for the \`npm explore\` command.
+ `,
+ flatten,
+})
+
+define('shrinkwrap', {
+ default: true,
+ type: Boolean,
+ deprecated: `
+ Use the --package-lock setting instead.
+ `,
+ description: `
+ Alias for --package-lock
+ `,
+ flatten (key, obj, flatOptions) {
+ obj['package-lock'] = obj.shrinkwrap
+ definitions['package-lock'].flatten('package-lock', obj, flatOptions)
+ },
+})
+
+define('sign-git-commit', {
+ default: false,
+ type: Boolean,
+ description: `
+ If set to true, then the \`npm version\` command will commit the new
+ package version using \`-S\` to add a signature.
+
+ Note that git requires you to have set up GPG keys in your git configs
+ for this to work properly.
+ `,
+ flatten,
+})
+
+define('sign-git-tag', {
+ default: false,
+ type: Boolean,
+ description: `
+ If set to true, then the \`npm version\` command will tag the version
+ using \`-s\` to add a signature.
+
+ Note that git requires you to have set up GPG keys in your git configs
+ for this to work properly.
+ `,
+ flatten,
+})
+
+define('sso-poll-frequency', {
+ default: 500,
+ type: Number,
+ deprecated: `
+ The --auth-type method of SSO/SAML/OAuth will be removed in a future
+ version of npm in favor of web-based login.
+ `,
+ description: `
+ When used with SSO-enabled \`auth-type\`s, configures how regularly the
+ registry should be polled while the user is completing authentication.
+ `,
+ flatten,
+})
+
+define('sso-type', {
+ default: 'oauth',
+ type: [null, 'oauth', 'saml'],
+ deprecated: `
+ The --auth-type method of SSO/SAML/OAuth will be removed in a future
+ version of npm in favor of web-based login.
+ `,
+ description: `
+ If \`--auth-type=sso\`, the type of SSO type to use.
+ `,
+ flatten,
+})
+
+define('strict-peer-deps', {
+ default: false,
+ type: Boolean,
+ description: `
+ If set to \`true\`, and \`--legacy-peer-deps\` is not set, then _any_
+ conflicting \`peerDependencies\` will be treated as an install failure,
+ even if npm could reasonably guess the appropriate resolution based on
+ non-peer dependency relationships.
+
+ By default, conflicting \`peerDependencies\` deep in the dependency graph
+ will be resolved using the nearest non-peer dependency specification,
+ even if doing so will result in some packages receiving a peer dependency
+ outside the range set in their package's \`peerDependencies\` object.
+
+ When such and override is performed, a warning is printed, explaining the
+ conflict and the packages involved. If \`--strict-peer-deps\` is set,
+ then this warning is treated as a failure.
+ `,
+ flatten,
+})
+
+define('strict-ssl', {
+ default: true,
+ type: Boolean,
+ description: `
+ Whether or not to do SSL key validation when making requests to the
+ registry via https.
+
+ See also the \`ca\` config.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.strictSSL = obj[key]
+ },
+})
+
+define('tag', {
+ default: 'latest',
+ type: String,
+ description: `
+ If you ask npm to install a package and don't tell it a specific version,
+ then it will install the specified tag.
+
+ Also the tag that is added to the package@version specified by the \`npm
+ tag\` command, if no explicit tag is given.
+ `,
+ flatten (key, obj, flatOptions) {
+ flatOptions.defaultTag = obj[key]
+ },
+})
+
+define('tag-version-prefix', {
+ default: 'v',
+ type: String,
+ description: `
+ If set, alters the prefix used when tagging a new version when performing
+ a version increment using \`npm-version\`. To remove the prefix
+ altogether, set it to the empty string: \`""\`.
+
+ Because other tools may rely on the convention that npm version tags look
+ like \`v1.0.0\`, _only use this property if it is absolutely necessary_.
+ In particular, use care when overriding this setting for public packages.
+ `,
+ flatten,
+})
+
+define('timing', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, writes an \`npm-debug\` log to \`_logs\` and timing information
+ to \`_timing.json\`, both in your cache, even if the command completes
+ successfully. \`_timing.json\` is a newline delimited list of JSON
+ objects.
+
+ You can quickly view it with this [json](https://npm.im/json) command
+ line: \`npm exec -- json -g < ~/.npm/_timing.json\`.
+ `,
+})
+
+define('tmp', {
+ default: tmpdir(),
+ defaultDescription: `
+ The value returned by the Node.js \`os.tmpdir()\` method
+ <https://nodejs.org/api/os.html#os_os_tmpdir>
+ `,
+ type: path,
+ deprecated: `
+ This setting is no longer used. npm stores temporary files in a special
+ location in the cache, and they are managed by
+ [\`cacache\`](http://npm.im/cacache).
+ `,
+ description: `
+ Historically, the location where temporary files were stored. No longer
+ relevant.
+ `,
+})
+
+define('umask', {
+ default: 0,
+ type: Umask,
+ description: `
+ The "umask" value to use when setting the file creation mode on files and
+ folders.
+
+ Folders and executables are given a mode which is \`0o777\` masked
+ against this value. Other files are given a mode which is \`0o666\`
+ masked against this value.
+
+ Note that the underlying system will _also_ apply its own umask value to
+ files and folders that are created, and npm does not circumvent this, but
+ rather adds the \`--umask\` config to it.
+
+ Thus, the effective default umask value on most POSIX systems is 0o22,
+ meaning that folders and executables are created with a mode of 0o755 and
+ other files are created with a mode of 0o644.
+ `,
+ flatten,
+})
+
+define('unicode', {
+ default: unicode,
+ defaultDescription: `
+ false on windows, true on mac/unix systems with a unicode locale, as
+ defined by the LC_ALL, LC_CTYPE, or LANG environment variables.
+ `,
+ type: Boolean,
+ description: `
+ When set to true, npm uses unicode characters in the tree output. When
+ false, it uses ascii characters instead of unicode glyphs.
+ `,
+})
+
+define('update-notifier', {
+ default: true,
+ type: Boolean,
+ description: `
+ Set to false to suppress the update notification when using an older
+ version of npm than the latest.
+ `,
+})
+
+define('usage', {
+ default: false,
+ type: Boolean,
+ short: ['?', 'H', 'h'],
+ description: `
+ Show short usage output about the command specified.
+ `,
+})
+
+define('user-agent', {
+ default: 'npm/{npm-version} ' +
+ 'node/{node-version} ' +
+ '{platform} ' +
+ '{arch} ' +
+ '{ci}',
+ type: String,
+ description: `
+ Sets the User-Agent request header. The following fields are replaced
+ with their actual counterparts:
+
+ * \`{npm-version}\` - The npm version in use
+ * \`{node-version}\` - The Node.js version in use
+ * \`{platform}\` - The value of \`process.platform\`
+ * \`{arch}\` - The value of \`process.arch\`
+ * \`{ci}\` - The value of the \`ci-name\` config, if set, prefixed with
+ \`ci/\`, or an empty string if \`ci-name\` is empty.
+ `,
+ flatten (key, obj, flatOptions) {
+ const value = obj[key]
+ const ciName = obj['ci-name']
+ flatOptions.userAgent =
+ value.replace(/\{node-version\}/gi, obj['node-version'])
+ .replace(/\{npm-version\}/gi, obj['npm-version'])
+ .replace(/\{platform\}/gi, process.platform)
+ .replace(/\{arch\}/gi, process.arch)
+ .replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '')
+ .trim()
+ },
+})
+
+define('userconfig', {
+ default: '~/.npmrc',
+ type: path,
+ description: `
+ The location of user-level configuration settings.
+
+ This may be overridden by the \`npm_config_userconfig\` environment
+ variable or the \`--userconfig\` command line option, but may _not_
+ be overridden by settings in the \`globalconfig\` file.
+ `,
+})
+
+define('version', {
+ default: false,
+ type: Boolean,
+ short: 'v',
+ description: `
+ If true, output the npm version and exit successfully.
+
+ Only relevant when specified explicitly on the command line.
+ `,
+})
+
+define('versions', {
+ default: false,
+ type: Boolean,
+ description: `
+ If true, output the npm version as well as node's \`process.versions\`
+ map and the version in the current working directory's \`package.json\`
+ file if one exists, and exit successfully.
+
+ Only relevant when specified explicitly on the command line.
+ `,
+})
+
+define('viewer', {
+ default: isWindows ? 'browser' : 'man',
+ defaultDescription: `
+ "man" on Posix, "browser" on Windows
+ `,
+ type: String,
+ description: `
+ The program to use to view help content.
+
+ Set to \`"browser"\` to view html help content in the default web browser.
+ `,
+})
+
+define('yes', {
+ default: false,
+ type: Boolean,
+ short: 'y',
+ description: `
+ Automatically answer "yes" to any prompts that npm might print on
+ the command line.
+ `,
+})
diff --git a/lib/utils/config/describe-all.js b/lib/utils/config/describe-all.js
new file mode 100644
index 000000000..ab3f3a63e
--- /dev/null
+++ b/lib/utils/config/describe-all.js
@@ -0,0 +1,16 @@
+const definitions = require('./definitions.js')
+const describeAll = () => {
+ // sort not-deprecated ones to the top
+ /* istanbul ignore next - typically already sorted in the definitions file,
+ * but this is here so that our help doc will stay consistent if we decide
+ * to move them around. */
+ const sort = ([keya, {deprecated: depa}], [keyb, {deprecated: depb}]) => {
+ return depa && !depb ? 1
+ : !depa && depb ? -1
+ : keya.localeCompare(keyb)
+ }
+ return Object.entries(definitions).sort(sort)
+ .map(([key, def]) => def.describe())
+ .join('\n\n')
+}
+module.exports = describeAll
diff --git a/lib/utils/config/flatten.js b/lib/utils/config/flatten.js
new file mode 100644
index 000000000..f6d6124bd
--- /dev/null
+++ b/lib/utils/config/flatten.js
@@ -0,0 +1,32 @@
+// use the defined flattening function, and copy over any scoped
+// registries and registry-specific "nerfdart" configs verbatim
+//
+// TODO: make these getters so that we only have to make dirty
+// the thing that changed, and then flatten the fields that
+// could have changed when a config.set is called.
+//
+// TODO: move nerfdart auth stuff into a nested object that
+// is only passed along to paths that end up calling npm-registry-fetch.
+const definitions = require('./definitions.js')
+const flatten = (obj, flat = {}) => {
+ for (const [key, val] of Object.entries(obj)) {
+ const def = definitions[key]
+ if (def && def.flatten)
+ def.flatten(key, obj, flat)
+ else if (/@.*:registry$/i.test(key) || /^\/\//.test(key))
+ flat[key] = val
+ }
+
+ // XXX make this the bin/npm-cli.js file explicitly instead
+ // otherwise using npm programmatically is a bit of a pain.
+ flat.npmBin = require.main ? require.main.filename
+ : /* istanbul ignore next - not configurable property */ undefined
+ flat.nodeBin = process.env.NODE || process.execPath
+
+ // XXX should this be sha512? is it even relevant?
+ flat.hashAlgorithm = 'sha1'
+
+ return flat
+}
+
+module.exports = flatten
diff --git a/lib/utils/config/index.js b/lib/utils/config/index.js
new file mode 100644
index 000000000..a24f58652
--- /dev/null
+++ b/lib/utils/config/index.js
@@ -0,0 +1,52 @@
+const flatten = require('./flatten.js')
+const definitions = require('./definitions.js')
+const describeAll = require('./describe-all.js')
+
+// aliases where they get expanded into a completely different thing
+// these are NOT supported in the environment or npmrc files, only
+// expanded on the CLI.
+// TODO: when we switch off of nopt, use an arg parser that supports
+// more reasonable aliasing and short opts right in the definitions set.
+const shorthands = {
+ 'enjoy-by': ['--before'],
+ d: ['--loglevel', 'info'],
+ dd: ['--loglevel', 'verbose'],
+ ddd: ['--loglevel', 'silly'],
+ quiet: ['--loglevel', 'warn'],
+ q: ['--loglevel', 'warn'],
+ s: ['--loglevel', 'silent'],
+ silent: ['--loglevel', 'silent'],
+ verbose: ['--loglevel', 'verbose'],
+ desc: ['--description'],
+ help: ['--usage'],
+ local: ['--no-global'],
+ n: ['--no-yes'],
+ no: ['--no-yes'],
+ porcelain: ['--parseable'],
+ readonly: ['--read-only'],
+ reg: ['--registry'],
+}
+
+for (const [key, {short}] of Object.entries(definitions)) {
+ if (!short)
+ continue
+ // can be either an array or string
+ for (const s of [].concat(short))
+ shorthands[s] = [`--${key}`]
+}
+
+module.exports = {
+ get defaults () {
+ // NB: 'default' is a reserved word
+ return Object.entries(definitions).map(([key, { default: def }]) => {
+ return [key, def]
+ }).reduce((defaults, [key, def]) => {
+ defaults[key] = def
+ return defaults
+ }, {})
+ },
+ definitions,
+ flatten,
+ shorthands,
+ describeAll,
+}
diff --git a/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js b/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js
new file mode 100644
index 000000000..ad506ae8e
--- /dev/null
+++ b/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js
@@ -0,0 +1,264 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below. Do not ignore changes!
+ */
+'use strict'
+exports[`test/lib/utils/config/definition.js TAP basic definition > description of deprecated thing 1`] = `
+#### \`deprecated\`
+
+* Default: A number bigger than 1
+* Type: An expression of a numeric quantity using numerals
+* DEPRECATED: do not use this
+
+it should not be used ever
+
+not even once.
+`
+
+exports[`test/lib/utils/config/definition.js TAP basic definition > human-readable description 1`] = `
+#### \`key\`
+
+* Default: "some default value"
+* Type: Number or String
+
+just a test thingie
+`
+
+exports[`test/lib/utils/config/definition.js TAP long description > cols=-1 1`] = `
+#### \`walden\`
+
+* Default: true
+* Type: Boolean
+
+WHEN I WROTE the
+following pages,
+or rather the
+bulk of them, I
+lived alone, in
+the woods, a
+mile from any
+neighbor, in a
+house which I
+had built
+myself, on the
+shore of Walden
+Pond, in
+Concord,
+Massachusetts,
+and earned my
+living by the
+labor of my
+hands only. I
+lived there two
+years and two
+months. At
+present I am a
+sojourner in
+civilized life
+again.
+
+I should not
+obtrude my
+affairs so much
+on the notice of
+my readers if
+very particular
+inquiries had
+not been made by
+my townsmen
+concerning my
+mode of life,
+which some would
+call
+impertinent,
+though they do
+not appear to me
+at all
+impertinent,
+but, considering
+the
+circumstances,
+very natural and
+pertinent.
+
+\`\`\`
+this.is('a', {
+ code: 'sample',
+})
+
+with (multiple) {
+ blocks()
+}
+\`\`\`
+
+`
+
+exports[`test/lib/utils/config/definition.js TAP long description > cols=0 1`] = `
+#### \`walden\`
+
+* Default: true
+* Type: Boolean
+
+WHEN I WROTE the
+following pages,
+or rather the
+bulk of them, I
+lived alone, in
+the woods, a
+mile from any
+neighbor, in a
+house which I
+had built
+myself, on the
+shore of Walden
+Pond, in
+Concord,
+Massachusetts,
+and earned my
+living by the
+labor of my
+hands only. I
+lived there two
+years and two
+months. At
+present I am a
+sojourner in
+civilized life
+again.
+
+I should not
+obtrude my
+affairs so much
+on the notice of
+my readers if
+very particular
+inquiries had
+not been made by
+my townsmen
+concerning my
+mode of life,
+which some would
+call
+impertinent,
+though they do
+not appear to me
+at all
+impertinent,
+but, considering
+the
+circumstances,
+very natural and
+pertinent.
+
+\`\`\`
+this.is('a', {
+ code: 'sample',
+})
+
+with (multiple) {
+ blocks()
+}
+\`\`\`
+
+`
+
+exports[`test/lib/utils/config/definition.js TAP long description > cols=40 1`] = `
+#### \`walden\`
+
+* Default: true
+* Type: Boolean
+
+WHEN I WROTE the following pages, or
+rather the bulk of them, I lived
+alone, in the woods, a mile from any
+neighbor, in a house which I had
+built myself, on the shore of Walden
+Pond, in Concord, Massachusetts, and
+earned my living by the labor of my
+hands only. I lived there two years
+and two months. At present I am a
+sojourner in civilized life again.
+
+I should not obtrude my affairs so
+much on the notice of my readers if
+very particular inquiries had not
+been made by my townsmen concerning
+my mode of life, which some would
+call impertinent, though they do not
+appear to me at all impertinent,
+but, considering the circumstances,
+very natural and pertinent.
+
+\`\`\`
+this.is('a', {
+ code: 'sample',
+})
+
+with (multiple) {
+ blocks()
+}
+\`\`\`
+
+`
+
+exports[`test/lib/utils/config/definition.js TAP long description > cols=9000 1`] = `
+#### \`walden\`
+
+* Default: true
+* Type: Boolean
+
+WHEN I WROTE the following pages, or rather the bulk of them, I lived alone,
+in the woods, a mile from any neighbor, in a house which I had built myself,
+on the shore of Walden Pond, in Concord, Massachusetts, and earned my living
+by the labor of my hands only. I lived there two years and two months. At
+present I am a sojourner in civilized life again.
+
+I should not obtrude my affairs so much on the notice of my readers if very
+particular inquiries had not been made by my townsmen concerning my mode of
+life, which some would call impertinent, though they do not appear to me at
+all impertinent, but, considering the circumstances, very natural and
+pertinent.
+
+\`\`\`
+this.is('a', {
+ code: 'sample',
+})
+
+with (multiple) {
+ blocks()
+}
+\`\`\`
+
+`
+
+exports[`test/lib/utils/config/definition.js TAP long description > cols=NaN 1`] = `
+#### \`walden\`
+
+* Default: true
+* Type: Boolean
+
+WHEN I WROTE the following pages, or rather the bulk of them, I lived alone,
+in the woods, a mile from any neighbor, in a house which I had built myself,
+on the shore of Walden Pond, in Concord, Massachusetts, and earned my living
+by the labor of my hands only. I lived there two years and two months. At
+present I am a sojourner in civilized life again.
+
+I should not obtrude my affairs so much on the notice of my readers if very
+particular inquiries had not been made by my townsmen concerning my mode of
+life, which some would call impertinent, though they do not appear to me at
+all impertinent, but, considering the circumstances, very natural and
+pertinent.
+
+\`\`\`
+this.is('a', {
+ code: 'sample',
+})
+
+with (multiple) {
+ blocks()
+}
+\`\`\`
+
+`
diff --git a/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js b/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js
new file mode 100644
index 000000000..3e82da699
--- /dev/null
+++ b/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js
@@ -0,0 +1,153 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below. Do not ignore changes!
+ */
+'use strict'
+exports[`test/lib/utils/config/definitions.js TAP > all config keys 1`] = `
+Array [
+ "_auth",
+ "access",
+ "all",
+ "allow-same-version",
+ "also",
+ "always-auth",
+ "audit",
+ "audit-level",
+ "auth-type",
+ "before",
+ "bin-links",
+ "browser",
+ "ca",
+ "cache",
+ "cache-max",
+ "cache-min",
+ "cafile",
+ "call",
+ "cert",
+ "ci-name",
+ "cidr",
+ "color",
+ "commit-hooks",
+ "depth",
+ "description",
+ "diff",
+ "diff-ignore-all-space",
+ "diff-name-only",
+ "diff-no-prefix",
+ "diff-dst-prefix",
+ "diff-src-prefix",
+ "diff-text",
+ "diff-unified",
+ "dry-run",
+ "editor",
+ "engine-strict",
+ "fetch-retries",
+ "fetch-retry-factor",
+ "fetch-retry-maxtimeout",
+ "fetch-retry-mintimeout",
+ "fetch-timeout",
+ "force",
+ "foreground-scripts",
+ "format-package-lock",
+ "fund",
+ "git",
+ "git-tag-version",
+ "global",
+ "global-style",
+ "globalconfig",
+ "heading",
+ "https-proxy",
+ "if-present",
+ "ignore-scripts",
+ "include",
+ "include-staged",
+ "init-author-email",
+ "init-author-name",
+ "init-author-url",
+ "init-license",
+ "init-module",
+ "init-version",
+ "init.author.email",
+ "init.author.name",
+ "init.author.url",
+ "init.license",
+ "init.module",
+ "init.version",
+ "json",
+ "key",
+ "legacy-bundling",
+ "legacy-peer-deps",
+ "link",
+ "local-address",
+ "loglevel",
+ "logs-max",
+ "long",
+ "maxsockets",
+ "message",
+ "node-options",
+ "node-version",
+ "noproxy",
+ "npm-version",
+ "offline",
+ "omit",
+ "only",
+ "optional",
+ "otp",
+ "package",
+ "package-lock",
+ "package-lock-only",
+ "parseable",
+ "prefer-offline",
+ "prefer-online",
+ "prefix",
+ "preid",
+ "production",
+ "progress",
+ "proxy",
+ "read-only",
+ "rebuild-bundle",
+ "registry",
+ "save",
+ "save-bundle",
+ "save-dev",
+ "save-exact",
+ "save-optional",
+ "save-peer",
+ "save-prefix",
+ "save-prod",
+ "scope",
+ "script-shell",
+ "searchexclude",
+ "searchlimit",
+ "searchopts",
+ "searchstaleness",
+ "shell",
+ "shrinkwrap",
+ "sign-git-commit",
+ "sign-git-tag",
+ "sso-poll-frequency",
+ "sso-type",
+ "strict-peer-deps",
+ "strict-ssl",
+ "tag",
+ "tag-version-prefix",
+ "timing",
+ "tmp",
+ "umask",
+ "unicode",
+ "update-notifier",
+ "usage",
+ "user-agent",
+ "userconfig",
+ "version",
+ "versions",
+ "viewer",
+ "yes",
+]
+`
+
+exports[`test/lib/utils/config/definitions.js TAP > all config keys that are shared to flatOptions 1`] = `
+Array []
+`
diff --git a/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js b/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js
new file mode 100644
index 000000000..ca758c701
--- /dev/null
+++ b/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js
@@ -0,0 +1,1349 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below. Do not ignore changes!
+ */
+'use strict'
+exports[`test/lib/utils/config/describe-all.js TAP > must match snapshot 1`] = `
+#### \`_auth\`
+
+* Default: null
+* Type: null or String
+
+A basic-auth string to use when authenticating against the npm registry.
+
+Warning: This should generally not be set via a command-line option. It is
+safer to use a registry-provided authentication bearer token stored in the
+~/.npmrc file by running \`npm login\`.
+
+#### \`access\`
+
+* Default: 'restricted' for scoped packages, 'public' for unscoped packages
+* Type: null, "restricted", or "public"
+
+When publishing scoped packages, the access level defaults to \`restricted\`.
+If you want your scoped package to be publicly viewable (and installable)
+set \`--access=public\`. The only valid values for \`access\` are \`public\` and
+\`restricted\`. Unscoped packages _always_ have an access level of \`public\`.
+
+#### \`all\`
+
+* Default: false
+* Type: Boolean
+
+When running \`npm outdated\` and \`npm ls\`, setting \`--all\` will show all
+outdated or installed packages, rather than only those directly depended
+upon by the current project.
+
+#### \`allow-same-version\`
+
+* Default: false
+* Type: Boolean
+
+Prevents throwing an error when \`npm version\` is used to set the new version
+to the same value as the current version.
+
+#### \`always-auth\`
+
+* Default: false
+* Type: Boolean
+
+Force npm to always require authentication when accessing the registry, even
+for \`GET\` requests.
+
+#### \`audit\`
+
+* Default: true
+* Type: Boolean
+
+When "true" submit audit reports alongside \`npm install\` runs to the default
+registry and all registries configured for scopes. See the documentation for
+[\`npm audit\`](/commands/npm-audit) for details on what is submitted.
+
+#### \`audit-level\`
+
+* Default: null
+* Type: "low", "moderate", "high", "critical", "none", or null
+
+The minimum level of vulnerability for \`npm audit\` to exit with a non-zero
+exit code.
+
+#### \`before\`
+
+* Default: null
+* Type: null or Date
+
+If passed to \`npm install\`, will rebuild the npm tree such that only
+versions that were available **on or before** the \`--before\` time get
+installed. If there's no versions available for the current set of direct
+dependencies, the command will error.
+
+If the requested version is a \`dist-tag\` and the given tag does not pass the
+\`--before\` filter, the most recent version less than or equal to that tag
+will be used. For example, \`foo@latest\` might install \`foo@1.2\` even though
+\`latest\` is \`2.0\`.
+
+#### \`bin-links\`
+
+* Default: true
+* Type: Boolean
+
+Tells npm to create symlinks (or \`.cmd\` shims on Windows) for package
+executables.
+
+Set to false to have it not do this. This can be used to work around the
+fact that some file systems don't support symlinks, even on ostensibly Unix
+systems.
+
+#### \`browser\`
+
+* Default: OS X: \`"open"\`, Windows: \`"start"\`, Others: \`"xdg-open"\`
+* Type: null, Boolean, or String
+
+The browser that is called by npm commands to open websites.
+
+Set to \`false\` to suppress browser behavior and instead print urls to
+terminal.
+
+Set to \`true\` to use default system URL opener.
+
+#### \`ca\`
+
+* Default: null
+* Type: null or String (can be set multiple times)
+
+The Certificate Authority signing certificate that is trusted for SSL
+connections to the registry. Values should be in PEM format (Windows calls
+it "Base-64 encoded X.509 (.CER)") with newlines replaced by the string
+"\\n". For example:
+
+\`\`\`ini
+ca="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
+\`\`\`
+
+Set to \`null\` to only allow "known" registrars, or to a specific CA cert to
+trust only that specific signing authority.
+
+Multiple CAs can be trusted by specifying an array of certificates:
+
+\`\`\`ini
+ca[]="..."
+ca[]="..."
+\`\`\`
+
+See also the \`strict-ssl\` config.
+
+#### \`cache\`
+
+* Default: Windows: \`%LocalAppData%\\npm-cache\`, Posix: \`~/.npm\`
+* Type: Path
+
+The location of npm's cache directory. See [\`npm
+cache\`](/commands/npm-cache)
+
+#### \`cafile\`
+
+* Default: null
+* Type: Path
+
+A path to a file containing one or multiple Certificate Authority signing
+certificates. Similar to the \`ca\` setting, but allows for multiple CA's, as
+well as for the CA information to be stored in a file on disk.
+
+#### \`call\`
+
+* Default: ""
+* Type: String
+
+Optional companion option for \`npm exec\`, \`npx\` that allows for specifying a
+custom command to be run along with the installed packages.
+
+\`\`\`bash
+npm exec --package yo --package generator-node --call "yo node"
+\`\`\`
+
+
+#### \`cert\`
+
+* Default: null
+* Type: null or String
+
+A client certificate to pass when accessing the registry. Values should be
+in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with
+newlines replaced by the string "\\n". For example:
+
+\`\`\`ini
+cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
+\`\`\`
+
+It is _not_ the path to a certificate file (and there is no "certfile"
+option).
+
+#### \`ci-name\`
+
+* Default: The name of the current CI system, or \`null\` when not on a known CI
+ platform.
+* Type: null or String
+
+The name of a continuous integration system. If not set explicitly, npm will
+detect the current CI environment using the
+[\`@npmcli/ci-detect\`](http://npm.im/@npmcli/ci-detect) module.
+
+#### \`cidr\`
+
+* Default: null
+* Type: null or String (can be set multiple times)
+
+This is a list of CIDR address to be used when configuring limited access
+tokens with the \`npm token create\` command.
+
+#### \`color\`
+
+* Default: true unless the NO_COLOR environ is set to something other than '0'
+* Type: "always" or Boolean
+
+If false, never shows colors. If \`"always"\` then always shows colors. If
+true, then only prints color codes for tty file descriptors.
+
+#### \`commit-hooks\`
+
+* Default: true
+* Type: Boolean
+
+Run git commit hooks when using the \`npm version\` command.
+
+#### \`depth\`
+
+* Default: \`Infinity\` if \`--all\` is set, otherwise \`1\`
+* Type: null or Number
+
+The depth to go when recursing packages for \`npm ls\`.
+
+If not set, \`npm ls\` will show only the immediate dependencies of the root
+project. If \`--all\` is set, then npm will show all dependencies by default.
+
+#### \`description\`
+
+* Default: true
+* Type: Boolean
+
+Show the description in \`npm search\`
+
+#### \`diff\`
+
+* Default:
+* Type: String (can be set multiple times)
+
+Define arguments to compare in \`npm diff\`.
+
+#### \`diff-dst-prefix\`
+
+* Default: "b/"
+* Type: String
+
+Destination prefix to be used in \`npm diff\` output.
+
+#### \`diff-ignore-all-space\`
+
+* Default: false
+* Type: Boolean
+
+Ignore whitespace when comparing lines in \`npm diff\`.
+
+#### \`diff-name-only\`
+
+* Default: false
+* Type: Boolean
+
+Prints only filenames when using \`npm diff\`.
+
+#### \`diff-no-prefix\`
+
+* Default: false
+* Type: Boolean
+
+Do not show any source or destination prefix in \`npm diff\` output.
+
+Note: this causes \`npm diff\` to ignore the \`--diff-src-prefix\` and
+\`--diff-dst-prefix\` configs.
+
+#### \`diff-src-prefix\`
+
+* Default: "a/"
+* Type: String
+
+Source prefix to be used in \`npm diff\` output.
+
+#### \`diff-text\`
+
+* Default: false
+* Type: Boolean
+
+Treat all files as text in \`npm diff\`.
+
+#### \`diff-unified\`
+
+* Default: 3
+* Type: Number
+
+The number of lines of context to print in \`npm diff\`.
+
+#### \`dry-run\`
+
+* Default: false
+* Type: Boolean
+
+Indicates that you don't want npm to make any changes and that it should
+only report what it would have done. This can be passed into any of the
+commands that modify your local installation, eg, \`install\`, \`update\`,
+\`dedupe\`, \`uninstall\`, as well as \`pack\` and \`publish\`.
+
+Note: This is NOT honored by other network related commands, eg \`dist-tags\`,
+\`owner\`, etc.
+
+#### \`editor\`
+
+* Default: The EDITOR or VISUAL environment variables, or 'notepad.exe' on
+ Windows, or 'vim' on Unix systems
+* Type: String
+
+The command to run for \`npm edit\` and \`npm config edit\`.
+
+#### \`engine-strict\`
+
+* Default: false
+* Type: Boolean
+
+If set to true, then npm will stubbornly refuse to install (or even consider
+installing) any package that claims to not be compatible with the current
+Node.js version.
+
+This can be overridden by setting the \`--force\` flag.
+
+#### \`fetch-retries\`
+
+* Default: 2
+* Type: Number
+
+The "retries" config for the \`retry\` module to use when fetching packages
+from the registry.
+
+npm will retry idempotent read requests to the registry in the case of
+network failures or 5xx HTTP errors.
+
+#### \`fetch-retry-factor\`
+
+* Default: 10
+* Type: Number
+
+The "factor" config for the \`retry\` module to use when fetching packages.
+
+#### \`fetch-retry-maxtimeout\`
+
+* Default: 60000 (1 minute)
+* Type: Number
+
+The "maxTimeout" config for the \`retry\` module to use when fetching
+packages.
+
+#### \`fetch-retry-mintimeout\`
+
+* Default: 10000 (10 seconds)
+* Type: Number
+
+The "minTimeout" config for the \`retry\` module to use when fetching
+packages.
+
+#### \`fetch-timeout\`
+
+* Default: 300000 (5 minutes)
+* Type: Number
+
+The maximum amount of time to wait for HTTP requests to complete.
+
+#### \`force\`
+
+* Default: false
+* Type: Boolean
+
+Removes various protections against unfortunate side effects, common
+mistakes, unnecessary performance degradation, and malicious input.
+
+* Allow clobbering non-npm files in global installs.
+* Allow the \`npm version\` command to work on an unclean git repository.
+* Allow deleting the cache folder with \`npm cache clean\`.
+* Allow installing packages that have an \`engines\` declaration requiring a
+ different version of npm.
+* Allow installing packages that have an \`engines\` declaration requiring a
+ different version of \`node\`, even if \`--engine-strict\` is enabled.
+* Allow \`npm audit fix\` to install modules outside your stated dependency
+ range (including SemVer-major changes).
+* Allow unpublishing all versions of a published package.
+* Allow conflicting peerDependencies to be installed in the root project.
+
+If you don't have a clear idea of what you want to do, it is strongly
+recommended that you do not use this option!
+
+#### \`foreground-scripts\`
+
+* Default: false
+* Type: Boolean
+
+Run all build scripts (ie, \`preinstall\`, \`install\`, and \`postinstall\`)
+scripts for installed packages in the foreground process, sharing standard
+input, output, and error with the main npm process.
+
+Note that this will generally make installs run slower, and be much noisier,
+but can be useful for debugging.
+
+#### \`format-package-lock\`
+
+* Default: true
+* Type: Boolean
+
+Format \`package-lock.json\` or \`npm-shrinkwrap.json\` as a human readable
+file.
+
+#### \`fund\`
+
+* Default: true
+* Type: Boolean
+
+When "true" displays the message at the end of each \`npm install\`
+acknowledging the number of dependencies looking for funding. See [\`npm
+fund\`](/commands/npm-fund) for details.
+
+#### \`git\`
+
+* Default: "git"
+* Type: String
+
+The command to use for git commands. If git is installed on the computer,
+but is not in the \`PATH\`, then set this to the full path to the git binary.
+
+#### \`git-tag-version\`
+
+* Default: true
+* Type: Boolean
+
+Tag the commit when using the \`npm version\` command.
+
+#### \`global\`
+
+* Default: false
+* Type: Boolean
+
+Operates in "global" mode, so that packages are installed into the \`prefix\`
+folder instead of the current working directory. See
+[folders](/configuring-npm/folders) for more on the differences in behavior.
+
+* packages are installed into the \`{prefix}/lib/node_modules\` folder, instead
+ of the current working directory.
+* bin files are linked to \`{prefix}/bin\`
+* man pages are linked to \`{prefix}/share/man\`
+
+#### \`global-style\`
+
+* Default: false
+* Type: Boolean
+
+Causes npm to install the package into your local \`node_modules\` folder with
+the same layout it uses with the global \`node_modules\` folder. Only your
+direct dependencies will show in \`node_modules\` and everything they depend
+on will be flattened in their \`node_modules\` folders. This obviously will
+eliminate some deduping. If used with \`legacy-bundling\`, \`legacy-bundling\`
+will be preferred.
+
+#### \`globalconfig\`
+
+* Default: The global --prefix setting plus 'etc/npmrc'. For example,
+ '/usr/local/etc/npmrc'
+* Type: Path
+
+The config file to read for global config options.
+
+#### \`heading\`
+
+* Default: "npm"
+* Type: String
+
+The string that starts all the debugging log output.
+
+#### \`https-proxy\`
+
+* Default: null
+* Type: null or URL
+
+A proxy to use for outgoing https requests. If the \`HTTPS_PROXY\` or
+\`https_proxy\` or \`HTTP_PROXY\` or \`http_proxy\` environment variables are set,
+proxy settings will be honored by the underlying \`make-fetch-happen\`
+library.
+
+#### \`if-present\`
+
+* Default: false
+* Type: Boolean
+
+If true, npm will not exit with an error code when \`run-script\` is invoked
+for a script that isn't defined in the \`scripts\` section of \`package.json\`.
+This option can be used when it's desirable to optionally run a script when
+it's present and fail if the script fails. This is useful, for example, when
+running scripts that may only apply for some builds in an otherwise generic
+CI setup.
+
+#### \`ignore-scripts\`
+
+* Default: false
+* Type: Boolean
+
+If true, npm does not run scripts specified in package.json files.
+
+#### \`include\`
+
+* Default:
+* Type: "prod", "dev", "optional", or "peer" (can be set multiple times)
+
+Option that allows for defining which types of dependencies to install.
+
+This is the inverse of \`--omit=<type>\`.
+
+Dependency types specified in \`--include\` will not be omitted, regardless of
+the order in which omit/include are specified on the command-line.
+
+#### \`include-staged\`
+
+* Default: false
+* Type: Boolean
+
+Allow installing "staged" published packages, as defined by [npm RFC PR
+#92](https://github.com/npm/rfcs/pull/92).
+
+This is experimental, and not implemented by the npm public registry.
+
+#### \`init-author-email\`
+
+* Default: ""
+* Type: String
+
+The value \`npm init\` should use by default for the package author's email.
+
+#### \`init-author-name\`
+
+* Default: ""
+* Type: String
+
+The value \`npm init\` should use by default for the package author's name.
+
+#### \`init-author-url\`
+
+* Default: ""
+* Type: "" or URL
+
+The value \`npm init\` should use by default for the package author's
+homepage.
+
+#### \`init-license\`
+
+* Default: "ISC"
+* Type: String
+
+The value \`npm init\` should use by default for the package license.
+
+#### \`init-module\`
+
+* Default: "~/.npm-init.js"
+* Type: Path
+
+A module that will be loaded by the \`npm init\` command. See the
+documentation for the
+[init-package-json](https://github.com/npm/init-package-json) module for
+more information, or [npm init](/commands/npm-init).
+
+#### \`init-version\`
+
+* Default: "1.0.0"
+* Type: SemVer string
+
+The value that \`npm init\` should use by default for the package version
+number, if not already set in package.json.
+
+#### \`json\`
+
+* Default: false
+* Type: Boolean
+
+Whether or not to output JSON data, rather than the normal output.
+
+This feature is currently experimental, and the output data structures for
+many commands is either not implemented in JSON yet, or subject to change.
+Only the output from \`npm ls --json\` and \`npm search --json\` are currently
+valid.
+
+#### \`key\`
+
+* Default: null
+* Type: null or String
+
+A client key to pass when accessing the registry. Values should be in PEM
+format with newlines replaced by the string "\\n". For example:
+
+\`\`\`ini
+key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----"
+\`\`\`
+
+It is _not_ the path to a key file (and there is no "keyfile" option).
+
+#### \`legacy-bundling\`
+
+* Default: false
+* Type: Boolean
+
+Causes npm to install the package such that versions of npm prior to 1.4,
+such as the one included with node 0.8, can install the package. This
+eliminates all automatic deduping. If used with \`global-style\` this option
+will be preferred.
+
+#### \`legacy-peer-deps\`
+
+* Default: false
+* Type: Boolean
+
+Causes npm to completely ignore \`peerDependencies\` when building a package
+tree, as in npm versions 3 through 6.
+
+If a package cannot be installed because of overly strict \`peerDependencies\`
+that collide, it provides a way to move forward resolving the situation.
+
+This differs from \`--omit=peer\`, in that \`--omit=peer\` will avoid unpacking
+\`peerDependencies\` on disk, but will still design a tree such that
+\`peerDependencies\` _could_ be unpacked in a correct place.
+
+Use of \`legacy-peer-deps\` is not recommended, as it will not enforce the
+\`peerDependencies\` contract that meta-dependencies may rely on.
+
+#### \`link\`
+
+* Default: false
+* Type: Boolean
+
+If true, then local installs will link if there is a suitable globally
+installed package.
+
+Note that this means that local installs can cause things to be installed
+into the global space at the same time. The link is only done if one of the
+two conditions are met:
+
+* The package is not already installed globally, or
+* the globally installed version is identical to the version that is being
+ installed locally.
+
+#### \`local-address\`
+
+* Default: null
+* Type: IP Address
+
+The IP address of the local interface to use when making connections to the
+npm registry. Must be IPv4 in versions of Node prior to 0.12.
+
+#### \`loglevel\`
+
+* Default: "notice"
+* Type: "silent", "error", "warn", "notice", "http", "timing", "info",
+ "verbose", or "silly"
+
+What level of logs to report. On failure, *all* logs are written to
+\`npm-debug.log\` in the current working directory.
+
+Any logs of a higher level than the setting are shown. The default is
+"notice".
+
+#### \`logs-max\`
+
+* Default: 10
+* Type: Number
+
+The maximum number of log files to store.
+
+#### \`long\`
+
+* Default: false
+* Type: Boolean
+
+Show extended information in \`npm ls\` and \`npm search\`.
+
+#### \`maxsockets\`
+
+* Default: Infinity
+* Type: Number
+
+The maximum number of connections to use per origin (protocol/host/port
+combination).
+
+#### \`message\`
+
+* Default: "%s"
+* Type: String
+
+Commit message which is used by \`npm version\` when creating version commit.
+
+Any "%s" in the message will be replaced with the version number.
+
+#### \`node-options\`
+
+* Default: null
+* Type: null or String
+
+Options to pass through to Node.js via the \`NODE_OPTIONS\` environment
+variable. This does not impact how npm itself is executed but it does impact
+how lifecycle scripts are called.
+
+#### \`node-version\`
+
+* Default: Node.js \`process.version\` value
+* Type: SemVer string
+
+The node version to use when checking a package's \`engines\` setting.
+
+#### \`noproxy\`
+
+* Default: The value of the NO_PROXY environment variable
+* Type: String (can be set multiple times)
+
+Domain extensions that should bypass any proxies.
+
+Also accepts a comma-delimited string.
+
+#### \`npm-version\`
+
+* Default: Output of \`npm --version\`
+* Type: SemVer string
+
+The npm version to use when checking a package's \`engines\` setting.
+
+#### \`offline\`
+
+* Default: false
+* Type: Boolean
+
+Force offline mode: no network requests will be done during install. To
+allow the CLI to fill in missing cache data, see \`--prefer-offline\`.
+
+#### \`omit\`
+
+* Default: 'dev' if the NODE_ENV environment variable is set to 'production',
+ otherwise empty.
+* Type: "dev", "optional", or "peer" (can be set multiple times)
+
+Dependency types to omit from the installation tree on disk.
+
+Note that these dependencies _are_ still resolved and added to the
+\`package-lock.json\` or \`npm-shrinkwrap.json\` file. They are just not
+physically installed on disk.
+
+If a package type appears in both the \`--include\` and \`--omit\` lists, then
+it will be included.
+
+If the resulting omit list includes \`'dev'\`, then the \`NODE_ENV\` environment
+variable will be set to \`'production'\` for all lifecycle scripts.
+
+#### \`otp\`
+
+* Default: null
+* Type: null or String
+
+This is a one-time password from a two-factor authenticator. It's needed
+when publishing or changing package permissions with \`npm access\`.
+
+If not set, and a registry response fails with a challenge for a one-time
+password, npm will prompt on the command line for one.
+
+#### \`package\`
+
+* Default:
+* Type: String (can be set multiple times)
+
+The package to install for [\`npm exec\`](/commands/npm-exec)
+
+#### \`package-lock\`
+
+* Default: true
+* Type: Boolean
+
+If set to false, then ignore \`package-lock.json\` files when installing. This
+will also prevent _writing_ \`package-lock.json\` if \`save\` is true.
+
+When package package-locks are disabled, automatic pruning of extraneous
+modules will also be disabled. To remove extraneous modules with
+package-locks disabled use \`npm prune\`.
+
+#### \`package-lock-only\`
+
+* Default: false
+* Type: Boolean
+
+If set to true, it will update only the \`package-lock.json\`, instead of
+checking \`node_modules\` and downloading dependencies.
+
+#### \`parseable\`
+
+* Default: false
+* Type: Boolean
+
+Output parseable results from commands that write to standard output. For
+\`npm search\`, this will be tab-separated table format.
+
+#### \`prefer-offline\`
+
+* Default: false
+* Type: Boolean
+
+If true, staleness checks for cached data will be bypassed, but missing data
+will be requested from the server. To force full offline mode, use
+\`--offline\`.
+
+#### \`prefer-online\`
+
+* Default: false
+* Type: Boolean
+
+If true, staleness checks for cached data will be forced, making the CLI
+look for updates immediately even for fresh package data.
+
+#### \`prefix\`
+
+* Default: In global mode, the folder where the node executable is installed.
+ In local mode, the nearest parent folder containing either a package.json
+ file or a node_modules folder.
+* Type: Path
+
+The location to install global items. If set on the command line, then it
+forces non-global commands to run in the specified folder.
+
+#### \`preid\`
+
+* Default: ""
+* Type: String
+
+The "prerelease identifier" to use as a prefix for the "prerelease" part of
+a semver. Like the \`rc\` in \`1.2.0-rc.8\`.
+
+#### \`progress\`
+
+* Default: \`true\` unless running in a known CI system
+* Type: Boolean
+
+When set to \`true\`, npm will display a progress bar during time intensive
+operations, if \`process.stderr\` is a TTY.
+
+Set to \`false\` to suppress the progress bar.
+
+#### \`proxy\`
+
+* Default: null
+* Type: null, false, or URL
+
+A proxy to use for outgoing http requests. If the \`HTTP_PROXY\` or
+\`http_proxy\` environment variables are set, proxy settings will be honored
+by the underlying \`request\` library.
+
+#### \`read-only\`
+
+* Default: false
+* Type: Boolean
+
+This is used to mark a token as unable to publish when configuring limited
+access tokens with the \`npm token create\` command.
+
+#### \`rebuild-bundle\`
+
+* Default: true
+* Type: Boolean
+
+Rebuild bundled dependencies after installation.
+
+#### \`registry\`
+
+* Default: "https://registry.npmjs.org/"
+* Type: URL
+
+The base URL of the npm registry.
+
+#### \`save\`
+
+* Default: true
+* Type: Boolean
+
+Save installed packages to a package.json file as dependencies.
+
+When used with the \`npm rm\` command, removes the dependency from
+package.json.
+
+#### \`save-bundle\`
+
+* Default: false
+* Type: Boolean
+
+If a package would be saved at install time by the use of \`--save\`,
+\`--save-dev\`, or \`--save-optional\`, then also put it in the
+\`bundleDependencies\` list.
+
+Ignore if \`--save-peer\` is set, since peerDependencies cannot be bundled.
+
+#### \`save-dev\`
+
+* Default: false
+* Type: Boolean
+
+Save installed packages to a package.json file as \`devDependencies\`.
+
+#### \`save-exact\`
+
+* Default: false
+* Type: Boolean
+
+Dependencies saved to package.json will be configured with an exact version
+rather than using npm's default semver range operator.
+
+#### \`save-optional\`
+
+* Default: false
+* Type: Boolean
+
+Save installed packages to a package.json file as \`optionalDependencies\`.
+
+#### \`save-peer\`
+
+* Default: false
+* Type: Boolean
+
+Save installed packages. to a package.json file as \`peerDependencies\`
+
+#### \`save-prefix\`
+
+* Default: "^"
+* Type: String
+
+Configure how versions of packages installed to a package.json file via
+\`--save\` or \`--save-dev\` get prefixed.
+
+For example if a package has version \`1.2.3\`, by default its version is set
+to \`^1.2.3\` which allows minor upgrades for that package, but after \`npm
+config set save-prefix='~'\` it would be set to \`~1.2.3\` which only allows
+patch upgrades.
+
+#### \`save-prod\`
+
+* Default: false
+* Type: Boolean
+
+Save installed packages into \`dependencies\` specifically. This is useful if
+a package already exists in \`devDependencies\` or \`optionalDependencies\`, but
+you want to move it to be a non-optional production dependency.
+
+This is the default behavior if \`--save\` is true, and neither \`--save-dev\`
+or \`--save-optional\` are true.
+
+#### \`scope\`
+
+* Default: the scope of the current project, if any, or ""
+* Type: String
+
+Associate an operation with a scope for a scoped registry.
+
+Useful when logging in to a private registry for the first time:
+
+\`\`\`bash
+npm login --scope=@mycorp --registry=https://registry.mycorp.com
+\`\`\`
+
+This will cause \`@mycorp\` to be mapped to the registry for future
+installation of packages specified according to the pattern
+\`@mycorp/package\`.
+
+#### \`script-shell\`
+
+* Default: '/bin/sh' on POSIX systems, 'cmd.exe' on Windows
+* Type: null or String
+
+The shell to use for scripts run with the \`npm run\` command.
+
+#### \`searchexclude\`
+
+* Default: ""
+* Type: String
+
+Space-separated options that limit the results from search.
+
+#### \`searchlimit\`
+
+* Default: 20
+* Type: Number
+
+Number of items to limit search results to. Will not apply at all to legacy
+searches.
+
+#### \`searchopts\`
+
+* Default: ""
+* Type: String
+
+Space-separated options that are always passed to search.
+
+#### \`searchstaleness\`
+
+* Default: 900
+* Type: Number
+
+The age of the cache, in seconds, before another registry request is made if
+using legacy search endpoint.
+
+#### \`shell\`
+
+* Default: SHELL environment variable, or "bash" on Posix, or "cmd.exe" on
+ Windows
+* Type: String
+
+The shell to run for the \`npm explore\` command.
+
+#### \`sign-git-commit\`
+
+* Default: false
+* Type: Boolean
+
+If set to true, then the \`npm version\` command will commit the new package
+version using \`-S\` to add a signature.
+
+Note that git requires you to have set up GPG keys in your git configs for
+this to work properly.
+
+#### \`sign-git-tag\`
+
+* Default: false
+* Type: Boolean
+
+If set to true, then the \`npm version\` command will tag the version using
+\`-s\` to add a signature.
+
+Note that git requires you to have set up GPG keys in your git configs for
+this to work properly.
+
+#### \`strict-peer-deps\`
+
+* Default: false
+* Type: Boolean
+
+If set to \`true\`, and \`--legacy-peer-deps\` is not set, then _any_
+conflicting \`peerDependencies\` will be treated as an install failure, even
+if npm could reasonably guess the appropriate resolution based on non-peer
+dependency relationships.
+
+By default, conflicting \`peerDependencies\` deep in the dependency graph will
+be resolved using the nearest non-peer dependency specification, even if
+doing so will result in some packages receiving a peer dependency outside
+the range set in their package's \`peerDependencies\` object.
+
+When such and override is performed, a warning is printed, explaining the
+conflict and the packages involved. If \`--strict-peer-deps\` is set, then
+this warning is treated as a failure.
+
+#### \`strict-ssl\`
+
+* Default: true
+* Type: Boolean
+
+Whether or not to do SSL key validation when making requests to the registry
+via https.
+
+See also the \`ca\` config.
+
+#### \`tag\`
+
+* Default: "latest"
+* Type: String
+
+If you ask npm to install a package and don't tell it a specific version,
+then it will install the specified tag.
+
+Also the tag that is added to the package@version specified by the \`npm tag\`
+command, if no explicit tag is given.
+
+#### \`tag-version-prefix\`
+
+* Default: "v"
+* Type: String
+
+If set, alters the prefix used when tagging a new version when performing a
+version increment using \`npm-version\`. To remove the prefix altogether, set
+it to the empty string: \`""\`.
+
+Because other tools may rely on the convention that npm version tags look
+like \`v1.0.0\`, _only use this property if it is absolutely necessary_. In
+particular, use care when overriding this setting for public packages.
+
+#### \`timing\`
+
+* Default: false
+* Type: Boolean
+
+If true, writes an \`npm-debug\` log to \`_logs\` and timing information to
+\`_timing.json\`, both in your cache, even if the command completes
+successfully. \`_timing.json\` is a newline delimited list of JSON objects.
+
+You can quickly view it with this [json](https://npm.im/json) command line:
+\`npm exec -- json -g < ~/.npm/_timing.json\`.
+
+#### \`umask\`
+
+* Default: 0
+* Type: Octal numeric string in range 0000..0777 (0..511)
+
+The "umask" value to use when setting the file creation mode on files and
+folders.
+
+Folders and executables are given a mode which is \`0o777\` masked against
+this value. Other files are given a mode which is \`0o666\` masked against
+this value.
+
+Note that the underlying system will _also_ apply its own umask value to
+files and folders that are created, and npm does not circumvent this, but
+rather adds the \`--umask\` config to it.
+
+Thus, the effective default umask value on most POSIX systems is 0o22,
+meaning that folders and executables are created with a mode of 0o755 and
+other files are created with a mode of 0o644.
+
+#### \`unicode\`
+
+* Default: false on windows, true on mac/unix systems with a unicode locale,
+ as defined by the LC_ALL, LC_CTYPE, or LANG environment variables.
+* Type: Boolean
+
+When set to true, npm uses unicode characters in the tree output. When
+false, it uses ascii characters instead of unicode glyphs.
+
+#### \`update-notifier\`
+
+* Default: true
+* Type: Boolean
+
+Set to false to suppress the update notification when using an older version
+of npm than the latest.
+
+#### \`usage\`
+
+* Default: false
+* Type: Boolean
+
+Show short usage output about the command specified.
+
+#### \`user-agent\`
+
+* Default: "npm/{npm-version} node/{node-version} {platform} {arch} {ci}"
+* Type: String
+
+Sets the User-Agent request header. The following fields are replaced with
+their actual counterparts:
+
+* \`{npm-version}\` - The npm version in use
+* \`{node-version}\` - The Node.js version in use
+* \`{platform}\` - The value of \`process.platform\`
+* \`{arch}\` - The value of \`process.arch\`
+* \`{ci}\` - The value of the \`ci-name\` config, if set, prefixed with \`ci/\`, or
+ an empty string if \`ci-name\` is empty.
+
+#### \`userconfig\`
+
+* Default: "~/.npmrc"
+* Type: Path
+
+The location of user-level configuration settings.
+
+This may be overridden by the \`npm_config_userconfig\` environment variable
+or the \`--userconfig\` command line option, but may _not_ be overridden by
+settings in the \`globalconfig\` file.
+
+#### \`version\`
+
+* Default: false
+* Type: Boolean
+
+If true, output the npm version and exit successfully.
+
+Only relevant when specified explicitly on the command line.
+
+#### \`versions\`
+
+* Default: false
+* Type: Boolean
+
+If true, output the npm version as well as node's \`process.versions\` map and
+the version in the current working directory's \`package.json\` file if one
+exists, and exit successfully.
+
+Only relevant when specified explicitly on the command line.
+
+#### \`viewer\`
+
+* Default: "man" on Posix, "browser" on Windows
+* Type: String
+
+The program to use to view help content.
+
+Set to \`"browser"\` to view html help content in the default web browser.
+
+#### \`yes\`
+
+* Default: false
+* Type: Boolean
+
+Automatically answer "yes" to any prompts that npm might print on the
+command line.
+
+#### \`also\`
+
+* Default: null
+* Type: null, "dev", or "development"
+* DEPRECATED: Please use --include=dev instead.
+
+When set to \`dev\` or \`development\`, this is an alias for \`--include=dev\`.
+
+#### \`auth-type\`
+
+* Default: "legacy"
+* Type: "legacy", "sso", "saml", or "oauth"
+* DEPRECATED: This method of SSO/SAML/OAuth is deprecated and will be removed
+ in a future version of npm in favor of web-based login.
+
+What authentication strategy to use with \`adduser\`/\`login\`.
+
+#### \`cache-max\`
+
+* Default: Infinity
+* Type: Number
+* DEPRECATED: This option has been deprecated in favor of \`--prefer-online\`
+
+\`--cache-max=0\` is an alias for \`--prefer-online\`
+
+#### \`cache-min\`
+
+* Default: 0
+* Type: Number
+* DEPRECATED: This option has been deprecated in favor of \`--prefer-offline\`.
+
+\`--cache-min=9999 (or bigger)\` is an alias for \`--prefer-offline\`.
+
+#### \`init.author.email\`
+
+* Default: ""
+* Type: String
+* DEPRECATED: Use \`--init-author-email\` instead.
+
+Alias for \`--init-author-email\`
+
+#### \`init.author.name\`
+
+* Default: ""
+* Type: String
+* DEPRECATED: Use \`--init-author-name\` instead.
+
+Alias for \`--init-author-name\`
+
+#### \`init.author.url\`
+
+* Default: ""
+* Type: "" or URL
+* DEPRECATED: Use \`--init-author-url\` instead.
+
+Alias for \`--init-author-url\`
+
+#### \`init.license\`
+
+* Default: "ISC"
+* Type: String
+* DEPRECATED: Use \`--init-license\` instead.
+
+Alias for \`--init-license\`
+
+#### \`init.module\`
+
+* Default: "~/.npm-init.js"
+* Type: Path
+* DEPRECATED: Use \`--init-module\` instead.
+
+Alias for \`--init-module\`
+
+#### \`init.version\`
+
+* Default: "1.0.0"
+* Type: SemVer string
+* DEPRECATED: Use \`--init-version\` instead.
+
+Alias for \`--init-version\`
+
+#### \`only\`
+
+* Default: null
+* Type: null, "prod", or "production"
+* DEPRECATED: Use \`--omit=dev\` to omit dev dependencies from the install.
+
+When set to \`prod\` or \`production\`, this is an alias for \`--omit=dev\`.
+
+#### \`optional\`
+
+* Default: null
+* Type: null or Boolean
+* DEPRECATED: Use \`--omit=optional\` to exclude optional dependencies, or
+ \`--include=optional\` to include them.
+
+Default value does install optional deps unless otherwise omitted.
+
+Alias for --include=optional or --omit=optional
+
+#### \`production\`
+
+* Default: false
+* Type: Boolean
+* DEPRECATED: Use \`--omit=dev\` instead.
+
+Alias for \`--omit=dev\`
+
+#### \`shrinkwrap\`
+
+* Default: true
+* Type: Boolean
+* DEPRECATED: Use the --package-lock setting instead.
+
+Alias for --package-lock
+
+#### \`sso-poll-frequency\`
+
+* Default: 500
+* Type: Number
+* DEPRECATED: The --auth-type method of SSO/SAML/OAuth will be removed in a
+ future version of npm in favor of web-based login.
+
+When used with SSO-enabled \`auth-type\`s, configures how regularly the
+registry should be polled while the user is completing authentication.
+
+#### \`sso-type\`
+
+* Default: "oauth"
+* Type: null, "oauth", or "saml"
+* DEPRECATED: The --auth-type method of SSO/SAML/OAuth will be removed in a
+ future version of npm in favor of web-based login.
+
+If \`--auth-type=sso\`, the type of SSO type to use.
+
+#### \`tmp\`
+
+* Default: The value returned by the Node.js \`os.tmpdir()\` method
+ <https://nodejs.org/api/os.html#os_os_tmpdir>
+* Type: Path
+* DEPRECATED: This setting is no longer used. npm stores temporary files in a
+ special location in the cache, and they are managed by
+ [\`cacache\`](http://npm.im/cacache).
+
+Historically, the location where temporary files were stored. No longer
+relevant.
+`
diff --git a/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js b/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js
new file mode 100644
index 000000000..1044b3063
--- /dev/null
+++ b/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js
@@ -0,0 +1,127 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below. Do not ignore changes!
+ */
+'use strict'
+exports[`test/lib/utils/config/index.js TAP > shorthands 1`] = `
+Object {
+ "?": Array [
+ "--usage",
+ ],
+ "a": Array [
+ "--all",
+ ],
+ "B": Array [
+ "--save-bundle",
+ ],
+ "c": Array [
+ "--call",
+ ],
+ "C": Array [
+ "--prefix",
+ ],
+ "d": Array [
+ "--loglevel",
+ "info",
+ ],
+ "D": Array [
+ "--save-dev",
+ ],
+ "dd": Array [
+ "--loglevel",
+ "verbose",
+ ],
+ "ddd": Array [
+ "--loglevel",
+ "silly",
+ ],
+ "desc": Array [
+ "--description",
+ ],
+ "E": Array [
+ "--save-exact",
+ ],
+ "enjoy-by": Array [
+ "--before",
+ ],
+ "f": Array [
+ "--force",
+ ],
+ "g": Array [
+ "--global",
+ ],
+ "h": Array [
+ "--usage",
+ ],
+ "H": Array [
+ "--usage",
+ ],
+ "help": Array [
+ "--usage",
+ ],
+ "l": Array [
+ "--long",
+ ],
+ "local": Array [
+ "--no-global",
+ ],
+ "m": Array [
+ "--message",
+ ],
+ "n": Array [
+ "--no-yes",
+ ],
+ "no": Array [
+ "--no-yes",
+ ],
+ "O": Array [
+ "--save-optional",
+ ],
+ "p": Array [
+ "--parseable",
+ ],
+ "P": Array [
+ "--save-prod",
+ ],
+ "porcelain": Array [
+ "--parseable",
+ ],
+ "q": Array [
+ "--loglevel",
+ "warn",
+ ],
+ "quiet": Array [
+ "--loglevel",
+ "warn",
+ ],
+ "readonly": Array [
+ "--read-only",
+ ],
+ "reg": Array [
+ "--registry",
+ ],
+ "s": Array [
+ "--loglevel",
+ "silent",
+ ],
+ "S": Array [
+ "--save",
+ ],
+ "silent": Array [
+ "--loglevel",
+ "silent",
+ ],
+ "v": Array [
+ "--version",
+ ],
+ "verbose": Array [
+ "--loglevel",
+ "verbose",
+ ],
+ "y": Array [
+ "--yes",
+ ],
+}
+`
diff --git a/test/lib/utils/config/definition.js b/test/lib/utils/config/definition.js
new file mode 100644
index 000000000..25530f723
--- /dev/null
+++ b/test/lib/utils/config/definition.js
@@ -0,0 +1,150 @@
+const t = require('tap')
+const Definition = require('../../../../lib/utils/config/definition.js')
+const {
+ typeDefs: {
+ semver: { type: semver },
+ Umask: { type: Umask },
+ url: { type: url },
+ path: { type: path },
+ },
+} = require('@npmcli/config')
+
+t.test('basic definition', async t => {
+ const def = new Definition('key', {
+ default: 'some default value',
+ type: [Number, String],
+ description: 'just a test thingie',
+ })
+ t.same(def, {
+ constructor: Definition,
+ key: 'key',
+ default: 'some default value',
+ defaultDescription: '"some default value"',
+ type: [Number, String],
+ typeDescription: 'Number or String',
+ description: 'just a test thingie',
+ })
+ t.matchSnapshot(def.describe(), 'human-readable description')
+
+ const deprecated = new Definition('deprecated', {
+ deprecated: 'do not use this',
+ default: 1234,
+ description: ' it should not be used\n ever\n\n not even once.\n\n',
+ type: Number,
+ defaultDescription: 'A number bigger than 1',
+ typeDescription: 'An expression of a numeric quantity using numerals',
+ })
+ t.matchSnapshot(deprecated.describe(), 'description of deprecated thing')
+
+ const nullOrUmask = new Definition('key', {
+ default: null,
+ type: [null, Umask],
+ description: 'asdf',
+ })
+ t.equal(nullOrUmask.typeDescription, 'null or Octal numeric string in range 0000..0777 (0..511)')
+ const nullDateOrBool = new Definition('key', {
+ default: 7,
+ type: [null, Date, Boolean],
+ description: 'asdf',
+ })
+ t.equal(nullDateOrBool.typeDescription, 'null, Date, or Boolean')
+ const manyPaths = new Definition('key', {
+ default: ['asdf'],
+ type: [path, Array],
+ description: 'asdf',
+ })
+ t.equal(manyPaths.typeDescription, 'Path (can be set multiple times)')
+ const pathOrUrl = new Definition('key', {
+ default: ['https://example.com'],
+ type: [path, url],
+ description: 'asdf',
+ })
+ t.equal(pathOrUrl.typeDescription, 'Path or URL')
+ const multi12 = new Definition('key', {
+ default: [],
+ type: [1, 2, Array],
+ description: 'asdf',
+ })
+ t.equal(multi12.typeDescription, '1 or 2 (can be set multiple times)')
+ const multi123 = new Definition('key', {
+ default: [],
+ type: [1, 2, 3, Array],
+ description: 'asdf',
+ })
+ t.equal(multi123.typeDescription, '1, 2, or 3 (can be set multiple times)')
+ const multi123Semver = new Definition('key', {
+ default: [],
+ type: [1, 2, 3, Array, semver],
+ description: 'asdf',
+ })
+ t.equal(multi123Semver.typeDescription, '1, 2, 3, or SemVer string (can be set multiple times)')
+})
+
+t.test('missing fields', async t => {
+ t.throws(() => new Definition('lacks-default', {
+ description: 'no default',
+ type: String,
+ }), { message: 'config lacks default: lacks-default' })
+ t.throws(() => new Definition('lacks-type', {
+ description: 'no type',
+ default: 1234,
+ }), { message: 'config lacks type: lacks-type' })
+ t.throws(() => new Definition(null, {
+ description: 'falsey key',
+ default: 1234,
+ type: Number,
+ }), { message: 'config lacks key: null' })
+ t.throws(() => new Definition('extra-field', {
+ type: String,
+ default: 'extra',
+ extra: 'more than is wanted',
+ description: 'this is not ok',
+ }), { message: 'config defines unknown field extra: extra-field' })
+})
+
+t.test('long description', async t => {
+ const { stdout: { columns } } = process
+ t.teardown(() => process.stdout.columns = columns)
+
+ const long = new Definition('walden', {
+ description: `
+ WHEN I WROTE the following pages, or rather the bulk of them, I lived
+ alone, in the woods, a mile from any neighbor, in a house which I had
+ built myself, on the shore of Walden Pond, in Concord, Massachusetts, and
+ earned my living by the labor of my hands only. I lived there two years
+ and two months. At present I am a sojourner in civilized life again.
+
+ I should not obtrude my affairs so much on the notice of my readers if
+ very particular inquiries had not been made by my townsmen concerning my
+ mode of life, which some would call impertinent, though they do not
+ appear to me at all impertinent, but, considering the circumstances, very
+ natural and pertinent.
+
+ \`\`\`
+ this.is('a', {
+ code: 'sample',
+ })
+
+ with (multiple) {
+ blocks()
+ }
+ \`\`\`
+ `,
+ default: true,
+ type: Boolean,
+ })
+ process.stdout.columns = 40
+ t.matchSnapshot(long.describe(), 'cols=40')
+
+ process.stdout.columns = 9000
+ t.matchSnapshot(long.describe(), 'cols=9000')
+
+ process.stdout.columns = 0
+ t.matchSnapshot(long.describe(), 'cols=0')
+
+ process.stdout.columns = -1
+ t.matchSnapshot(long.describe(), 'cols=-1')
+
+ process.stdout.columns = NaN
+ t.matchSnapshot(long.describe(), 'cols=NaN')
+})
diff --git a/test/lib/utils/config/definitions.js b/test/lib/utils/config/definitions.js
new file mode 100644
index 000000000..3169feefb
--- /dev/null
+++ b/test/lib/utils/config/definitions.js
@@ -0,0 +1,697 @@
+const t = require('tap')
+
+const requireInject = require('require-inject')
+const { resolve } = require('path')
+
+// have to fake the node version, or else it'll only pass on this one
+Object.defineProperty(process, 'version', {
+ value: 'v14.8.0',
+})
+
+// also fake the npm version, so that it doesn't get reset every time
+const pkg = require('../../../../package.json')
+
+// this is a pain to keep typing
+const defpath = '../../../../lib/utils/config/definitions.js'
+
+// set this in the test when we need it
+delete process.env.NODE_ENV
+const definitions = require(defpath)
+
+const isWin = '../../../../lib/utils/is-windows.js'
+
+// snapshot these just so we note when they change
+t.matchSnapshot(Object.keys(definitions), 'all config keys')
+t.matchSnapshot(Object.keys(definitions).filter(d => d.flatten),
+ 'all config keys that are shared to flatOptions')
+
+t.equal(definitions['npm-version'].default, pkg.version, 'npm-version default')
+t.equal(definitions['node-version'].default, process.version, 'node-version default')
+
+t.test('basic flattening function camelCases from css-case', t => {
+ const flat = {}
+ const obj = { 'always-auth': true }
+ definitions['always-auth'].flatten('always-auth', obj, flat)
+ t.strictSame(flat, { alwaysAuth: true })
+ t.end()
+})
+
+t.test('editor', t => {
+ t.test('has EDITOR and VISUAL, use EDITOR', t => {
+ process.env.EDITOR = 'vim'
+ process.env.VISUAL = 'mate'
+ const defs = requireInject(defpath)
+ t.equal(defs.editor.default, 'vim')
+ t.end()
+ })
+ t.test('has VISUAL but no EDITOR, use VISUAL', t => {
+ delete process.env.EDITOR
+ process.env.VISUAL = 'mate'
+ const defs = requireInject(defpath)
+ t.equal(defs.editor.default, 'mate')
+ t.end()
+ })
+ t.test('has neither EDITOR nor VISUAL, system specific', t => {
+ delete process.env.EDITOR
+ delete process.env.VISUAL
+ const defsWin = requireInject(defpath, {
+ [isWin]: true,
+ })
+ t.equal(defsWin.editor.default, 'notepad.exe')
+ const defsNix = requireInject(defpath, {
+ [isWin]: false,
+ })
+ t.equal(defsNix.editor.default, 'vi')
+ t.end()
+ })
+ t.end()
+})
+
+t.test('shell', t => {
+ t.test('windows, env.ComSpec then cmd.exe', t => {
+ process.env.ComSpec = 'command.com'
+ const defsComSpec = requireInject(defpath, {
+ [isWin]: true,
+ })
+ t.equal(defsComSpec.shell.default, 'command.com')
+ delete process.env.ComSpec
+ const defsNoComSpec = requireInject(defpath, {
+ [isWin]: true,
+ })
+ t.equal(defsNoComSpec.shell.default, 'cmd')
+ t.end()
+ })
+
+ t.test('nix, SHELL then sh', t => {
+ process.env.SHELL = '/usr/local/bin/bash'
+ const defsShell = requireInject(defpath, {
+ [isWin]: false,
+ })
+ t.equal(defsShell.shell.default, '/usr/local/bin/bash')
+ delete process.env.SHELL
+ const defsNoShell = requireInject(defpath, {
+ [isWin]: false,
+ })
+ t.equal(defsNoShell.shell.default, 'sh')
+ t.end()
+ })
+
+ t.end()
+})
+
+t.test('local-address allowed types', t => {
+ t.test('get list from os.networkInterfaces', t => {
+ const os = {
+ tmpdir: () => '/tmp',
+ networkInterfaces: () => ({
+ eth420: [{ address: '127.0.0.1' }],
+ eth69: [{ address: 'no place like home' }],
+ }),
+ }
+ const defs = requireInject(defpath, { os })
+ t.same(defs['local-address'].type, [
+ null,
+ '127.0.0.1',
+ 'no place like home',
+ ])
+ t.end()
+ })
+ t.test('handle os.networkInterfaces throwing', t => {
+ const os = {
+ tmpdir: () => '/tmp',
+ networkInterfaces: () => {
+ throw new Error('no network interfaces for some reason')
+ },
+ }
+ const defs = requireInject(defpath, { os })
+ t.same(defs['local-address'].type, [null])
+ t.end()
+ })
+ t.end()
+})
+
+t.test('unicode allowed?', t => {
+ const { LC_ALL, LC_CTYPE, LANG } = process.env
+ t.teardown(() => Object.assign(process.env, { LC_ALL, LC_CTYPE, LANG }))
+
+ process.env.LC_ALL = 'utf8'
+ process.env.LC_CTYPE = 'UTF-8'
+ process.env.LANG = 'Unicode utf-8'
+
+ const lcAll = requireInject(defpath)
+ t.equal(lcAll.unicode.default, true)
+ process.env.LC_ALL = 'no unicode for youUUUU!'
+ const noLcAll = requireInject(defpath)
+ t.equal(noLcAll.unicode.default, false)
+
+ delete process.env.LC_ALL
+ const lcCtype = requireInject(defpath)
+ t.equal(lcCtype.unicode.default, true)
+ process.env.LC_CTYPE = 'something other than unicode version 8'
+ const noLcCtype = requireInject(defpath)
+ t.equal(noLcCtype.unicode.default, false)
+
+ delete process.env.LC_CTYPE
+ const lang = requireInject(defpath)
+ t.equal(lang.unicode.default, true)
+ process.env.LANG = 'ISO-8859-1'
+ const noLang = requireInject(defpath)
+ t.equal(noLang.unicode.default, false)
+ t.end()
+})
+
+t.test('cache', t => {
+ process.env.LOCALAPPDATA = 'app/data/local'
+ const defsWinLocalAppData = requireInject(defpath, {
+ [isWin]: true,
+ })
+ t.equal(defsWinLocalAppData.cache.default, 'app/data/local/npm-cache')
+
+ delete process.env.LOCALAPPDATA
+ const defsWinNoLocalAppData = requireInject(defpath, {
+ [isWin]: true,
+ })
+ t.equal(defsWinNoLocalAppData.cache.default, '~/npm-cache')
+
+ const defsNix = requireInject(defpath, {
+ [isWin]: false,
+ })
+ t.equal(defsNix.cache.default, '~/.npm')
+
+ const flat = {}
+ defsNix.cache.flatten('cache', { cache: '/some/cache/value' }, flat)
+ const {join} = require('path')
+ t.equal(flat.cache, join('/some/cache/value', '_cacache'))
+
+ t.end()
+})
+
+t.test('flatteners that populate flat.omit array', t => {
+ t.test('also', t => {
+ const flat = {}
+ const obj = {}
+
+ // ignored if setting is not dev or development
+ obj.also = 'ignored'
+ definitions.also.flatten('also', obj, flat)
+ t.strictSame(obj, {also: 'ignored'}, 'nothing done')
+ t.strictSame(flat, {}, 'nothing done')
+
+ obj.also = 'development'
+ definitions.also.flatten('also', obj, flat)
+ t.strictSame(obj, { also: 'development', include: ['dev'] }, 'marked dev as included')
+ t.strictSame(flat, { omit: [] }, 'nothing omitted, so nothing changed')
+
+ obj.omit = ['dev', 'optional']
+ obj.include = []
+ definitions.also.flatten('also', obj, flat)
+ t.strictSame(obj, { also: 'development', omit: ['dev', 'optional'], include: ['dev'] }, 'marked dev as included')
+ t.strictSame(flat, { omit: ['optional'] }, 'removed dev from omit')
+ t.end()
+ })
+
+ t.test('include', t => {
+ const flat = {}
+ const obj = { include: ['dev'] }
+ definitions.include.flatten('include', obj, flat)
+ t.strictSame(flat, {omit: []}, 'not omitting anything')
+ obj.omit = ['optional', 'dev']
+ definitions.include.flatten('include', obj, flat)
+ t.strictSame(flat, {omit: ['optional']}, 'only omitting optional')
+ t.end()
+ })
+
+ t.test('omit', t => {
+ const flat = {}
+ const obj = { include: ['dev'], omit: ['dev', 'optional'] }
+ definitions.omit.flatten('omit', obj, flat)
+ t.strictSame(flat, { omit: ['optional'] }, 'do not omit what is included')
+
+ process.env.NODE_ENV = 'production'
+ const defProdEnv = requireInject(defpath)
+ t.strictSame(defProdEnv.omit.default, ['dev'], 'omit dev in production')
+ t.end()
+ })
+
+ t.test('only', t => {
+ const flat = {}
+ const obj = { only: 'asdf' }
+ definitions.only.flatten('only', obj, flat)
+ t.strictSame(flat, {}, 'ignored if value is not production')
+
+ obj.only = 'prod'
+ definitions.only.flatten('only', obj, flat)
+ t.strictSame(flat, {omit: ['dev']}, 'omit dev when --only=prod')
+
+ obj.include = ['dev']
+ flat.omit = []
+ definitions.only.flatten('only', obj, flat)
+ t.strictSame(flat, {omit: []}, 'do not omit when included')
+
+ t.end()
+ })
+
+ t.test('optional', t => {
+ const flat = {}
+ const obj = { optional: null }
+
+ definitions.optional.flatten('optional', obj, flat)
+ t.strictSame(obj, { optional: null }, 'do nothing by default')
+ t.strictSame(flat, {}, 'do nothing by default')
+
+ obj.optional = true
+ definitions.optional.flatten('optional', obj, flat)
+ t.strictSame(obj, {include: ['optional'], optional: true}, 'include optional when set')
+ t.strictSame(flat, {omit: []}, 'nothing to omit in flatOptions')
+
+ delete obj.include
+ obj.optional = false
+ definitions.optional.flatten('optional', obj, flat)
+ t.strictSame(obj, {omit: ['optional'], optional: false}, 'omit optional when set false')
+ t.strictSame(flat, {omit: ['optional']}, 'omit optional when set false')
+
+ t.end()
+ })
+
+ t.test('production', t => {
+ const flat = {}
+ const obj = {production: true}
+ definitions.production.flatten('production', obj, flat)
+ t.strictSame(obj, {production: true, omit: ['dev']}, '--production sets --omit=dev')
+ t.strictSame(flat, {omit: ['dev']}, '--production sets --omit=dev')
+
+ delete obj.omit
+ obj.production = false
+ delete flat.omit
+ definitions.production.flatten('production', obj, flat)
+ t.strictSame(obj, {production: false}, '--no-production has no effect')
+ t.strictSame(flat, {}, '--no-production has no effect')
+
+ obj.production = true
+ obj.include = ['dev']
+ definitions.production.flatten('production', obj, flat)
+ t.strictSame(obj, {production: true, include: ['dev'], omit: ['dev']}, 'omit and include dev')
+ t.strictSame(flat, {omit: []}, 'do not omit dev when included')
+
+ t.end()
+ })
+
+ t.end()
+})
+
+t.test('cache-max', t => {
+ const flat = {}
+ const obj = { 'cache-max': 10342 }
+ definitions['cache-max'].flatten('cache-max', obj, flat)
+ t.strictSame(flat, {}, 'no effect if not <= 0')
+ obj['cache-max'] = 0
+ definitions['cache-max'].flatten('cache-max', obj, flat)
+ t.strictSame(flat, {preferOnline: true}, 'preferOnline if <= 0')
+ t.end()
+})
+
+t.test('cache-min', t => {
+ const flat = {}
+ const obj = { 'cache-min': 123 }
+ definitions['cache-min'].flatten('cache-min', obj, flat)
+ t.strictSame(flat, {}, 'no effect if not >= 9999')
+ obj['cache-min'] = 9999
+ definitions['cache-min'].flatten('cache-min', obj, flat)
+ t.strictSame(flat, {preferOffline: true}, 'preferOffline if >=9999')
+ t.end()
+})
+
+t.test('color', t => {
+ const { isTTY } = process.stdout
+ t.teardown(() => process.stdout.isTTY = isTTY)
+
+ const flat = {}
+ const obj = { color: 'always' }
+
+ definitions.color.flatten('color', obj, flat)
+ t.strictSame(flat, {color: true}, 'true when --color=always')
+
+ obj.color = false
+ definitions.color.flatten('color', obj, flat)
+ t.strictSame(flat, {color: false}, 'true when --no-color')
+
+ process.stdout.isTTY = false
+ obj.color = true
+ definitions.color.flatten('color', obj, flat)
+ t.strictSame(flat, {color: false}, 'no color when stdout not tty')
+ process.stdout.isTTY = true
+ definitions.color.flatten('color', obj, flat)
+ t.strictSame(flat, {color: true}, '--color turns on color when stdout is tty')
+
+ delete process.env.NO_COLOR
+ const defsAllowColor = requireInject(defpath)
+ t.equal(defsAllowColor.color.default, true, 'default true when no NO_COLOR env')
+
+ process.env.NO_COLOR = '0'
+ const defsNoColor0 = requireInject(defpath)
+ t.equal(defsNoColor0.color.default, true, 'default true when no NO_COLOR=0')
+
+ process.env.NO_COLOR = '1'
+ const defsNoColor1 = requireInject(defpath)
+ t.equal(defsNoColor1.color.default, false, 'default false when no NO_COLOR=1')
+
+ t.end()
+})
+
+t.test('retry options', t => {
+ const obj = {}
+ // <config>: flat.retry[<option>]
+ const mapping = {
+ 'fetch-retries': 'retries',
+ 'fetch-retry-factor': 'factor',
+ 'fetch-retry-maxtimeout': 'maxTimeout',
+ 'fetch-retry-mintimeout': 'minTimeout',
+ }
+ for (const [config, option] of Object.entries(mapping)) {
+ const msg = `${config} -> retry.${option}`
+ const flat = {}
+ obj[config] = 99
+ definitions[config].flatten(config, obj, flat)
+ t.strictSame(flat, {retry: {[option]: 99}}, msg)
+ delete obj[config]
+ }
+ t.end()
+})
+
+t.test('search options', t => {
+ const obj = {}
+ // <config>: flat.search[<option>]
+ const mapping = {
+ description: 'description',
+ searchexclude: 'exclude',
+ searchlimit: 'limit',
+ searchstaleness: 'staleness',
+ }
+
+ for (const [config, option] of Object.entries(mapping)) {
+ const msg = `${config} -> search.${option}`
+ const flat = {}
+ obj[config] = 99
+ definitions[config].flatten(config, obj, flat)
+ t.strictSame(flat, { search: { limit: 20, [option]: 99 }}, msg)
+ delete obj[config]
+ }
+
+ const flat = {}
+ obj.searchopts = 'a=b&b=c'
+ definitions.searchopts.flatten('searchopts', obj, flat)
+ t.strictSame(flat, {
+ search: {
+ limit: 20,
+ opts: Object.assign(Object.create(null), {
+ a: 'b',
+ b: 'c',
+ }),
+ },
+ }, 'searchopts -> querystring.parse() -> search.opts')
+ delete obj.searchopts
+
+ t.end()
+})
+
+t.test('noProxy', t => {
+ const obj = { noproxy: ['1.2.3.4,2.3.4.5', '3.4.5.6'] }
+ const flat = {}
+ definitions.noproxy.flatten('noproxy', obj, flat)
+ t.strictSame(flat, { noProxy: '1.2.3.4,2.3.4.5,3.4.5.6' })
+ t.end()
+})
+
+t.test('maxSockets', t => {
+ const obj = { maxsockets: 123 }
+ const flat = {}
+ definitions.maxsockets.flatten('maxsockets', obj, flat)
+ t.strictSame(flat, { maxSockets: 123 })
+ t.end()
+})
+
+t.test('projectScope', t => {
+ const obj = { scope: 'asdf' }
+ const flat = {}
+ definitions.scope.flatten('scope', obj, flat)
+ t.strictSame(flat, { projectScope: '@asdf' }, 'prepend @ if needed')
+
+ obj.scope = '@asdf'
+ definitions.scope.flatten('scope', obj, flat)
+ t.strictSame(flat, { projectScope: '@asdf' }, 'leave untouched if has @')
+
+ t.end()
+})
+
+t.test('strictSSL', t => {
+ const obj = { 'strict-ssl': false }
+ const flat = {}
+ definitions['strict-ssl'].flatten('strict-ssl', obj, flat)
+ t.strictSame(flat, { strictSSL: false })
+ obj['strict-ssl'] = true
+ definitions['strict-ssl'].flatten('strict-ssl', obj, flat)
+ t.strictSame(flat, { strictSSL: true })
+ t.end()
+})
+
+t.test('shrinkwrap/package-lock', t => {
+ const obj = { shrinkwrap: false }
+ const flat = {}
+ definitions.shrinkwrap.flatten('shrinkwrap', obj, flat)
+ t.strictSame(flat, {packageLock: false})
+ obj.shrinkwrap = true
+ definitions.shrinkwrap.flatten('shrinkwrap', obj, flat)
+ t.strictSame(flat, {packageLock: true})
+
+ delete obj.shrinkwrap
+ obj['package-lock'] = false
+ definitions['package-lock'].flatten('package-lock', obj, flat)
+ t.strictSame(flat, {packageLock: false})
+ obj['package-lock'] = true
+ definitions['package-lock'].flatten('package-lock', obj, flat)
+ t.strictSame(flat, {packageLock: true})
+
+ t.end()
+})
+
+t.test('scriptShell', t => {
+ const obj = { 'script-shell': null }
+ const flat = {}
+ definitions['script-shell'].flatten('script-shell', obj, flat)
+ t.ok(Object.prototype.hasOwnProperty.call(flat, 'scriptShell'),
+ 'should set it to undefined explicitly')
+ t.strictSame(flat, { scriptShell: undefined }, 'no other fields')
+
+ obj['script-shell'] = 'asdf'
+ definitions['script-shell'].flatten('script-shell', obj, flat)
+ t.strictSame(flat, { scriptShell: 'asdf' }, 'sets if not falsey')
+
+ t.end()
+})
+
+t.test('defaultTag', t => {
+ const obj = { tag: 'next' }
+ const flat = {}
+ definitions.tag.flatten('tag', obj, flat)
+ t.strictSame(flat, {defaultTag: 'next'})
+ t.end()
+})
+
+t.test('timeout', t => {
+ const obj = { 'fetch-timeout': 123 }
+ const flat = {}
+ definitions['fetch-timeout'].flatten('fetch-timeout', obj, flat)
+ t.strictSame(flat, {timeout: 123})
+ t.end()
+})
+
+t.test('saveType', t => {
+ t.test('save-prod', t => {
+ const obj = { 'save-prod': false }
+ const flat = {}
+ definitions['save-prod'].flatten('save-prod', obj, flat)
+ t.strictSame(flat, {}, 'no effect if false and missing')
+ flat.saveType = 'prod'
+ definitions['save-prod'].flatten('save-prod', obj, flat)
+ t.strictSame(flat, {}, 'remove if false and set to prod')
+ flat.saveType = 'dev'
+ definitions['save-prod'].flatten('save-prod', obj, flat)
+ t.strictSame(flat, {saveType: 'dev'}, 'ignore if false and not already prod')
+ obj['save-prod'] = true
+ definitions['save-prod'].flatten('save-prod', obj, flat)
+ t.strictSame(flat, {saveType: 'prod'}, 'set to prod if true')
+ t.end()
+ })
+
+ t.test('save-dev', t => {
+ const obj = { 'save-dev': false }
+ const flat = {}
+ definitions['save-dev'].flatten('save-dev', obj, flat)
+ t.strictSame(flat, {}, 'no effect if false and missing')
+ flat.saveType = 'dev'
+ definitions['save-dev'].flatten('save-dev', obj, flat)
+ t.strictSame(flat, {}, 'remove if false and set to dev')
+ flat.saveType = 'prod'
+ obj['save-dev'] = false
+ definitions['save-dev'].flatten('save-dev', obj, flat)
+ t.strictSame(flat, {saveType: 'prod'}, 'ignore if false and not already dev')
+ obj['save-dev'] = true
+ definitions['save-dev'].flatten('save-dev', obj, flat)
+ t.strictSame(flat, {saveType: 'dev'}, 'set to dev if true')
+ t.end()
+ })
+
+ t.test('save-bundle', t => {
+ const obj = { 'save-bundle': true }
+ const flat = {}
+ definitions['save-bundle'].flatten('save-bundle', obj, flat)
+ t.strictSame(flat, {saveBundle: true}, 'set the saveBundle flag')
+
+ obj['save-bundle'] = false
+ definitions['save-bundle'].flatten('save-bundle', obj, flat)
+ t.strictSame(flat, {saveBundle: false}, 'unset the saveBundle flag')
+
+ obj['save-bundle'] = true
+ obj['save-peer'] = true
+ definitions['save-bundle'].flatten('save-bundle', obj, flat)
+ t.strictSame(flat, {saveBundle: false}, 'false if save-peer is set')
+
+ t.end()
+ })
+
+ t.test('save-peer', t => {
+ const obj = { 'save-peer': false}
+ const flat = {}
+ definitions['save-peer'].flatten('save-peer', obj, flat)
+ t.strictSame(flat, {}, 'no effect if false and not yet set')
+
+ obj['save-peer'] = true
+ definitions['save-peer'].flatten('save-peer', obj, flat)
+ t.strictSame(flat, {saveType: 'peer'}, 'set saveType to peer if unset')
+
+ flat.saveType = 'optional'
+ definitions['save-peer'].flatten('save-peer', obj, flat)
+ t.strictSame(flat, {saveType: 'peerOptional'}, 'set to peerOptional if optional already')
+
+ definitions['save-peer'].flatten('save-peer', obj, flat)
+ t.strictSame(flat, {saveType: 'peerOptional'}, 'no effect if already peerOptional')
+
+ obj['save-peer'] = false
+ definitions['save-peer'].flatten('save-peer', obj, flat)
+ t.strictSame(flat, {saveType: 'optional'}, 'switch peerOptional to optional if false')
+
+ obj['save-peer'] = false
+ flat.saveType = 'peer'
+ definitions['save-peer'].flatten('save-peer', obj, flat)
+ t.strictSame(flat, {}, 'remove saveType if peer and setting false')
+
+ t.end()
+ })
+
+ t.test('save-optional', t => {
+ const obj = { 'save-optional': false}
+ const flat = {}
+ definitions['save-optional'].flatten('save-optional', obj, flat)
+ t.strictSame(flat, {}, 'no effect if false and not yet set')
+
+ obj['save-optional'] = true
+ definitions['save-optional'].flatten('save-optional', obj, flat)
+ t.strictSame(flat, {saveType: 'optional'}, 'set saveType to optional if unset')
+
+ flat.saveType = 'peer'
+ definitions['save-optional'].flatten('save-optional', obj, flat)
+ t.strictSame(flat, {saveType: 'peerOptional'}, 'set to peerOptional if peer already')
+
+ definitions['save-optional'].flatten('save-optional', obj, flat)
+ t.strictSame(flat, {saveType: 'peerOptional'}, 'no effect if already peerOptional')
+
+ obj['save-optional'] = false
+ definitions['save-optional'].flatten('save-optional', obj, flat)
+ t.strictSame(flat, {saveType: 'peer'}, 'switch peerOptional to peer if false')
+
+ flat.saveType = 'optional'
+ definitions['save-optional'].flatten('save-optional', obj, flat)
+ t.strictSame(flat, {}, 'remove saveType if optional and setting false')
+
+ t.end()
+ })
+
+ t.end()
+})
+
+t.test('cafile -> flat.ca', t => {
+ const path = t.testdir({
+ cafile: `
+-----BEGIN CERTIFICATE-----
+XXXX
+XXXX
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+YYYY\r
+YYYY\r
+-----END CERTIFICATE-----
+`,
+ })
+ const cafile = resolve(path, 'cafile')
+
+ const obj = {}
+ const flat = {}
+ definitions.cafile.flatten('cafile', obj, flat)
+ t.strictSame(flat, {}, 'no effect if no cafile set')
+ obj.cafile = resolve(path, 'no/cafile/here')
+ definitions.cafile.flatten('cafile', obj, flat)
+ t.strictSame(flat, {}, 'no effect if cafile not found')
+ obj.cafile = cafile
+ definitions.cafile.flatten('cafile', obj, flat)
+ t.strictSame(flat, {
+ ca: [
+ '-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----',
+ '-----BEGIN CERTIFICATE-----\nYYYY\nYYYY\n-----END CERTIFICATE-----',
+ ],
+ })
+ t.test('error other than ENOENT gets thrown', t => {
+ const poo = new Error('poo')
+ const defnReadFileThrows = requireInject(defpath, {
+ fs: {
+ ...require('fs'),
+ readFileSync: () => {
+ throw poo
+ },
+ },
+ })
+ t.throws(() => defnReadFileThrows.cafile.flatten('cafile', obj, {}), poo)
+ t.end()
+ })
+
+ t.end()
+})
+
+t.test('detect CI', t => {
+ const defnNoCI = requireInject(defpath, {
+ '@npmcli/ci-detect': () => false,
+ })
+ const defnCIFoo = requireInject(defpath, {
+ '@npmcli/ci-detect': () => 'foo',
+ })
+ t.equal(defnNoCI['ci-name'].default, null, 'null when not in CI')
+ t.equal(defnCIFoo['ci-name'].default, 'foo', 'name of CI when in CI')
+ t.end()
+})
+
+t.test('user-agent', t => {
+ const obj = {
+ 'user-agent': definitions['user-agent'].default,
+ 'npm-version': '1.2.3',
+ 'node-version': '9.8.7',
+ }
+ const flat = {}
+ const expectNoCI = `npm/1.2.3 node/9.8.7 ` +
+ `${process.platform} ${process.arch}`
+ definitions['user-agent'].flatten('user-agent', obj, flat)
+ t.equal(flat.userAgent, expectNoCI)
+ obj['ci-name'] = 'foo'
+ const expectCI = `${expectNoCI} ci/foo`
+ definitions['user-agent'].flatten('user-agent', obj, flat)
+ t.equal(flat.userAgent, expectCI)
+ t.end()
+})
diff --git a/test/lib/utils/config/describe-all.js b/test/lib/utils/config/describe-all.js
new file mode 100644
index 000000000..814d92ac9
--- /dev/null
+++ b/test/lib/utils/config/describe-all.js
@@ -0,0 +1,6 @@
+const t = require('tap')
+const describeAll = require('../../../../lib/utils/config/describe-all.js')
+// this basically ends up being a duplicate of the helpdoc dumped into
+// a snapshot, but it verifies that we get the same help output on every
+// platform where we run CI.
+t.matchSnapshot(describeAll())
diff --git a/test/lib/utils/config/flatten.js b/test/lib/utils/config/flatten.js
new file mode 100644
index 000000000..9fac0820c
--- /dev/null
+++ b/test/lib/utils/config/flatten.js
@@ -0,0 +1,34 @@
+const t = require('tap')
+const flatten = require('../../../../lib/utils/config/flatten.js')
+
+require.main.filename = '/path/to/npm'
+delete process.env.NODE
+process.execPath = '/path/to/node'
+
+const obj = {
+ 'save-dev': true,
+ '@foobar:registry': 'https://foo.bar.com/',
+ '//foo.bar.com:_authToken': 'foobarbazquuxasdf',
+ userconfig: '/path/to/.npmrc',
+}
+
+const flat = flatten(obj)
+t.strictSame(flat, {
+ saveType: 'dev',
+ '@foobar:registry': 'https://foo.bar.com/',
+ '//foo.bar.com:_authToken': 'foobarbazquuxasdf',
+ npmBin: '/path/to/npm',
+ nodeBin: '/path/to/node',
+ hashAlgorithm: 'sha1',
+})
+
+// now flatten something else on top of it.
+process.env.NODE = '/usr/local/bin/node.exe'
+flatten({ 'save-dev': false }, flat)
+t.strictSame(flat, {
+ '@foobar:registry': 'https://foo.bar.com/',
+ '//foo.bar.com:_authToken': 'foobarbazquuxasdf',
+ npmBin: '/path/to/npm',
+ nodeBin: '/usr/local/bin/node.exe',
+ hashAlgorithm: 'sha1',
+})
diff --git a/test/lib/utils/config/index.js b/test/lib/utils/config/index.js
new file mode 100644
index 000000000..75d72e784
--- /dev/null
+++ b/test/lib/utils/config/index.js
@@ -0,0 +1,24 @@
+const t = require('tap')
+const config = require('../../../../lib/utils/config/index.js')
+const flatten = require('../../../../lib/utils/config/flatten.js')
+const definitions = require('../../../../lib/utils/config/definitions.js')
+const describeAll = require('../../../../lib/utils/config/describe-all.js')
+t.matchSnapshot(config.shorthands, 'shorthands')
+
+// just spot check a few of these to show that we got defaults assembled
+t.match(config.defaults, {
+ registry: definitions.registry.default,
+ 'init-module': definitions['init-module'].default,
+})
+
+// is a getter, so changes are reflected
+definitions.registry.default = 'https://example.com'
+t.strictSame(config.defaults.registry, 'https://example.com')
+
+t.strictSame(config, {
+ defaults: config.defaults,
+ shorthands: config.shorthands,
+ flatten,
+ definitions,
+ describeAll,
+})