This commit is contained in:
2025-08-18 23:06:34 +08:00
parent 0bc04fb659
commit ed18af0cad
1926 changed files with 275098 additions and 0 deletions

15
package/node_modules/@npmcli/config/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) npm, Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

257
package/node_modules/@npmcli/config/README.md generated vendored Normal file
View File

@@ -0,0 +1,257 @@
# `@npmcli/config`
Configuration management for the npm cli.
This module is the spiritual descendant of
[`npmconf`](http://npm.im/npmconf), and the code that once lived in npm's
`lib/config/` folder.
It does the management of configuration files that npm uses, but
importantly, does _not_ define all the configuration defaults or types, as
those parts make more sense to live within the npm CLI itself.
The only exceptions:
- The `prefix` config value has some special semantics, setting the local
prefix if specified on the CLI options and not in global mode, or the
global prefix otherwise.
- The `project` config file is loaded based on the local prefix (which can
only be set by the CLI config options, and otherwise defaults to a walk
up the folder tree to the first parent containing a `node_modules`
folder, `package.json` file, or `package-lock.json` file.)
- The `userconfig` value, as set by the environment and CLI (defaulting to
`~/.npmrc`, is used to load user configs.
- The `globalconfig` value, as set by the environment, CLI, and
`userconfig` file (defaulting to `$PREFIX/etc/npmrc`) is used to load
global configs.
- A `builtin` config, read from a `npmrc` file in the root of the npm
project itself, overrides all defaults.
The resulting hierarchy of configs:
- CLI switches. eg `--some-key=some-value` on the command line. These are
parsed by [`nopt`](http://npm.im/nopt), which is not a great choice, but
it's the one that npm has used forever, and changing it will be
difficult.
- Environment variables. eg `npm_config_some_key=some_value` in the
environment. There is no way at this time to modify this prefix.
- INI-formatted project configs. eg `some-key = some-value` in the
`localPrefix` folder (ie, the `cwd`, or its nearest parent that contains
either a `node_modules` folder or `package.json` file.)
- INI-formatted userconfig file. eg `some-key = some-value` in `~/.npmrc`.
The `userconfig` config value can be overridden by the `cli`, `env`, or
`project` configs to change this value.
- INI-formatted globalconfig file. eg `some-key = some-value` in
the `globalPrefix` folder, which is inferred by looking at the location
of the node executable, or the `prefix` setting in the `cli`, `env`,
`project`, or `userconfig`. The `globalconfig` value at any of those
levels can override this.
- INI-formatted builtin config file. eg `some-key = some-value` in
`/usr/local/lib/node_modules/npm/npmrc`. This is not configurable, and
is determined by looking in the `npmPath` folder.
- Default values (passed in by npm when it loads this module).
## USAGE
```js
const Config = require('@npmcli/config')
const { shorthands, definitions, flatten } = require('@npmcli/config/lib/definitions')
const conf = new Config({
// path to the npm module being run
npmPath: resolve(__dirname, '..'),
definitions,
shorthands,
flatten,
// optional, defaults to process.argv
// argv: [] <- if you are using this package in your own cli
// and dont want to have colliding argv
argv: process.argv,
// optional, defaults to process.env
env: process.env,
// optional, defaults to process.execPath
execPath: process.execPath,
// optional, defaults to process.platform
platform: process.platform,
// optional, defaults to process.cwd()
cwd: process.cwd(),
})
// emits log events on the process object
// see `proc-log` for more info
process.on('log', (level, ...args) => {
console.log(level, ...args)
})
// returns a promise that fails if config loading fails, and
// resolves when the config object is ready for action
conf.load().then(() => {
conf.validate()
console.log('loaded ok! some-key = ' + conf.get('some-key'))
}).catch(er => {
console.error('error loading configs!', er)
})
```
## API
The `Config` class is the sole export.
```js
const Config = require('@npmcli/config')
```
### static `Config.typeDefs`
The type definitions passed to `nopt` for CLI option parsing and known
configuration validation.
### constructor `new Config(options)`
Options:
- `types` Types of all known config values. Note that some are effectively
given semantic value in the config loading process itself.
- `shorthands` An object mapping a shorthand value to an array of CLI
arguments that replace it.
- `defaults` Default values for each of the known configuration keys.
These should be defined for all configs given a type, and must be valid.
- `npmPath` The path to the `npm` module, for loading the `builtin` config
file.
- `cwd` Optional, defaults to `process.cwd()`, used for inferring the
`localPrefix` and loading the `project` config.
- `platform` Optional, defaults to `process.platform`. Used when inferring
the `globalPrefix` from the `execPath`, since this is done diferently on
Windows.
- `execPath` Optional, defaults to `process.execPath`. Used to infer the
`globalPrefix`.
- `env` Optional, defaults to `process.env`. Source of the environment
variables for configuration.
- `argv` Optional, defaults to `process.argv`. Source of the CLI options
used for configuration.
Returns a `config` object, which is not yet loaded.
Fields:
- `config.globalPrefix` The prefix for `global` operations. Set by the
`prefix` config value, or defaults based on the location of the
`execPath` option.
- `config.localPrefix` The prefix for `local` operations. Set by the
`prefix` config value on the CLI only, or defaults to either the `cwd` or
its nearest ancestor containing a `node_modules` folder or `package.json`
file.
- `config.sources` A read-only `Map` of the file (or a comment, if no file
found, or relevant) to the config level loaded from that source.
- `config.data` A `Map` of config level to `ConfigData` objects. These
objects should not be modified directly under any circumstances.
- `source` The source where this data was loaded from.
- `raw` The raw data used to generate this config data, as it was parsed
initially from the environment, config file, or CLI options.
- `data` The data object reflecting the inheritance of configs up to this
point in the chain.
- `loadError` Any errors encountered that prevented the loading of this
config data.
- `config.list` A list sorted in priority of all the config data objects in
the prototype chain. `config.list[0]` is the `cli` level,
`config.list[1]` is the `env` level, and so on.
- `cwd` The `cwd` param
- `env` The `env` param
- `argv` The `argv` param
- `execPath` The `execPath` param
- `platform` The `platform` param
- `defaults` The `defaults` param
- `shorthands` The `shorthands` param
- `types` The `types` param
- `npmPath` The `npmPath` param
- `globalPrefix` The effective `globalPrefix`
- `localPrefix` The effective `localPrefix`
- `prefix` If `config.get('global')` is true, then `globalPrefix`,
otherwise `localPrefix`
- `home` The user's home directory, found by looking at `env.HOME` or
calling `os.homedir()`.
- `loaded` A boolean indicating whether or not configs are loaded
- `valid` A getter that returns `true` if all the config objects are valid.
Any data objects that have been modified with `config.set(...)` will be
re-evaluated when `config.valid` is read.
### `config.load()`
Load configuration from the various sources of information.
Returns a `Promise` that resolves when configuration is loaded, and fails
if a fatal error is encountered.
### `config.find(key)`
Find the effective place in the configuration levels a given key is set.
Returns one of: `cli`, `env`, `project`, `user`, `global`, `builtin`, or
`default`.
Returns `null` if the key is not set.
### `config.get(key, where = 'cli')`
Load the given key from the config stack.
### `config.set(key, value, where = 'cli')`
Set the key to the specified value, at the specified level in the config
stack.
### `config.delete(key, where = 'cli')`
Delete the configuration key from the specified level in the config stack.
### `config.validate(where)`
Verify that all known configuration options are set to valid values, and
log a warning if they are invalid.
Invalid auth options will cause this method to throw an error with a `code`
property of `ERR_INVALID_AUTH`, and a `problems` property listing the specific
concerns with the current configuration.
If `where` is not set, then all config objects are validated.
Returns `true` if all configs are valid.
Note that it's usually enough (and more efficient) to just check
`config.valid`, since each data object is marked for re-evaluation on every
`config.set()` operation.
### `config.repair(problems)`
Accept an optional array of problems (as thrown by `config.validate()`) and
perform the necessary steps to resolve them. If no problems are provided,
this method will call `config.validate()` internally to retrieve them.
Note that you must `await config.save('user')` in order to persist the changes.
### `config.isDefault(key)`
Returns `true` if the value is coming directly from the
default definitions, if the current value for the key config is
coming from any other source, returns `false`.
This method can be used for avoiding or tweaking default values, e.g:
> Given a global default definition of foo='foo' it's possible to read that
> value such as:
>
> ```js
> const save = config.get('foo')
> ```
>
> Now in a different place of your app it's possible to avoid using the `foo`
> default value, by checking to see if the current config value is currently
> one that was defined by the default definitions:
>
> ```js
> const save = config.isDefault('foo') ? 'bar' : config.get('foo')
> ```
### `config.save(where)`
Save the config file specified by the `where` param. Must be one of
`project`, `user`, `global`, `builtin`.

View File

@@ -0,0 +1,253 @@
// 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',
'defaultDescription',
'deprecated',
'description',
'exclusive',
'flatten',
'hint',
'key',
'short',
'type',
'typeDescription',
'usage',
'envExport',
]
const {
semver: { type: semver },
Umask: { type: Umask },
url: { type: url },
path: { type: path },
} = require('../type-defs.js')
class Definition {
constructor (key, def) {
this.key = key
// if it's set falsey, don't export it, otherwise we do by default
this.envExport = true
Object.assign(this, def)
this.validate()
if (!this.defaultDescription) {
this.defaultDescription = describeValue(this.default)
}
if (!this.typeDescription) {
this.typeDescription = describeType(this.type)
}
// hint is only used for non-boolean values
if (!this.hint) {
if (this.type === Number) {
this.hint = '<number>'
} else {
this.hint = `<${this.key}>`
}
}
if (!this.usage) {
this.usage = describeUsage(this)
}
}
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 noEnvExport = this.envExport
? ''
: `
This value is not exported to the environment for child processes.
`
const deprecated = !this.deprecated ? '' : `* DEPRECATED: ${unindent(this.deprecated)}\n`
/* eslint-disable-next-line max-len */
const exclusive = !this.exclusive ? '' : `\nThis config can not be used with: \`${this.exclusive.join('`, `')}\``
return wrapAll(`#### \`${this.key}\`
* Default: ${unindent(this.defaultDescription)}
* Type: ${unindent(this.typeDescription)}
${deprecated}
${description}
${exclusive}
${noEnvExport}`)
}
}
const describeUsage = def => {
let key = ''
// Single type
if (!Array.isArray(def.type)) {
if (def.short) {
key = `-${def.short}|`
}
if (def.type === Boolean && def.default !== false) {
key = `${key}--no-${def.key}`
} else {
key = `${key}--${def.key}`
}
if (def.type !== Boolean) {
key = `${key} ${def.hint}`
}
return key
}
key = `--${def.key}`
if (def.short) {
key = `-${def.short}|--${def.key}`
}
// Multiple types
let types = def.type
const multiple = types.includes(Array)
const bool = types.includes(Boolean)
// null type means optional and doesn't currently affect usage output since
// all non-optional params have defaults so we render everything as optional
types = types.filter(t => t !== null && t !== Array && t !== Boolean)
if (!types.length) {
return key
}
let description
if (!types.some(t => typeof t !== 'string')) {
// Specific values, use specifics given
description = `<${types.filter(d => d).join('|')}>`
} else {
// Generic values, use hint
description = def.hint
}
if (bool) {
// Currently none of our multi-type configs with boolean values default to
// false so all their hints should show `--no-`, if we ever add ones that
// default to false we can branch the logic here
key = `--no-${def.key}|${key}`
}
const usage = `${key} ${description}`
if (multiple) {
return `${usage} [${usage} ...]`
} else {
return usage
}
}
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
.slice(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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
const definitions = require('./definitions.js')
// 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 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
}
}
return flat
}
const definitionProps = Object.entries(definitions)
.reduce((acc, [key, { short = [], default: d }]) => {
// can be either an array or string
for (const s of [].concat(short)) {
acc.shorthands[s] = [`--${key}`]
}
acc.defaults[key] = d
return acc
}, { shorthands: {}, defaults: {} })
// 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'],
iwr: ['--include-workspace-root'],
...definitionProps.shorthands,
}
module.exports = {
defaults: definitionProps.defaults,
definitions,
flatten,
shorthands,
}

