diff --git a/lib/utils/config/definition.js b/lib/utils/config/definition.js new file mode 100644 index 000000000000..b6323a51259d --- /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 000000000000..1ad3a166bdf8 --- /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=\`. + + 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 + + `, + 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 000000000000..ab3f3a63ea75 --- /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 000000000000..f6d6124bddf7 --- /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 000000000000..a24f5865242b --- /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 000000000000..ad506ae8e358 --- /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 000000000000..3e82da69913c --- /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 000000000000..ca758c701d17 --- /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=\`. + +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 + +* 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 000000000000..1044b3063c4b --- /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 000000000000..25530f723713 --- /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 000000000000..3169feefb8f9 --- /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 = {} + // : flat.retry[