Socket
Socket
Sign inDemoInstall

@npmcli/config

Package Overview
Dependencies
Maintainers
6
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@npmcli/config - npm Package Compare versions

Comparing version 2.4.0 to 3.0.0

5

lib/env-replace.js

@@ -6,9 +6,10 @@ // replace any ${ENV} values with the appropriate environ.

module.exports = (f, env) => f.replace(envExpr, (orig, esc, name) => {
const val = env[name] !== undefined ? env[name] : `\$\{${name}\}`
const val = env[name] !== undefined ? env[name] : `$\{${name}}`
// consume the escape chars that are relevant.
if (esc.length % 2)
if (esc.length % 2) {
return orig.substr((esc.length + 1) / 2)
}
return (esc.substr(esc.length / 2)) + val
})

206

lib/index.js

@@ -6,2 +6,4 @@ // TODO: set the scope config from package.json or explicit cli config

const mkdirp = require('mkdirp-infer-owner')
const mapWorkspaces = require('@npmcli/map-workspaces')
const rpj = require('read-package-json-fast')

@@ -95,3 +97,2 @@ /* istanbul ignore next */

}) {
// turn the definitions into nopt's weirdo syntax

@@ -105,4 +106,5 @@ this.definitions = definitions

types[key] = def.type
if (def.deprecated)
if (def.deprecated) {
this.deprecated[key] = def.deprecated.trim().replace(/\n +/, '\n')
}
}

@@ -168,6 +170,8 @@

find (key) {
if (!this.loaded)
if (!this.loaded) {
throw new Error('call config.load() before reading values')
}
return this[_find](key)
}
[_find] (key) {

@@ -178,4 +182,5 @@ // have to look in reverse order

const [where, { data }] = entries[i]
if (hasOwnProperty(data, key))
if (hasOwnProperty(data, key)) {
return where
}
}

@@ -186,6 +191,8 @@ return null

get (key, where) {
if (!this.loaded)
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

@@ -197,3 +204,3 @@ // while in the process of loading.

}
const { data, source } = this.data.get(where || 'cli')
const { data } = this.data.get(where || 'cli')
return where === null || hasOwnProperty(data, key) ? data[key] : undefined

@@ -203,6 +210,8 @@ }