14
package/node_modules/@npmcli/config/lib/env-replace.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// replace any ${ENV} values with the appropriate environ.
const envExpr = /(?<!\\)(\\*)\$\{([^${}]+)\}/g
module.exports = (f, env) => f.replace(envExpr, (orig, esc, name) => {
const val = env[name] !== undefined ? env[name] : `$\{${name}}`
// consume the escape chars that are relevant.
if (esc.length % 2) {
return orig.slice((esc.length + 1) / 2)
}
return (esc.slice(esc.length / 2)) + val
})

23
package/node_modules/@npmcli/config/lib/errors.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
'use strict'
class ErrInvalidAuth extends Error {
constructor (problems) {
let message = 'Invalid auth configuration found: '
message += problems.map((problem) => {
// istanbul ignore else
if (problem.action === 'delete') {
return `\`${problem.key}\` is not allowed in ${problem.where} config`
} else if (problem.action === 'rename') {
return `\`${problem.from}\` must be renamed to \`${problem.to}\` in ${problem.where} config`
}
}).join(', ')
message += '\nPlease run `npm config fix` to repair your configuration.`'
super(message)
this.code = 'ERR_INVALID_AUTH'
this.problems = problems
}
}
module.exports = {
ErrInvalidAuth,
}

931
package/node_modules/@npmcli/config/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,931 @@
// TODO: set the scope config from package.json or explicit cli config
const { walkUp } = require('walk-up-path')
const ini = require('ini')
const nopt = require('nopt')
const { log, time } = require('proc-log')
const { resolve, dirname, join } = require('node:path')
const { homedir } = require('node:os')
const {
readFile,
writeFile,
chmod,
unlink,
stat,
mkdir,
} = require('node:fs/promises')
const fileExists = (...p) => stat(resolve(...p))
.then((st) => st.isFile())
.catch(() => false)
const dirExists = (...p) => stat(resolve(...p))
.then((st) => st.isDirectory())
.catch(() => false)
const hasOwnProperty = (obj, key) =>
Object.prototype.hasOwnProperty.call(obj, key)
const typeDefs = require('./type-defs.js')
const nerfDart = require('./nerf-dart.js')
const envReplace = require('./env-replace.js')
const parseField = require('./parse-field.js')
const setEnvs = require('./set-envs.js')
// types that can be saved back to
const confFileTypes = new Set([
'global',
'user',
'project',
])
const confTypes = new Set([
'default',
'builtin',
...confFileTypes,
'env',
'cli',
])
class Config {
#loaded = false
#flatten
// populated the first time we flatten the object
#flatOptions = null
static get typeDefs () {
return typeDefs
}
constructor ({
definitions,
shorthands,
flatten,
npmPath,
// options just to override in tests, mostly
env = process.env,
argv = process.argv,
platform = process.platform,
execPath = process.execPath,
cwd = process.cwd(),
excludeNpmCwd = false,
}) {
// turn the definitions into nopt's weirdo syntax
this.definitions = definitions
const types = {}
const defaults = {}
this.deprecated = {}
for (const [key, def] of Object.entries(definitions)) {
defaults[key] = def.default
types[key] = def.type
if (def.deprecated) {
this.deprecated[key] = def.deprecated.trim().replace(/\n +/, '\n')
}
}
this.#flatten = flatten
this.types = types
this.shorthands = shorthands
this.defaults = defaults
this.npmPath = npmPath
this.npmBin = join(this.npmPath, 'bin/npm-cli.js')
this.argv = argv
this.env = env
this.execPath = execPath
this.platform = platform
this.cwd = cwd
this.excludeNpmCwd = excludeNpmCwd
// set when we load configs
this.globalPrefix = null
this.localPrefix = null
this.localPackage = null
// defaults to env.HOME, but will always be *something*
this.home = null
// set up the prototype chain of config objects
const wheres = [...confTypes]
this.data = new Map()
let parent = null
for (const where of wheres) {
this.data.set(where, parent = new ConfigData(parent))
}
this.data.set = () => {
throw new Error('cannot change internal config data structure')
}
this.data.delete = () => {
throw new Error('cannot change internal config data structure')
}
this.sources = new Map([])
this.list = []
for (const { data } of this.data.values()) {
this.list.unshift(data)
}
Object.freeze(this.list)
this.#loaded = false
}
get loaded () {
return this.#loaded
}
get prefix () {
return this.#get('global') ? this.globalPrefix : this.localPrefix
}
// return the location where key is found.
find (key) {
if (!this.loaded) {
throw new Error('call config.load() before reading values')
}
// have to look in reverse order
const entries = [...this.data.entries()]
for (let i = entries.length - 1; i > -1; i--) {
const [where, { data }] = entries[i]
if (hasOwnProperty(data, key)) {
return where
}
}
return null
}
get (key, where) {
if (!this.loaded) {
throw new Error('call config.load() before reading values')
}
return this.#get(key, where)
}
// we need to get values sometimes, so use this internal one to do so
// while in the process of loading.
#get (key, where = null) {
if (where !== null && !confTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
const { data } = this.data.get(where || 'cli')
return where === null || hasOwnProperty(data, key) ? data[key] : undefined
}
set (key, val, where = 'cli') {
if (!this.loaded) {
throw new Error('call config.load() before setting values')
}
if (!confTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
this.#checkDeprecated(key)
const { data, raw } = this.data.get(where)
data[key] = val
if (['global', 'user', 'project'].includes(where)) {
raw[key] = val
}
// this is now dirty, the next call to this.valid will have to check it
this.data.get(where)[_valid] = null
// the flat options are invalidated, regenerate next time they're needed
this.#flatOptions = null
}
get flat () {
if (this.#flatOptions) {
return this.#flatOptions
}
// create the object for flat options passed to deps
const timeEnd = time.start('config:load:flatten')
this.#flatOptions = {}
// walk from least priority to highest
for (const { data } of this.data.values()) {
this.#flatten(data, this.#flatOptions)
}
this.#flatOptions.nodeBin = this.execPath
this.#flatOptions.npmBin = this.npmBin
timeEnd()
return this.#flatOptions
}
delete (key, where = 'cli') {
if (!this.loaded) {
throw new Error('call config.load() before deleting values')
}
if (!confTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
const { data, raw } = this.data.get(where)
delete data[key]
if (['global', 'user', 'project'].includes(where)) {
delete raw[key]
}
}
async load () {
if (this.loaded) {
throw new Error('attempting to load npm config multiple times')
}
// first load the defaults, which sets the global prefix
this.loadDefaults()
// next load the builtin config, as this sets new effective defaults
await this.loadBuiltinConfig()
// cli and env are not async, and can set the prefix, relevant to project
this.loadCLI()
this.loadEnv()
// next project config, which can affect userconfig location
await this.loadProjectConfig()
// then user config, which can affect globalconfig location
await this.loadUserConfig()
// last but not least, global config file
await this.loadGlobalConfig()
// set this before calling setEnvs, so that we don't have to share
// private attributes, as that module also does a bunch of get operations
this.#loaded = true
// set proper globalPrefix now that everything is loaded
this.globalPrefix = this.get('prefix')
this.setEnvs()
}
loadDefaults () {
this.loadGlobalPrefix()
this.loadHome()
const defaultsObject = {
...this.defaults,
prefix: this.globalPrefix,
}
try {
defaultsObject['npm-version'] = require(join(this.npmPath, 'package.json')).version
} catch {
// in some weird state where the passed in npmPath does not have a package.json
// this will never happen in npm, but is guarded here in case this is consumed
// in other ways + tests
}
this.#loadObject(defaultsObject, 'default', 'default values')
const { data } = this.data.get('default')
// if the prefix is set on cli, env, or userconfig, then we need to
// default the globalconfig file to that location, instead of the default
// global prefix. It's weird that `npm get globalconfig --prefix=/foo`
// returns `/foo/etc/npmrc`, but better to not change it at this point.
// define a custom getter, but turn into a normal prop
// if we set it. otherwise it can't be set on child objects
Object.defineProperty(data, 'globalconfig', {
get: () => resolve(this.#get('prefix'), 'etc/npmrc'),
set (value) {
Object.defineProperty(data, 'globalconfig', {
value,
configurable: true,
writable: true,
enumerable: true,
})
},
configurable: true,
enumerable: true,
})
}
loadHome () {
this.home = this.env.HOME || homedir()
}
loadGlobalPrefix () {
if (this.globalPrefix) {
throw new Error('cannot load default global prefix more than once')
}
if (this.env.PREFIX) {
this.globalPrefix = this.env.PREFIX
} else if (this.platform === 'win32') {
// c:\node\node.exe --> prefix=c:\node\
this.globalPrefix = dirname(this.execPath)
} else {
// /usr/local/bin/node --> prefix=/usr/local
this.globalPrefix = dirname(dirname(this.execPath))
// destdir only is respected on Unix
if (this.env.DESTDIR) {
this.globalPrefix = join(this.env.DESTDIR, this.globalPrefix)
}
}
}
loadEnv () {
const conf = Object.create(null)
for (const [envKey, envVal] of Object.entries(this.env)) {
if (!/^npm_config_/i.test(envKey) || envVal === '') {
continue
}
let key = envKey.slice('npm_config_'.length)
if (!key.startsWith('//')) { // don't normalize nerf-darted keys
key = key.replace(/(?!^)_/g, '-') // don't replace _ at the start of the key
.toLowerCase()
}
conf[key] = envVal
}
this.#loadObject(conf, 'env', 'environment')
}
loadCLI () {
nopt.invalidHandler = (k, val, type) =>
this.invalidHandler(k, val, type, 'command line options', 'cli')
const conf = nopt(this.types, this.shorthands, this.argv)
nopt.invalidHandler = null
this.parsedArgv = conf.argv
delete conf.argv
this.#loadObject(conf, 'cli', 'command line options')
}
get valid () {
for (const [where, { valid }] of this.data.entries()) {
if (valid === false || valid === null && !this.validate(where)) {
return false
}
}
return true
}
validate (where) {
if (!where) {
let valid = true
const authProblems = []
for (const entryWhere of this.data.keys()) {
// no need to validate our defaults, we know they're fine
// cli was already validated when parsed the first time
if (entryWhere === 'default' || entryWhere === 'builtin' || entryWhere === 'cli') {
continue
}
const ret = this.validate(entryWhere)
valid = valid && ret
if (['global', 'user', 'project'].includes(entryWhere)) {
// after validating everything else, we look for old auth configs we no longer support
// if these keys are found, we build up a list of them and the appropriate action and
// attach it as context on the thrown error
// first, keys that should be removed
for (const key of ['_authtoken', '-authtoken']) {
if (this.get(key, entryWhere)) {
authProblems.push({ action: 'delete', key, where: entryWhere })
}
}
// NOTE we pull registry without restricting to the current 'where' because we want to
// suggest scoping things to the registry they would be applied to, which is the default
// regardless of where it was defined
const nerfedReg = nerfDart(this.get('registry'))
// keys that should be nerfed but currently are not
for (const key of ['_auth', '_authToken', 'username', '_password']) {
if (this.get(key, entryWhere)) {
// username and _password must both exist in the same file to be recognized correctly
if (key === 'username' && !this.get('_password', entryWhere)) {
authProblems.push({ action: 'delete', key, where: entryWhere })
} else if (key === '_password' && !this.get('username', entryWhere)) {
authProblems.push({ action: 'delete', key, where: entryWhere })
} else {
authProblems.push({
action: 'rename',
from: key,
to: `${nerfedReg}:${key}`,
where: entryWhere,
})
}
}
}
}
}
if (authProblems.length) {
const { ErrInvalidAuth } = require('./errors.js')
throw new ErrInvalidAuth(authProblems)
}
return valid
} else {
const obj = this.data.get(where)
obj[_valid] = true
nopt.invalidHandler = (k, val, type) =>
this.invalidHandler(k, val, type, obj.source, where)
nopt.clean(obj.data, this.types, typeDefs)
nopt.invalidHandler = null
return obj[_valid]
}
}
// fixes problems identified by validate(), accepts the 'problems' property from a thrown
// ErrInvalidAuth to avoid having to check everything again
repair (problems) {
if (!problems) {
try {
this.validate()
} catch (err) {
// coverage skipped here because we don't need to test re-throwing an error
// istanbul ignore next
if (err.code !== 'ERR_INVALID_AUTH') {
throw err
}
problems = err.problems
} finally {
if (!problems) {
problems = []
}
}
}
for (const problem of problems) {
// coverage disabled for else branch because it doesn't do anything and shouldn't
// istanbul ignore else
if (problem.action === 'delete') {
this.delete(problem.key, problem.where)
} else if (problem.action === 'rename') {
const raw = this.data.get(problem.where).raw?.[problem.from]
const calculated = this.get(problem.from, problem.where)
this.set(problem.to, raw || calculated, problem.where)
this.delete(problem.from, problem.where)
}
}
}
// Returns true if the value is coming directly from the source defined
// in default definitions, if the current value for the key config is
// coming from any other different source, returns false
isDefault (key) {
const [defaultType, ...types] = [...confTypes]
const defaultData = this.data.get(defaultType).data
return hasOwnProperty(defaultData, key)
&& types.every(type => {
const typeData = this.data.get(type).data
return !hasOwnProperty(typeData, key)
})
}
invalidHandler (k, val, type, source, where) {
const typeDescription = require('./type-description.js')
log.warn(
'invalid config',
k + '=' + JSON.stringify(val),
`set in ${source}`
)
this.data.get(where)[_valid] = false
if (Array.isArray(type)) {
if (type.includes(typeDefs.url.type)) {
type = typeDefs.url.type
} else {
/* istanbul ignore if - no actual configs matching this, but
* path types SHOULD be handled this way, like URLs, for the
* same reason */
if (type.includes(typeDefs.path.type)) {
type = typeDefs.path.type
}
}
}
const typeDesc = typeDescription(type)
const mustBe = typeDesc
.filter(m => m !== undefined && m !== Array)
const msg = 'Must be' + this.#getOneOfKeywords(mustBe, typeDesc)
const desc = mustBe.length === 1 ? mustBe[0]
: [...new Set(mustBe.map(n => typeof n === 'string' ? n : JSON.stringify(n)))].join(', ')
log.warn('invalid config', msg, desc)
}
#getOneOfKeywords (mustBe, typeDesc) {
let keyword
if (mustBe.length === 1 && typeDesc.includes(Array)) {
keyword = ' one or more'
} else if (mustBe.length > 1 && typeDesc.includes(Array)) {
keyword = ' one or more of:'
} else if (mustBe.length > 1) {
keyword = ' one of:'
} else {
keyword = ''
}
return keyword
}
#loadObject (obj, where, source, er = null) {
// obj is the raw data read from the file
const conf = this.data.get(where)
if (conf.source) {
const m = `double-loading "${where}" configs from ${source}, ` +
`previously loaded from ${conf.source}`
throw new Error(m)
}
if (this.sources.has(source)) {
const m = `double-loading config "${source}" as "${where}", ` +
`previously loaded as "${this.sources.get(source)}"`
throw new Error(m)
}
conf.source = source
this.sources.set(source, where)
if (er) {
conf.loadError = er
if (er.code !== 'ENOENT') {
log.verbose('config', `error loading ${where} config`, er)
}
} else {
conf.raw = obj
for (const [key, value] of Object.entries(obj)) {
const k = envReplace(key, this.env)
const v = this.parseField(value, k)
if (where !== 'default') {
this.#checkDeprecated(k)
if (this.definitions[key]?.exclusive) {
for (const exclusive of this.definitions[key].exclusive) {
if (!this.isDefault(exclusive)) {
throw new TypeError(`--${key} can not be provided when using --${exclusive}`)
}
}
}
}
conf.data[k] = v
}
}
}
#checkDeprecated (key) {
// XXX(npm9+) make this throw an error
if (this.deprecated[key]) {
log.warn('config', key, this.deprecated[key])
}
}
// Parse a field, coercing it to the best type available.
parseField (f, key, listElement = false) {
return parseField(f, key, this, listElement)
}
async #loadFile (file, type) {
// only catch the error from readFile, not from the loadObject call
log.silly('config', `load:file:${file}`)
await readFile(file, 'utf8').then(
data => {
const parsedConfig = ini.parse(data)
if (type === 'project' && parsedConfig.prefix) {
// Log error if prefix is mentioned in project .npmrc
/* eslint-disable-next-line max-len */
log.error('config', `prefix cannot be changed from project config: ${file}.`)
}
return this.#loadObject(parsedConfig, type, file)
},
er => this.#loadObject(null, type, file, er)
)
}
loadBuiltinConfig () {
return this.#loadFile(resolve(this.npmPath, 'npmrc'), 'builtin')
}
async loadProjectConfig () {
// the localPrefix can be set by the CLI config, but otherwise is
// found by walking up the folder tree. either way, we load it before
// we return to make sure localPrefix is set
await this.loadLocalPrefix()
// if we have not detected a local package json yet, try now that we
// have a local prefix
if (this.localPackage == null) {
this.localPackage = await fileExists(this.localPrefix, 'package.json')
}
if (this.#get('global') === true || this.#get('location') === 'global') {
this.data.get('project').source = '(global mode enabled, ignored)'
this.sources.set(this.data.get('project').source, 'project')
return
}
const projectFile = resolve(this.localPrefix, '.npmrc')
// if we're in the ~ directory, and there happens to be a node_modules
// folder (which is not TOO uncommon, it turns out), then we can end
// up loading the "project" config where the "userconfig" will be,
// which causes some calamaties. So, we only load project config if
// it doesn't match what the userconfig will be.
if (projectFile !== this.#get('userconfig')) {
return this.#loadFile(projectFile, 'project')
} else {
this.data.get('project').source = '(same as "user" config, ignored)'
this.sources.set(this.data.get('project').source, 'project')
}
}
async loadLocalPrefix () {
const cliPrefix = this.#get('prefix', 'cli')
if (cliPrefix) {
this.localPrefix = cliPrefix
return
}
const cliWorkspaces = this.#get('workspaces', 'cli')
const isGlobal = this.#get('global') || this.#get('location') === 'global'
for (const p of walkUp(this.cwd)) {
// HACK: this is an option set in tests to stop the local prefix from being set
// on tests that are created inside the npm repo
if (this.excludeNpmCwd && p === this.npmPath) {
break
}
const hasPackageJson = await fileExists(p, 'package.json')
if (!this.localPrefix && (hasPackageJson || await dirExists(p, 'node_modules'))) {
this.localPrefix = p
this.localPackage = hasPackageJson
// if workspaces are disabled, or we're in global mode, return now
if (cliWorkspaces === false || isGlobal) {
return
}
// otherwise, continue the loop
continue
}
if (this.localPrefix && hasPackageJson) {
const pkgJson = require('@npmcli/package-json')
// if we already set localPrefix but this dir has a package.json
// then we need to see if `p` is a workspace root by reading its package.json
// however, if reading it fails then we should just move on
const { content: pkg } = await pkgJson.normalize(p).catch(() => ({ content: {} }))
if (!pkg?.workspaces) {
continue
}
const mapWorkspaces = require('@npmcli/map-workspaces')
const workspaces = await mapWorkspaces({ cwd: p, pkg })
for (const w of workspaces.values()) {
if (w === this.localPrefix) {
// see if there's a .npmrc file in the workspace, if so log a warning
if (await fileExists(this.localPrefix, '.npmrc')) {
log.warn('config', `ignoring workspace config at ${this.localPrefix}/.npmrc`)
}
// set the workspace in the default layer, which allows it to be overridden easily
const { data } = this.data.get('default')
data.workspace = [this.localPrefix]
this.localPrefix = p
this.localPackage = hasPackageJson
log.info('config', `found workspace root at ${this.localPrefix}`)
// we found a root, so we return now
return
}
}
}
}
if (!this.localPrefix) {
this.localPrefix = this.cwd
}
}
loadUserConfig () {
return this.#loadFile(this.#get('userconfig'), 'user')
}
loadGlobalConfig () {
return this.#loadFile(this.#get('globalconfig'), 'global')
}
async save (where) {
if (!this.loaded) {
throw new Error('call config.load() before saving')
}
if (!confFileTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
const conf = this.data.get(where)
conf[_loadError] = null
if (where === 'user') {
// if email is nerfed, then we want to de-nerf it
const nerfed = nerfDart(this.get('registry'))
const email = this.get(`${nerfed}:email`, 'user')
if (email) {
this.delete(`${nerfed}:email`, 'user')
this.set('email', email, 'user')
}
}
// We need the actual raw data before we called parseField so that we are
// saving the same content back to the file
const iniData = ini.stringify(conf.raw).trim() + '\n'
if (!iniData.trim()) {
// ignore the unlink error (eg, if file doesn't exist)
await unlink(conf.source).catch(() => {})
return
}
const dir = dirname(conf.source)
await mkdir(dir, { recursive: true })
await writeFile(conf.source, iniData, 'utf8')
const mode = where === 'user' ? 0o600 : 0o666
await chmod(conf.source, mode)
}
clearCredentialsByURI (uri, level = 'user') {
const nerfed = nerfDart(uri)
const def = nerfDart(this.get('registry'))
if (def === nerfed) {
this.delete(`-authtoken`, level)
this.delete(`_authToken`, level)
this.delete(`_authtoken`, level)
this.delete(`_auth`, level)
this.delete(`_password`, level)
this.delete(`username`, level)
// de-nerf email if it's nerfed to the default registry
const email = this.get(`${nerfed}:email`, level)
if (email) {
this.set('email', email, level)
}
}
this.delete(`${nerfed}:_authToken`, level)
this.delete(`${nerfed}:_auth`, level)
this.delete(`${nerfed}:_password`, level)
this.delete(`${nerfed}:username`, level)
this.delete(`${nerfed}:email`, level)
this.delete(`${nerfed}:certfile`, level)
this.delete(`${nerfed}:keyfile`, level)
}
setCredentialsByURI (uri, { token, username, password, certfile, keyfile }) {
const nerfed = nerfDart(uri)
// field that hasn't been used as documented for a LONG time,
// and as of npm 7.10.0, isn't used at all. We just always
// send auth if we have it, only to the URIs under the nerf dart.
this.delete(`${nerfed}:always-auth`, 'user')
this.delete(`${nerfed}:email`, 'user')
if (certfile && keyfile) {
this.set(`${nerfed}:certfile`, certfile, 'user')
this.set(`${nerfed}:keyfile`, keyfile, 'user')
// cert/key may be used in conjunction with other credentials, thus no `else`
}
if (token) {
this.set(`${nerfed}:_authToken`, token, 'user')
this.delete(`${nerfed}:_password`, 'user')
this.delete(`${nerfed}:username`, 'user')
} else if (username || password) {
if (!username) {
throw new Error('must include username')
}
if (!password) {
throw new Error('must include password')
}
this.delete(`${nerfed}:_authToken`, 'user')
this.set(`${nerfed}:username`, username, 'user')
// note: not encrypted, no idea why we bothered to do this, but oh well
// protects against shoulder-hacks if password is memorable, I guess?
const encoded = Buffer.from(password, 'utf8').toString('base64')
this.set(`${nerfed}:_password`, encoded, 'user')
} else if (!certfile || !keyfile) {
throw new Error('No credentials to set.')
}
}
// this has to be a bit more complicated to support legacy data of all forms
getCredentialsByURI (uri) {
const nerfed = nerfDart(uri)
const def = nerfDart(this.get('registry'))
const creds = {}
// email is handled differently, it used to always be nerfed and now it never should be
// if it's set nerfed to the default registry, then we copy it to the unnerfed key
// TODO: evaluate removing 'email' from the credentials object returned here
const email = this.get(`${nerfed}:email`) || this.get('email')
if (email) {
if (nerfed === def) {
this.set('email', email, 'user')
}
creds.email = email
}
const certfileReg = this.get(`${nerfed}:certfile`)
const keyfileReg = this.get(`${nerfed}:keyfile`)
if (certfileReg && keyfileReg) {
creds.certfile = certfileReg
creds.keyfile = keyfileReg
// cert/key may be used in conjunction with other credentials, thus no `return`
}
const tokenReg = this.get(`${nerfed}:_authToken`)
if (tokenReg) {
creds.token = tokenReg
return creds
}
const userReg = this.get(`${nerfed}:username`)
const passReg = this.get(`${nerfed}:_password`)
if (userReg && passReg) {
creds.username = userReg
creds.password = Buffer.from(passReg, 'base64').toString('utf8')
const auth = `${creds.username}:${creds.password}`
creds.auth = Buffer.from(auth, 'utf8').toString('base64')
return creds
}
const authReg = this.get(`${nerfed}:_auth`)
if (authReg) {
const authDecode = Buffer.from(authReg, 'base64').toString('utf8')
const authSplit = authDecode.split(':')
creds.username = authSplit.shift()
creds.password = authSplit.join(':')
creds.auth = authReg
return creds
}
// at this point, nothing else is usable so just return what we do have
return creds
}
// set up the environment object we have with npm_config_* environs
// for all configs that are different from their default values, and
// set EDITOR and HOME.
setEnvs () {
setEnvs(this)
}
}
const _loadError = Symbol('loadError')
const _valid = Symbol('valid')
class ConfigData {
#data
#source = null
#raw = null
constructor (parent) {
this.#data = Object.create(parent && parent.data)
this.#raw = {}
this[_valid] = true
}
get data () {
return this.#data
}
get valid () {
return this[_valid]
}
set source (s) {
if (this.#source) {
throw new Error('cannot set ConfigData source more than once')
}
this.#source = s
}
get source () {
return this.#source
}
set loadError (e) {
if (this[_loadError] || (Object.keys(this.#raw).length)) {
throw new Error('cannot set ConfigData loadError after load')
}
this[_loadError] = e
}
get loadError () {
return this[_loadError]
}
set raw (r) {
if (Object.keys(this.#raw).length || this[_loadError]) {
throw new Error('cannot set ConfigData raw after load')
}
this.#raw = r
}
get raw () {
return this.#raw
}
}
module.exports = Config

18
package/node_modules/@npmcli/config/lib/nerf-dart.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
const { URL } = require('node:url')
/**
* Maps a URL to an identifier.
*
* Name courtesy schiffertronix media LLC, a New Jersey corporation
*
* @param {String} uri The URL to be nerfed.
*
* @returns {String} A nerfed URL.
*/
module.exports = (url) => {
const parsed = new URL(url)
const from = `${parsed.protocol}//${parsed.host}${parsed.pathname}`
const rel = new URL('.', from)
const res = `//${rel.host}${rel.pathname}`
return res
}

86
package/node_modules/@npmcli/config/lib/parse-field.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
// Parse a field, coercing it to the best type available.
const typeDefs = require('./type-defs.js')
const envReplace = require('./env-replace.js')
const { resolve } = require('node:path')
const { parse: umaskParse } = require('./umask.js')
const parseField = (f, key, opts, listElement = false) => {
if (typeof f !== 'string' && !Array.isArray(f)) {
return f
}
const { platform, types, home, env } = opts
// type can be array or a single thing. coerce to array.
const typeList = new Set([].concat(types[key]))
const isPath = typeList.has(typeDefs.path.type)
const isBool = typeList.has(typeDefs.Boolean.type)
const isString = isPath || typeList.has(typeDefs.String.type)
const isUmask = typeList.has(typeDefs.Umask.type)
const isNumber = typeList.has(typeDefs.Number.type)
const isList = !listElement && typeList.has(Array)
const isDate = typeList.has(typeDefs.Date.type)
if (Array.isArray(f)) {
return !isList ? f : f.map(field => parseField(field, key, opts, true))
}
// now we know it's a string
f = f.trim()
// list types get put in the environment separated by double-\n
// usually a single \n would suffice, but ca/cert configs can contain
// line breaks and multiple entries.
if (isList) {
return parseField(f.split('\n\n'), key, opts)
}
// --foo is like --foo=true for boolean types
if (isBool && !isString && f === '') {
return true
}
// string types can be the string 'true', 'false', etc.
// otherwise, parse these values out
if (!isString && !isPath && !isNumber) {
switch (f) {
case 'true': return true
case 'false': return false
case 'null': return null
case 'undefined': return undefined
}
}
f = envReplace(f, env)
if (isDate) {
return new Date(f)
}
if (isPath) {
const homePattern = platform === 'win32' ? /^~(\/|\\)/ : /^~\//
if (homePattern.test(f) && home) {
f = resolve(home, f.slice(2))
} else {
f = resolve(f)
}
}
if (isUmask) {
try {
return umaskParse(f)
} catch (er) {
// let it warn later when we validate
return f
}
}
if (isNumber && !isNaN(f)) {
f = +f
}
return f
}
module.exports = parseField

108
package/node_modules/@npmcli/config/lib/set-envs.js generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Set environment variables for any non-default configs,
// so that they're already there when we run lifecycle scripts.
//
// See https://github.com/npm/rfcs/pull/90
// Return the env key if this is a thing that belongs in the env.
// Ie, if the key isn't a @scope, //nerf.dart, or _private,
// and the value is a string or array. Otherwise return false.
const envKey = (key, val) => {
return !/^[/@_]/.test(key) &&
(typeof envVal(val) === 'string') &&
`npm_config_${key.replace(/-/g, '_').toLowerCase()}`
}
const envVal = val => Array.isArray(val) ? val.map(v => envVal(v)).join('\n\n')
: val === null || val === undefined || val === false ? ''
: typeof val === 'object' ? null
: String(val)
const sameConfigValue = (def, val) =>
!Array.isArray(val) || !Array.isArray(def) ? def === val
: sameArrayValue(def, val)
const sameArrayValue = (def, val) => {
if (def.length !== val.length) {
return false
}
for (let i = 0; i < def.length; i++) {
/* istanbul ignore next - there are no array configs where the default
* is not an empty array, so this loop is a no-op, but it's the correct
* thing to do if we ever DO add a config like that. */
if (def[i] !== val[i]) {
return false
}
}
return true
}
const setEnv = (env, rawKey, rawVal) => {
const val = envVal(rawVal)
const key = envKey(rawKey, val)
if (key && val !== null) {
env[key] = val
}
}
const setEnvs = (config) => {
// This ensures that all npm config values that are not the defaults are
// shared appropriately with child processes, without false positives.
const {
env,
defaults,
definitions,
list: [cliConf, envConf],
} = config
env.INIT_CWD = process.cwd()
// if the key is deprecated, skip it always.
// if the key is the default value,
// if the environ is NOT the default value,
// set the environ
// else skip it, it's fine
// if the key is NOT the default value,
// if the env is setting it, then leave it (already set)
// otherwise, set the env
const cliSet = new Set(Object.keys(cliConf))
const envSet = new Set(Object.keys(envConf))
for (const key in cliConf) {
const { deprecated, envExport = true } = definitions[key] || {}
if (deprecated || envExport === false) {
continue
}
if (sameConfigValue(defaults[key], cliConf[key])) {
// config is the default, if the env thought different, then we
// have to set it BACK to the default in the environment.
if (!sameConfigValue(envConf[key], cliConf[key])) {
setEnv(env, key, cliConf[key])
}
} else {
// config is not the default. if the env wasn't the one to set
// it that way, then we have to put it in the env
if (!(envSet.has(key) && !cliSet.has(key))) {
setEnv(env, key, cliConf[key])
}
}
}
// also set some other common nice envs that we want to rely on
env.HOME = config.home
env.npm_config_global_prefix = config.globalPrefix
env.npm_config_local_prefix = config.localPrefix
if (cliConf.editor) {
env.EDITOR = cliConf.editor
}
// note: this doesn't afect the *current* node process, of course, since
// it's already started, but it does affect the options passed to scripts.
if (cliConf['node-options']) {
env.NODE_OPTIONS = cliConf['node-options']
}
env.npm_execpath = config.npmBin
env.NODE = env.npm_node_execpath = config.execPath
}
module.exports = setEnvs

61
package/node_modules/@npmcli/config/lib/type-defs.js generated vendored Normal file
View File

@@ -0,0 +1,61 @@
const nopt = require('nopt')
const { validate: validateUmask } = require('./umask.js')
class Umask {}
class Semver {}
const semverValid = require('semver/functions/valid')
const validateSemver = (data, k, val) => {
const valid = semverValid(val)
if (!valid) {
return false
}
data[k] = valid
}
const noptValidatePath = nopt.typeDefs.path.validate
const validatePath = (data, k, val) => {
if (typeof val !== 'string') {
return false
}
return noptValidatePath(data, k, val)
}
// add descriptions so we can validate more usefully
module.exports = {
...nopt.typeDefs,
semver: {
type: Semver,
validate: validateSemver,
description: 'full valid SemVer string',
},
Umask: {
type: Umask,
validate: validateUmask,
description: 'octal number in range 0o000..0o777 (0..511)',
},
url: {
...nopt.typeDefs.url,
description: 'full url with "http://"',
},
path: {
...nopt.typeDefs.path,
validate: validatePath,
description: 'valid filesystem path',
},
Number: {
...nopt.typeDefs.Number,
description: 'numeric value',
},
Boolean: {
...nopt.typeDefs.Boolean,
description: 'boolean value (true or false)',
},
Date: {
...nopt.typeDefs.Date,
description: 'valid Date string',
},
}
// TODO: make nopt less of a global beast so this kludge isn't necessary
nopt.typeDefs = module.exports

View File

@@ -0,0 +1,21 @@
// return the description of the valid values of a field
// returns a string for one thing, or an array of descriptions
const typeDefs = require('./type-defs.js')
const typeDescription = t => {
if (!t || typeof t !== 'function' && typeof t !== 'object') {
return t
}
if (Array.isArray(t)) {
return t.map(t => typeDescription(t))
}
for (const { type, description } of Object.values(typeDefs)) {
if (type === t) {
return description || type
}
}
return t
}
module.exports = t => [].concat(typeDescription(t)).filter(t => t !== undefined)

35
package/node_modules/@npmcli/config/lib/umask.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
const parse = val => {
// this is run via nopt and parse field where everything is
// converted to a string first, ignoring coverage for now
// instead of figuring out what is happening under the hood in nopt
// istanbul ignore else
if (typeof val === 'string') {
if (/^0o?[0-7]+$/.test(val)) {
return parseInt(val.replace(/^0o?/, ''), 8)
} else if (/^[1-9][0-9]*$/.test(val)) {
return parseInt(val, 10)
} else {
throw new Error(`invalid umask value: ${val}`)
}
} else {
if (typeof val !== 'number') {
throw new Error(`invalid umask value: ${val}`)
}
val = Math.floor(val)
if (val < 0 || val > 511) {
throw new Error(`invalid umask value: ${val}`)
}
return val
}
}
const validate = (data, k, val) => {
try {
data[k] = parse(val)
return true
} catch (er) {
return false
}
}
module.exports = { parse, validate }

56
package/node_modules/@npmcli/config/package.json generated vendored Normal file
View File

@@ -0,0 +1,56 @@
{
"name": "@npmcli/config",
"version": "8.3.4",
"files": [
"bin/",
"lib/"
],
"main": "lib/index.js",
"description": "Configuration management for the npm cli",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/cli.git",
"directory": "workspaces/config"
},
"author": "GitHub Inc.",
"license": "ISC",
"scripts": {
"test": "tap",
"snap": "tap",
"lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
"postlint": "template-oss-check",
"lintfix": "npm run lint -- --fix",
"posttest": "npm run lint",
"template-oss-apply": "template-oss-apply --force"
},
"tap": {
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"devDependencies": {
"@npmcli/eslint-config": "^4.0.0",
"@npmcli/mock-globals": "^1.0.0",
"@npmcli/template-oss": "4.22.0",
"tap": "^16.3.8"
},
"dependencies": {
"@npmcli/map-workspaces": "^3.0.2",
"@npmcli/package-json": "^5.1.1",
"ci-info": "^4.0.0",
"ini": "^4.1.2",
"nopt": "^7.2.1",
"proc-log": "^4.2.0",
"semver": "^7.3.5",
"walk-up-path": "^3.0.1"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.22.0",
"content": "../../scripts/template-oss/index.js"
}
}