set (key, val, where = 'cli') {
if (!this.loaded)
if (!this.loaded) {
throw new Error('call config.load() before setting values')
if (!confTypes.has(where))
}
if (!confTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
this[_checkDeprecated](key)

@@ -220,4 +229,5 @@ const { data } = this.data.get(where)

get flat () {
if (this[_flatOptions])
if (this[_flatOptions]) {
return this[_flatOptions]
}

@@ -237,6 +247,8 @@ // create the object for flat options passed to deps

delete (key, where = 'cli') {
if (!this.loaded)
if (!this.loaded) {
throw new Error('call config.load() before deleting values')
if (!confTypes.has(where))
}
if (!confTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
delete this.data.get(where).data[key]

@@ -246,4 +258,5 @@ }

async load () {
if (this.loaded)
if (this.loaded) {
throw new Error('attempting to load npm config multiple times')
}

@@ -296,3 +309,5 @@ process.emit('time', 'config:load')

// might be a security hazard, which was the intention.
try { this.setCredentialsByURI(reg, creds) } catch (_) {}
try {
this.setCredentialsByURI(reg, creds)
} catch (_) {}
process.emit('timeEnd', 'config:load:credentials')

@@ -334,4 +349,5 @@

loadHome () {
if (this.env.HOME)
if (this.env.HOME) {
return this.home = this.env.HOME
}
this.home = homedir()

@@ -341,4 +357,5 @@ }

loadGlobalPrefix () {
if (this.globalPrefix)
if (this.globalPrefix) {
throw new Error('cannot load default global prefix more than once')
}

@@ -355,4 +372,5 @@ if (this.env.PREFIX) {

// destdir only is respected on Unix
if (this.env.DESTDIR)
if (this.env.DESTDIR) {
this.globalPrefix = join(this.env.DESTDIR, this.globalPrefix)
}
}

@@ -362,7 +380,7 @@ }

loadEnv () {
const prefix = 'npm_config_'
const conf = Object.create(null)
for (const [envKey, envVal] of Object.entries(this.env)) {
if (!/^npm_config_/i.test(envKey) || envVal === '')
if (!/^npm_config_/i.test(envKey) || envVal === '') {
continue
}
const key = envKey.substr('npm_config_'.length)

@@ -387,5 +405,6 @@ .replace(/(?!^)_/g, '-') // don't replace _ at the start of the key

get valid () {
for (const [where, {valid}] of this.data.entries()) {
if (valid === false || valid === null && !this.validate(where))
for (const [where, { valid }] of this.data.entries()) {
if (valid === false || valid === null && !this.validate(where)) {
return false
}
}

@@ -398,7 +417,8 @@ return true

let valid = true
for (const [where, obj] of this.data.entries()) {
for (const [where] of this.data.entries()) {
// no need to validate our defaults, we know they're fine
// cli was already validated when parsed the first time
if (where === 'default' || where === 'builtin' || where === 'cli')
if (where === 'default' || where === 'builtin' || where === 'cli') {
continue
}
const ret = this.validate(where)

@@ -445,10 +465,11 @@ valid = valid && ret

if (Array.isArray(type)) {
if (type.includes(typeDefs.url.type))
if (type.includes(typeDefs.url.type)) {
type = typeDefs.url.type
else {
} 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))
if (type.includes(typeDefs.path.type)) {
type = typeDefs.path.type
}
}

@@ -491,4 +512,5 @@ }

conf.loadError = er
if (er.code !== 'ENOENT')
if (er.code !== 'ENOENT') {
this.log.verbose('config', `error loading ${where} config`, er)
}
} else {

@@ -499,4 +521,5 @@ conf.raw = obj

const v = this.parseField(value, k)
if (where !== 'default')
if (where !== 'default') {
this[_checkDeprecated](k, where, obj, [key, value])
}
conf.data[k] = v

@@ -552,5 +575,5 @@ }

// it doesn't match what the userconfig will be.
if (projectFile !== this[_get]('userconfig'))
if (projectFile !== this[_get]('userconfig')) {
return this[_loadFile](projectFile, 'project')
else {
} else {
this.data.get('project').source = '(same as "user" config, ignored)'

@@ -568,19 +591,61 @@ this.sources.set(this.data.get('project').source, 'project')

const cliWorkspaces = this[_get]('workspaces', 'cli')
for (const p of walkUp(this.cwd)) {
// walk up until we have a nm dir or a pj file
const hasAny = (await Promise.all([
stat(resolve(p, 'node_modules'))
.then(st => st.isDirectory())
.catch(() => false),
stat(resolve(p, 'package.json'))
.then(st => st.isFile())
.catch(() => false),
])).some(is => is)
if (hasAny) {
const hasNodeModules = await stat(resolve(p, 'node_modules'))
.then((st) => st.isDirectory())
.catch(() => false)
const hasPackageJson = await stat(resolve(p, 'package.json'))
.then((st) => st.isFile())
.catch(() => false)
if (!this.localPrefix && (hasNodeModules || hasPackageJson)) {
this.localPrefix = p
return
// if workspaces are disabled, return now
if (cliWorkspaces === false) {
return
}
// otherwise, continue the loop
continue
}
if (this.localPrefix && hasPackageJson) {
// 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 pkg = await rpj(resolve(p, 'package.json')).catch(() => false)
if (!pkg) {
continue
}
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
const hasNpmrc = await stat(resolve(this.localPrefix, '.npmrc'))
.then((st) => st.isFile())
.catch(() => false)
if (hasNpmrc) {
this.log.warn(`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.log.info(`found workspace root at ${this.localPrefix}`)
// we found a root, so we return now
return
}
}
}
}
this.localPrefix = this.cwd
if (!this.localPrefix) {
this.localPrefix = this.cwd
}
}

@@ -597,6 +662,8 @@

async save (where) {
if (!this.loaded)
if (!this.loaded) {
throw new Error('call config.load() before saving')
if (!confFileTypes.has(where))
}
if (!confFileTypes.has(where)) {
throw new Error('invalid config location param: ' + where)
}
const conf = this.data.get(where)

@@ -613,3 +680,5 @@ conf[_raw] = { ...conf.data }

// saved back to the .npmrc file, so we're good.
try { this.setCredentialsByURI(reg, creds) } catch (_) {}
try {
this.setCredentialsByURI(reg, creds)
} catch (_) {}
}

@@ -630,4 +699,5 @@

const st = await stat(dir).catch(() => null)
if (st && (st.uid !== myUid || st.gid !== myGid))
if (st && (st.uid !== myUid || st.gid !== myGid)) {
await chown(conf.source, st.uid, st.gid).catch(() => {})
}
}

@@ -680,4 +750,5 @@ const mode = where === 'user' ? 0o600 : 0o666

this.get(`${nerfed}:email`, 'user')
if (email)
if (email) {
this.set('email', email, 'user')
}
}

@@ -698,6 +769,8 @@

} else if (username || password) {
if (!username)
if (!username) {
throw new Error('must include username')
if (!password)
}
if (!password) {
throw new Error('must include password')
}
this.delete(`${nerfed}:_authToken`, 'user')

@@ -720,4 +793,5 @@ this.set(`${nerfed}:username`, username, 'user')

const email = this.get(`${nerfed}:email`) || this.get('email')
if (email)
if (email) {
creds.email = email
}

@@ -757,4 +831,5 @@ const tokenReg = this.get(`${nerfed}:_authToken`) ||

const defaultNerf = nerfDart(this.get('registry'))
if (nerfed !== defaultNerf)
if (nerfed !== defaultNerf) {
return creds
}

@@ -774,4 +849,5 @@ const userDef = this.get('username')

const auth = this.get('_auth')
if (!auth)
if (!auth) {
return creds
}

@@ -789,3 +865,5 @@ const authDecode = Buffer.from(auth, 'base64').toString('utf8')

// set EDITOR and HOME.
setEnvs () { setEnvs(this) }
setEnvs () {
setEnvs(this)
}
}

@@ -816,23 +894,35 @@

set source (s) {
if (this[_source])
if (this[_source]) {
throw new Error('cannot set ConfigData source more than once')
}
this[_source] = s
}
get source () { return this[_source] }
get source () {
return this[_source]
}
set loadError (e) {
if (this[_loadError] || this[_raw])
if (this[_loadError] || this[_raw]) {
throw new Error('cannot set ConfigData loadError after load')
}
this[_loadError] = e
}
get loadError () { return this[_loadError] }
get loadError () {
return this[_loadError]
}
set raw (r) {
if (this[_raw] || this[_loadError])
if (this[_raw] || this[_loadError]) {
throw new Error('cannot set ConfigData raw after load')
}
this[_raw] = r
}
get raw () { return this[_raw] }
get raw () {
return this[_raw]
}
}
module.exports = Config

@@ -9,6 +9,7 @@ // Parse a field, coercing it to the best type available.

const parseField = (f, key, opts, listElement = false) => {
if (typeof f !== 'string' && !Array.isArray(f))
if (typeof f !== 'string' && !Array.isArray(f)) {
return f
}
const { platform, types, log, home, env } = opts
const { platform, types, home, env } = opts

@@ -24,4 +25,5 @@ // type can be array or a single thing. coerce to array.

if (Array.isArray(f))
if (Array.isArray(f)) {
return !isList ? f : f.map(field => parseField(field, key, opts, true))
}

@@ -34,8 +36,10 @@ // now we know it's a string

// line breaks and multiple entries.
if (isList)
if (isList) {
return parseField(f.split('\n\n'), key, opts)
}
// --foo is like --foo=true for boolean types
if (isBool && !isString && f === '')
if (isBool && !isString && f === '') {
return true
}

@@ -57,6 +61,7 @@ // string types can be the string 'true', 'false', etc.

const homePattern = platform === 'win32' ? /^~(\/|\\)/ : /^~\//
if (homePattern.test(f) && home)
if (homePattern.test(f) && home) {
f = resolve(home, f.substr(2))
else
} else {
f = resolve(f)
}
}

@@ -73,4 +78,5 @@

if (isNumber && !isNaN(f))
if (isNumber && !isNaN(f)) {
f = +f
}

@@ -77,0 +83,0 @@ return f

@@ -25,4 +25,5 @@ // Set environment variables for any non-default configs,

const sameArrayValue = (def, val) => {
if (def.length !== val.length)
if (def.length !== val.length) {
return false
}

@@ -33,4 +34,5 @@ for (let i = 0; i < def.length; i++) {

* thing to do if we ever DO add a config like that. */
if (def[i] !== val[i])
if (def[i] !== val[i]) {
return false
}
}

@@ -43,4 +45,5 @@ return true

const key = envKey(rawKey, val)
if (key && val !== null)
if (key && val !== null) {
env[key] = val
}
}

@@ -52,4 +55,2 @@

const {
globalPrefix,
platform,
env,

@@ -75,4 +76,5 @@ defaults,

const { deprecated, envExport = true } = definitions[key] || {}
if (deprecated || envExport === false)
if (deprecated || envExport === false) {
continue
}

@@ -82,9 +84,11 @@ if (sameConfigValue(defaults[key], cliConf[key])) {

// have to set it BACK to the default in the environment.
if (!sameConfigValue(envConf[key], cliConf[key]))
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)))
if (!(envSet.has(key) && !cliSet.has(key))) {
setEnv(env, key, cliConf[key])
}
}

@@ -97,12 +101,15 @@ }

env.npm_config_local_prefix = config.localPrefix
if (cliConf.editor)
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'])
if (cliConf['node-options']) {
env.NODE_OPTIONS = cliConf['node-options']
}
if (require.main && require.main.filename)
if (require.main && require.main.filename) {
env.npm_execpath = require.main.filename
}
env.NODE = env.npm_node_execpath = config.execPath

@@ -109,0 +116,0 @@ }

@@ -8,4 +8,5 @@ const nopt = require('nopt')

const valid = semver.valid(val)
if (!valid)
if (!valid) {
return false
}
data[k] = valid

@@ -16,4 +17,5 @@ }

const validatePath = (data, k, val) => {
if (typeof val !== 'string')
if (typeof val !== 'string') {
return false
}
return noptValidatePath(data, k, val)

@@ -20,0 +22,0 @@ }

@@ -5,11 +5,14 @@ // return the description of the valid values of a field

const typeDescription = t => {
if (!t || typeof t !== 'function' && typeof t !== 'object')
if (!t || typeof t !== 'function' && typeof t !== 'object') {
return t
}
if (Array.isArray(t))
if (Array.isArray(t)) {
return t.map(t => typeDescription(t))
}
for (const { type, description } of Object.values(typeDefs)) {
if (type === t)
if (type === t) {
return description || type
}
}

@@ -16,0 +19,0 @@

class Umask {}
const parse = val => {
if (typeof val === 'string') {
if (/^0o?[0-7]+$/.test(val))
if (/^0o?[0-7]+$/.test(val)) {
return parseInt(val.replace(/^0o?/, ''), 8)
else if (/^[1-9][0-9]*$/.test(val))
} else if (/^[1-9][0-9]*$/.test(val)) {
return parseInt(val, 10)
else
} else {
throw new Error(`invalid umask value: ${val}`)
}
}
if (typeof val !== 'number')
if (typeof val !== 'number') {
throw new Error(`invalid umask value: ${val}`)
}
val = Math.floor(val)
if (val < 0 || val > 511)
if (val < 0 || val > 511) {
throw new Error(`invalid umask value: ${val}`)
}
return val

@@ -17,0 +20,0 @@ }

{
"name": "@npmcli/config",
"version": "2.4.0",
"version": "3.0.0",
"files": [
"bin",
"lib"

@@ -13,3 +14,3 @@ ],

},
"author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)",
"author": "GitHub Inc.",
"license": "ISC",

@@ -21,3 +22,8 @@ "scripts": {

"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags"
"prepublishOnly": "git push origin --follow-tags",
"lint": "eslint '**/*.js'",
"postlint": "npm-template-check",
"lintfix": "npm run lint -- --fix",
"posttest": "npm run lint",
"template-copy": "npm-template-copy --force"
},

@@ -29,8 +35,11 @@ "tap": {

"devDependencies": {
"@npmcli/template-oss": "^2.5.1",
"tap": "^15.0.4"
},
"dependencies": {
"@npmcli/map-workspaces": "^2.0.0",
"ini": "^2.0.0",
"mkdirp-infer-owner": "^2.0.0",
"nopt": "^5.0.0",
"read-package-json-fast": "^2.0.3",
"semver": "^7.3.4",

@@ -40,4 +49,7 @@ "walk-up-path": "^1.0.0"

"engines": {
"node": ">=10"
"node": "^12.13.0 || ^14.15.0 || >=16"
},
"templateOSS": {
"version": "2.6.0"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc