@npmcli/config 2.3.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -6
- package/lib/env-replace.js +4 -3
- package/lib/index.js +167 -64
- package/lib/parse-field.js +14 -8
- package/lib/set-envs.js +18 -11
- package/lib/type-defs.js +4 -2
- package/lib/type-description.js +6 -3
- package/lib/umask.js +8 -5
- package/package.json +19 -6
- package/lib/proc-log.js +0 -4
package/README.md
CHANGED
|
@@ -78,9 +78,12 @@ const conf = new Config({
|
|
|
78
78
|
platform: process.platform,
|
|
79
79
|
// optional, defaults to process.cwd()
|
|
80
80
|
cwd: process.cwd(),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// emits log events on the process object
|
|
84
|
+
// see `proc-log` for more info
|
|
85
|
+
process.on('log', (level, ...args) => {
|
|
86
|
+
console.log(level, ...args)
|
|
84
87
|
})
|
|
85
88
|
|
|
86
89
|
// returns a promise that fails if config loading fails, and
|
|
@@ -124,8 +127,6 @@ Options:
|
|
|
124
127
|
Windows.
|
|
125
128
|
- `execPath` Optional, defaults to `process.execPath`. Used to infer the
|
|
126
129
|
`globalPrefix`.
|
|
127
|
-
- `log` Optional, the object used to log debug messages, warnings, and
|
|
128
|
-
errors. Defaults to emitting on the `process` object.
|
|
129
130
|
- `env` Optional, defaults to `process.env`. Source of the environment
|
|
130
131
|
variables for configuration.
|
|
131
132
|
- `argv` Optional, defaults to `process.argv`. Source of the CLI options
|
|
@@ -161,7 +162,6 @@ Fields:
|
|
|
161
162
|
- `argv` The `argv` param
|
|
162
163
|
- `execPath` The `execPath` param
|
|
163
164
|
- `platform` The `platform` param
|
|
164
|
-
- `log` The `log` param
|
|
165
165
|
- `defaults` The `defaults` param
|
|
166
166
|
- `shorthands` The `shorthands` param
|
|
167
167
|
- `types` The `types` param
|
|
@@ -218,6 +218,29 @@ Note that it's usually enough (and more efficient) to just check
|
|
|
218
218
|
`config.valid`, since each data object is marked for re-evaluation on every
|
|
219
219
|
`config.set()` operation.
|
|
220
220
|
|
|
221
|
+
### `config.isDefault(key)`
|
|
222
|
+
|
|
223
|
+
Returns `true` if the value is coming directly from the
|
|
224
|
+
default definitions, if the current value for the key config is
|
|
225
|
+
coming from any other source, returns `false`.
|
|
226
|
+
|
|
227
|
+
This method can be used for avoiding or tweaking default values, e.g:
|
|
228
|
+
|
|
229
|
+
> Given a global default definition of foo='foo' it's possible to read that
|
|
230
|
+
> value such as:
|
|
231
|
+
>
|
|
232
|
+
> ```js
|
|
233
|
+
> const save = config.get('foo')
|
|
234
|
+
> ```
|
|
235
|
+
>
|
|
236
|
+
> Now in a different place of your app it's possible to avoid using the `foo`
|
|
237
|
+
> default value, by checking to see if the current config value is currently
|
|
238
|
+
> one that was defined by the default definitions:
|
|
239
|
+
>
|
|
240
|
+
> ```js
|
|
241
|
+
> const save = config.isDefault('foo') ? 'bar' : config.get('foo')
|
|
242
|
+
> ```
|
|
243
|
+
|
|
221
244
|
### `config.save(where)`
|
|
222
245
|
|
|
223
246
|
Save the config file specified by the `where` param. Must be one of
|
package/lib/env-replace.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// replace any ${ENV} values with the appropriate environ.
|
|
2
2
|
|
|
3
|
-
const envExpr = /(\\*)\$\{([
|
|
3
|
+
const envExpr = /(?<!\\)(\\*)\$\{([^${}]+)\}/g
|
|
4
4
|
|
|
5
5
|
module.exports = (f, env) => f.replace(envExpr, (orig, esc, name) => {
|
|
6
|
-
const val = env[name] !== undefined ? env[name] :
|
|
6
|
+
const val = env[name] !== undefined ? env[name] : `$\{${name}}`
|
|
7
7
|
|
|
8
8
|
// consume the escape chars that are relevant.
|
|
9
|
-
if (esc.length % 2)
|
|
9
|
+
if (esc.length % 2) {
|
|
10
10
|
return orig.substr((esc.length + 1) / 2)
|
|
11
|
+
}
|
|
11
12
|
|
|
12
13
|
return (esc.substr(esc.length / 2)) + val
|
|
13
14
|
})
|
package/lib/index.js
CHANGED
|
@@ -3,6 +3,9 @@ const walkUp = require('walk-up-path')
|
|
|
3
3
|
const ini = require('ini')
|
|
4
4
|
const nopt = require('nopt')
|
|
5
5
|
const mkdirp = require('mkdirp-infer-owner')
|
|
6
|
+
const mapWorkspaces = require('@npmcli/map-workspaces')
|
|
7
|
+
const rpj = require('read-package-json-fast')
|
|
8
|
+
const log = require('proc-log')
|
|
6
9
|
|
|
7
10
|
/* istanbul ignore next */
|
|
8
11
|
const myUid = process.getuid && process.getuid()
|
|
@@ -86,12 +89,10 @@ class Config {
|
|
|
86
89
|
// options just to override in tests, mostly
|
|
87
90
|
env = process.env,
|
|
88
91
|
argv = process.argv,
|
|
89
|
-
log = require('./proc-log.js'),
|
|
90
92
|
platform = process.platform,
|
|
91
93
|
execPath = process.execPath,
|
|
92
94
|
cwd = process.cwd(),
|
|
93
95
|
}) {
|
|
94
|
-
|
|
95
96
|
// turn the definitions into nopt's weirdo syntax
|
|
96
97
|
this.definitions = definitions
|
|
97
98
|
const types = {}
|
|
@@ -100,8 +101,9 @@ class Config {
|
|
|
100
101
|
for (const [key, def] of Object.entries(definitions)) {
|
|
101
102
|
defaults[key] = def.default
|
|
102
103
|
types[key] = def.type
|
|
103
|
-
if (def.deprecated)
|
|
104
|
+
if (def.deprecated) {
|
|
104
105
|
this.deprecated[key] = def.deprecated.trim().replace(/\n +/, '\n')
|
|
106
|
+
}
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
// populated the first time we flatten the object
|
|
@@ -112,7 +114,6 @@ class Config {
|
|
|
112
114
|
this.defaults = defaults
|
|
113
115
|
|
|
114
116
|
this.npmPath = npmPath
|
|
115
|
-
this.log = log
|
|
116
117
|
this.argv = argv
|
|
117
118
|
this.env = env
|
|
118
119
|
this.execPath = execPath
|
|
@@ -162,41 +163,48 @@ class Config {
|
|
|
162
163
|
|
|
163
164
|
// return the location where key is found.
|
|
164
165
|
find (key) {
|
|
165
|
-
if (!this.loaded)
|
|
166
|
+
if (!this.loaded) {
|
|
166
167
|
throw new Error('call config.load() before reading values')
|
|
168
|
+
}
|
|
167
169
|
return this[_find](key)
|
|
168
170
|
}
|
|
171
|
+
|
|
169
172
|
[_find] (key) {
|
|
170
173
|
// have to look in reverse order
|
|
171
174
|
const entries = [...this.data.entries()]
|
|
172
175
|
for (let i = entries.length - 1; i > -1; i--) {
|
|
173
176
|
const [where, { data }] = entries[i]
|
|
174
|
-
if (hasOwnProperty(data, key))
|
|
177
|
+
if (hasOwnProperty(data, key)) {
|
|
175
178
|
return where
|
|
179
|
+
}
|
|
176
180
|
}
|
|
177
181
|
return null
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
get (key, where) {
|
|
181
|
-
if (!this.loaded)
|
|
185
|
+
if (!this.loaded) {
|
|
182
186
|
throw new Error('call config.load() before reading values')
|
|
187
|
+
}
|
|
183
188
|
return this[_get](key, where)
|
|
184
189
|
}
|
|
190
|
+
|
|
185
191
|
// we need to get values sometimes, so use this internal one to do so
|
|
186
192
|
// while in the process of loading.
|
|
187
193
|
[_get] (key, where = null) {
|
|
188
194
|
if (where !== null && !confTypes.has(where)) {
|
|
189
195
|
throw new Error('invalid config location param: ' + where)
|
|
190
196
|
}
|
|
191
|
-
const { data
|
|
197
|
+
const { data } = this.data.get(where || 'cli')
|
|
192
198
|
return where === null || hasOwnProperty(data, key) ? data[key] : undefined
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
set (key, val, where = 'cli') {
|
|
196
|
-
if (!this.loaded)
|
|
202
|
+
if (!this.loaded) {
|
|
197
203
|
throw new Error('call config.load() before setting values')
|
|
198
|
-
|
|
204
|
+
}
|
|
205
|
+
if (!confTypes.has(where)) {
|
|
199
206
|
throw new Error('invalid config location param: ' + where)
|
|
207
|
+
}
|
|
200
208
|
this[_checkDeprecated](key)
|
|
201
209
|
const { data } = this.data.get(where)
|
|
202
210
|
data[key] = val
|
|
@@ -209,8 +217,9 @@ class Config {
|
|
|
209
217
|
}
|
|
210
218
|
|
|
211
219
|
get flat () {
|
|
212
|
-
if (this[_flatOptions])
|
|
220
|
+
if (this[_flatOptions]) {
|
|
213
221
|
return this[_flatOptions]
|
|
222
|
+
}
|
|
214
223
|
|
|
215
224
|
// create the object for flat options passed to deps
|
|
216
225
|
process.emit('time', 'config:load:flatten')
|
|
@@ -225,16 +234,19 @@ class Config {
|
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
delete (key, where = 'cli') {
|
|
228
|
-
if (!this.loaded)
|
|
237
|
+
if (!this.loaded) {
|
|
229
238
|
throw new Error('call config.load() before deleting values')
|
|
230
|
-
|
|
239
|
+
}
|
|
240
|
+
if (!confTypes.has(where)) {
|
|
231
241
|
throw new Error('invalid config location param: ' + where)
|
|
242
|
+
}
|
|
232
243
|
delete this.data.get(where).data[key]
|
|
233
244
|
}
|
|
234
245
|
|
|
235
246
|
async load () {
|
|
236
|
-
if (this.loaded)
|
|
247
|
+
if (this.loaded) {
|
|
237
248
|
throw new Error('attempting to load npm config multiple times')
|
|
249
|
+
}
|
|
238
250
|
|
|
239
251
|
process.emit('time', 'config:load')
|
|
240
252
|
// first load the defaults, which sets the global prefix
|
|
@@ -282,7 +294,9 @@ class Config {
|
|
|
282
294
|
const creds = this.getCredentialsByURI(reg)
|
|
283
295
|
// ignore this error because a failed set will strip out anything that
|
|
284
296
|
// might be a security hazard, which was the intention.
|
|
285
|
-
try {
|
|
297
|
+
try {
|
|
298
|
+
this.setCredentialsByURI(reg, creds)
|
|
299
|
+
} catch (_) {}
|
|
286
300
|
process.emit('timeEnd', 'config:load:credentials')
|
|
287
301
|
|
|
288
302
|
// set proper globalPrefix now that everything is loaded
|
|
@@ -319,14 +333,16 @@ class Config {
|
|
|
319
333
|
}
|
|
320
334
|
|
|
321
335
|
loadHome () {
|
|
322
|
-
if (this.env.HOME)
|
|
336
|
+
if (this.env.HOME) {
|
|
323
337
|
return this.home = this.env.HOME
|
|
338
|
+
}
|
|
324
339
|
this.home = homedir()
|
|
325
340
|
}
|
|
326
341
|
|
|
327
342
|
loadGlobalPrefix () {
|
|
328
|
-
if (this.globalPrefix)
|
|
343
|
+
if (this.globalPrefix) {
|
|
329
344
|
throw new Error('cannot load default global prefix more than once')
|
|
345
|
+
}
|
|
330
346
|
|
|
331
347
|
if (this.env.PREFIX) {
|
|
332
348
|
this.globalPrefix = this.env.PREFIX
|
|
@@ -338,17 +354,18 @@ class Config {
|
|
|
338
354
|
this.globalPrefix = dirname(dirname(this.execPath))
|
|
339
355
|
|
|
340
356
|
// destdir only is respected on Unix
|
|
341
|
-
if (this.env.DESTDIR)
|
|
357
|
+
if (this.env.DESTDIR) {
|
|
342
358
|
this.globalPrefix = join(this.env.DESTDIR, this.globalPrefix)
|
|
359
|
+
}
|
|
343
360
|
}
|
|
344
361
|
}
|
|
345
362
|
|
|
346
363
|
loadEnv () {
|
|
347
|
-
const prefix = 'npm_config_'
|
|
348
364
|
const conf = Object.create(null)
|
|
349
365
|
for (const [envKey, envVal] of Object.entries(this.env)) {
|
|
350
|
-
if (!/^npm_config_/i.test(envKey) || envVal === '')
|
|
366
|
+
if (!/^npm_config_/i.test(envKey) || envVal === '') {
|
|
351
367
|
continue
|
|
368
|
+
}
|
|
352
369
|
const key = envKey.substr('npm_config_'.length)
|
|
353
370
|
.replace(/(?!^)_/g, '-') // don't replace _ at the start of the key
|
|
354
371
|
.toLowerCase()
|
|
@@ -368,9 +385,10 @@ class Config {
|
|
|
368
385
|
}
|
|
369
386
|
|
|
370
387
|
get valid () {
|
|
371
|
-
for (const [where, {valid}] of this.data.entries()) {
|
|
372
|
-
if (valid === false || valid === null && !this.validate(where))
|
|
388
|
+
for (const [where, { valid }] of this.data.entries()) {
|
|
389
|
+
if (valid === false || valid === null && !this.validate(where)) {
|
|
373
390
|
return false
|
|
391
|
+
}
|
|
374
392
|
}
|
|
375
393
|
return true
|
|
376
394
|
}
|
|
@@ -378,11 +396,12 @@ class Config {
|
|
|
378
396
|
validate (where) {
|
|
379
397
|
if (!where) {
|
|
380
398
|
let valid = true
|
|
381
|
-
for (const [where
|
|
399
|
+
for (const [where] of this.data.entries()) {
|
|
382
400
|
// no need to validate our defaults, we know they're fine
|
|
383
401
|
// cli was already validated when parsed the first time
|
|
384
|
-
if (where === 'default' || where === 'builtin' || where === 'cli')
|
|
402
|
+
if (where === 'default' || where === 'builtin' || where === 'cli') {
|
|
385
403
|
continue
|
|
404
|
+
}
|
|
386
405
|
const ret = this.validate(where)
|
|
387
406
|
valid = valid && ret
|
|
388
407
|
}
|
|
@@ -401,8 +420,22 @@ class Config {
|
|
|
401
420
|
}
|
|
402
421
|
}
|
|
403
422
|
|
|
423
|
+
// Returns true if the value is coming directly from the source defined
|
|
424
|
+
// in default definitions, if the current value for the key config is
|
|
425
|
+
// coming from any other different source, returns false
|
|
426
|
+
isDefault (key) {
|
|
427
|
+
const [defaultType, ...types] = [...confTypes]
|
|
428
|
+
const defaultData = this.data.get(defaultType).data
|
|
429
|
+
|
|
430
|
+
return hasOwnProperty(defaultData, key)
|
|
431
|
+
&& types.every(type => {
|
|
432
|
+
const typeData = this.data.get(type).data
|
|
433
|
+
return !hasOwnProperty(typeData, key)
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
404
437
|
invalidHandler (k, val, type, source, where) {
|
|
405
|
-
|
|
438
|
+
log.warn(
|
|
406
439
|
'invalid config',
|
|
407
440
|
k + '=' + JSON.stringify(val),
|
|
408
441
|
`set in ${source}`
|
|
@@ -410,14 +443,15 @@ class Config {
|
|
|
410
443
|
this.data.get(where)[_valid] = false
|
|
411
444
|
|
|
412
445
|
if (Array.isArray(type)) {
|
|
413
|
-
if (type.includes(typeDefs.url.type))
|
|
446
|
+
if (type.includes(typeDefs.url.type)) {
|
|
414
447
|
type = typeDefs.url.type
|
|
415
|
-
else {
|
|
448
|
+
} else {
|
|
416
449
|
/* istanbul ignore if - no actual configs matching this, but
|
|
417
450
|
* path types SHOULD be handled this way, like URLs, for the
|
|
418
451
|
* same reason */
|
|
419
|
-
if (type.includes(typeDefs.path.type))
|
|
452
|
+
if (type.includes(typeDefs.path.type)) {
|
|
420
453
|
type = typeDefs.path.type
|
|
454
|
+
}
|
|
421
455
|
}
|
|
422
456
|
}
|
|
423
457
|
|
|
@@ -434,7 +468,7 @@ class Config {
|
|
|
434
468
|
: mustBe.filter(m => m !== Array)
|
|
435
469
|
.map(n => typeof n === 'string' ? n : JSON.stringify(n))
|
|
436
470
|
.join(', ')
|
|
437
|
-
|
|
471
|
+
log.warn('invalid config', msg, desc)
|
|
438
472
|
}
|
|
439
473
|
|
|
440
474
|
[_loadObject] (obj, where, source, er = null) {
|
|
@@ -455,15 +489,17 @@ class Config {
|
|
|
455
489
|
this.sources.set(source, where)
|
|
456
490
|
if (er) {
|
|
457
491
|
conf.loadError = er
|
|
458
|
-
if (er.code !== 'ENOENT')
|
|
459
|
-
|
|
492
|
+
if (er.code !== 'ENOENT') {
|
|
493
|
+
log.verbose('config', `error loading ${where} config`, er)
|
|
494
|
+
}
|
|
460
495
|
} else {
|
|
461
496
|
conf.raw = obj
|
|
462
497
|
for (const [key, value] of Object.entries(obj)) {
|
|
463
498
|
const k = envReplace(key, this.env)
|
|
464
499
|
const v = this.parseField(value, k)
|
|
465
|
-
if (where !== 'default')
|
|
500
|
+
if (where !== 'default') {
|
|
466
501
|
this[_checkDeprecated](k, where, obj, [key, value])
|
|
502
|
+
}
|
|
467
503
|
conf.data[k] = v
|
|
468
504
|
}
|
|
469
505
|
}
|
|
@@ -473,7 +509,7 @@ class Config {
|
|
|
473
509
|
// XXX a future npm version will make this a warning.
|
|
474
510
|
// An even more future npm version will make this an error.
|
|
475
511
|
if (this.deprecated[key]) {
|
|
476
|
-
|
|
512
|
+
log.verbose('config', key, this.deprecated[key])
|
|
477
513
|
}
|
|
478
514
|
}
|
|
479
515
|
|
|
@@ -514,9 +550,9 @@ class Config {
|
|
|
514
550
|
// up loading the "project" config where the "userconfig" will be,
|
|
515
551
|
// which causes some calamaties. So, we only load project config if
|
|
516
552
|
// it doesn't match what the userconfig will be.
|
|
517
|
-
if (projectFile !== this[_get]('userconfig'))
|
|
553
|
+
if (projectFile !== this[_get]('userconfig')) {
|
|
518
554
|
return this[_loadFile](projectFile, 'project')
|
|
519
|
-
else {
|
|
555
|
+
} else {
|
|
520
556
|
this.data.get('project').source = '(same as "user" config, ignored)'
|
|
521
557
|
this.sources.set(this.data.get('project').source, 'project')
|
|
522
558
|
}
|
|
@@ -529,23 +565,65 @@ class Config {
|
|
|
529
565
|
return
|
|
530
566
|
}
|
|
531
567
|
|
|
568
|
+
const cliWorkspaces = this[_get]('workspaces', 'cli')
|
|
569
|
+
|
|
532
570
|
for (const p of walkUp(this.cwd)) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (hasAny) {
|
|
571
|
+
const hasNodeModules = await stat(resolve(p, 'node_modules'))
|
|
572
|
+
.then((st) => st.isDirectory())
|
|
573
|
+
.catch(() => false)
|
|
574
|
+
|
|
575
|
+
const hasPackageJson = await stat(resolve(p, 'package.json'))
|
|
576
|
+
.then((st) => st.isFile())
|
|
577
|
+
.catch(() => false)
|
|
578
|
+
|
|
579
|
+
if (!this.localPrefix && (hasNodeModules || hasPackageJson)) {
|
|
543
580
|
this.localPrefix = p
|
|
544
|
-
|
|
581
|
+
|
|
582
|
+
// if workspaces are disabled, return now
|
|
583
|
+
if (cliWorkspaces === false) {
|
|
584
|
+
return
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// otherwise, continue the loop
|
|
588
|
+
continue
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (this.localPrefix && hasPackageJson) {
|
|
592
|
+
// if we already set localPrefix but this dir has a package.json
|
|
593
|
+
// then we need to see if `p` is a workspace root by reading its package.json
|
|
594
|
+
// however, if reading it fails then we should just move on
|
|
595
|
+
const pkg = await rpj(resolve(p, 'package.json')).catch(() => false)
|
|
596
|
+
if (!pkg) {
|
|
597
|
+
continue
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const workspaces = await mapWorkspaces({ cwd: p, pkg })
|
|
601
|
+
for (const w of workspaces.values()) {
|
|
602
|
+
if (w === this.localPrefix) {
|
|
603
|
+
// see if there's a .npmrc file in the workspace, if so log a warning
|
|
604
|
+
const hasNpmrc = await stat(resolve(this.localPrefix, '.npmrc'))
|
|
605
|
+
.then((st) => st.isFile())
|
|
606
|
+
.catch(() => false)
|
|
607
|
+
|
|
608
|
+
if (hasNpmrc) {
|
|
609
|
+
log.warn(`ignoring workspace config at ${this.localPrefix}/.npmrc`)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// set the workspace in the default layer, which allows it to be overridden easily
|
|
613
|
+
const { data } = this.data.get('default')
|
|
614
|
+
data.workspace = [this.localPrefix]
|
|
615
|
+
this.localPrefix = p
|
|
616
|
+
log.info(`found workspace root at ${this.localPrefix}`)
|
|
617
|
+
// we found a root, so we return now
|
|
618
|
+
return
|
|
619
|
+
}
|
|
620
|
+
}
|
|
545
621
|
}
|
|
546
622
|
}
|
|
547
623
|
|
|
548
|
-
this.localPrefix
|
|
624
|
+
if (!this.localPrefix) {
|
|
625
|
+
this.localPrefix = this.cwd
|
|
626
|
+
}
|
|
549
627
|
}
|
|
550
628
|
|
|
551
629
|
loadUserConfig () {
|
|
@@ -557,10 +635,12 @@ class Config {
|
|
|
557
635
|
}
|
|
558
636
|
|
|
559
637
|
async save (where) {
|
|
560
|
-
if (!this.loaded)
|
|
638
|
+
if (!this.loaded) {
|
|
561
639
|
throw new Error('call config.load() before saving')
|
|
562
|
-
|
|
640
|
+
}
|
|
641
|
+
if (!confFileTypes.has(where)) {
|
|
563
642
|
throw new Error('invalid config location param: ' + where)
|
|
643
|
+
}
|
|
564
644
|
const conf = this.data.get(where)
|
|
565
645
|
conf[_raw] = { ...conf.data }
|
|
566
646
|
conf[_loadError] = null
|
|
@@ -572,7 +652,9 @@ class Config {
|
|
|
572
652
|
// we ignore this error because the failed set already removed
|
|
573
653
|
// anything that might be a security hazard, and it won't be
|
|
574
654
|
// saved back to the .npmrc file, so we're good.
|
|
575
|
-
try {
|
|
655
|
+
try {
|
|
656
|
+
this.setCredentialsByURI(reg, creds)
|
|
657
|
+
} catch (_) {}
|
|
576
658
|
}
|
|
577
659
|
|
|
578
660
|
const iniData = ini.stringify(conf.data).trim() + '\n'
|
|
@@ -588,8 +670,9 @@ class Config {
|
|
|
588
670
|
/* istanbul ignore if - this is best-effort and a pita to test */
|
|
589
671
|
if (myUid === 0) {
|
|
590
672
|
const st = await stat(dir).catch(() => null)
|
|
591
|
-
if (st && (st.uid !== myUid || st.gid !== myGid))
|
|
673
|
+
if (st && (st.uid !== myUid || st.gid !== myGid)) {
|
|
592
674
|
await chown(conf.source, st.uid, st.gid).catch(() => {})
|
|
675
|
+
}
|
|
593
676
|
}
|
|
594
677
|
const mode = where === 'user' ? 0o600 : 0o666
|
|
595
678
|
await chmod(conf.source, mode)
|
|
@@ -637,8 +720,9 @@ class Config {
|
|
|
637
720
|
email = email ||
|
|
638
721
|
this.get('email', 'user') ||
|
|
639
722
|
this.get(`${nerfed}:email`, 'user')
|
|
640
|
-
if (email)
|
|
723
|
+
if (email) {
|
|
641
724
|
this.set('email', email, 'user')
|
|
725
|
+
}
|
|
642
726
|
}
|
|
643
727
|
|
|
644
728
|
// field that hasn't been used as documented for a LONG time,
|
|
@@ -654,10 +738,12 @@ class Config {
|
|
|
654
738
|
this.delete(`${nerfed}:_password`, 'user')
|
|
655
739
|
this.delete(`${nerfed}:username`, 'user')
|
|
656
740
|
} else if (username || password) {
|
|
657
|
-
if (!username)
|
|
741
|
+
if (!username) {
|
|
658
742
|
throw new Error('must include username')
|
|
659
|
-
|
|
743
|
+
}
|
|
744
|
+
if (!password) {
|
|
660
745
|
throw new Error('must include password')
|
|
746
|
+
}
|
|
661
747
|
this.delete(`${nerfed}:_authToken`, 'user')
|
|
662
748
|
this.set(`${nerfed}:username`, username, 'user')
|
|
663
749
|
// note: not encrypted, no idea why we bothered to do this, but oh well
|
|
@@ -675,8 +761,9 @@ class Config {
|
|
|
675
761
|
const creds = {}
|
|
676
762
|
|
|
677
763
|
const email = this.get(`${nerfed}:email`) || this.get('email')
|
|
678
|
-
if (email)
|
|
764
|
+
if (email) {
|
|
679
765
|
creds.email = email
|
|
766
|
+
}
|
|
680
767
|
|
|
681
768
|
const tokenReg = this.get(`${nerfed}:_authToken`) ||
|
|
682
769
|
this.get(`${nerfed}:_authtoken`) ||
|
|
@@ -711,8 +798,9 @@ class Config {
|
|
|
711
798
|
// at this point, we can only use the values if the URI is the
|
|
712
799
|
// default registry.
|
|
713
800
|
const defaultNerf = nerfDart(this.get('registry'))
|
|
714
|
-
if (nerfed !== defaultNerf)
|
|
801
|
+
if (nerfed !== defaultNerf) {
|
|
715
802
|
return creds
|
|
803
|
+
}
|
|
716
804
|
|
|
717
805
|
const userDef = this.get('username')
|
|
718
806
|
const passDef = this.get('_password')
|
|
@@ -727,8 +815,9 @@ class Config {
|
|
|
727
815
|
// Handle the old-style _auth=<base64> style for the default
|
|
728
816
|
// registry, if set.
|
|
729
817
|
const auth = this.get('_auth')
|
|
730
|
-
if (!auth)
|
|
818
|
+
if (!auth) {
|
|
731
819
|
return creds
|
|
820
|
+
}
|
|
732
821
|
|
|
733
822
|
const authDecode = Buffer.from(auth, 'base64').toString('utf8')
|
|
734
823
|
const authSplit = authDecode.split(':')
|
|
@@ -741,7 +830,9 @@ class Config {
|
|
|
741
830
|
// set up the environment object we have with npm_config_* environs
|
|
742
831
|
// for all configs that are different from their default values, and
|
|
743
832
|
// set EDITOR and HOME.
|
|
744
|
-
setEnvs () {
|
|
833
|
+
setEnvs () {
|
|
834
|
+
setEnvs(this)
|
|
835
|
+
}
|
|
745
836
|
}
|
|
746
837
|
|
|
747
838
|
const _data = Symbol('data')
|
|
@@ -767,25 +858,37 @@ class ConfigData {
|
|
|
767
858
|
}
|
|
768
859
|
|
|
769
860
|
set source (s) {
|
|
770
|
-
if (this[_source])
|
|
861
|
+
if (this[_source]) {
|
|
771
862
|
throw new Error('cannot set ConfigData source more than once')
|
|
863
|
+
}
|
|
772
864
|
this[_source] = s
|
|
773
865
|
}
|
|
774
|
-
|
|
866
|
+
|
|
867
|
+
get source () {
|
|
868
|
+
return this[_source]
|
|
869
|
+
}
|
|
775
870
|
|
|
776
871
|
set loadError (e) {
|
|
777
|
-
if (this[_loadError] || this[_raw])
|
|
872
|
+
if (this[_loadError] || this[_raw]) {
|
|
778
873
|
throw new Error('cannot set ConfigData loadError after load')
|
|
874
|
+
}
|
|
779
875
|
this[_loadError] = e
|
|
780
876
|
}
|
|
781
|
-
|
|
877
|
+
|
|
878
|
+
get loadError () {
|
|
879
|
+
return this[_loadError]
|
|
880
|
+
}
|
|
782
881
|
|
|
783
882
|
set raw (r) {
|
|
784
|
-
if (this[_raw] || this[_loadError])
|
|
883
|
+
if (this[_raw] || this[_loadError]) {
|
|
785
884
|
throw new Error('cannot set ConfigData raw after load')
|
|
885
|
+
}
|
|
786
886
|
this[_raw] = r
|
|
787
887
|
}
|
|
788
|
-
|
|
888
|
+
|
|
889
|
+
get raw () {
|
|
890
|
+
return this[_raw]
|
|
891
|
+
}
|
|
789
892
|
}
|
|
790
893
|
|
|
791
894
|
module.exports = Config
|
package/lib/parse-field.js
CHANGED
|
@@ -6,10 +6,11 @@ const { resolve } = require('path')
|
|
|
6
6
|
const { parse: umaskParse } = require('./umask.js')
|
|
7
7
|
|
|
8
8
|
const parseField = (f, key, opts, listElement = false) => {
|
|
9
|
-
if (typeof f !== 'string' && !Array.isArray(f))
|
|
9
|
+
if (typeof f !== 'string' && !Array.isArray(f)) {
|
|
10
10
|
return f
|
|
11
|
+
}
|
|
11
12
|
|
|
12
|
-
const { platform, types,
|
|
13
|
+
const { platform, types, home, env } = opts
|
|
13
14
|
|
|
14
15
|
// type can be array or a single thing. coerce to array.
|
|
15
16
|
const typeList = new Set([].concat(types[key]))
|
|
@@ -20,8 +21,9 @@ const parseField = (f, key, opts, listElement = false) => {
|
|
|
20
21
|
const isNumber = typeList.has(typeDefs.Number.type)
|
|
21
22
|
const isList = !listElement && typeList.has(Array)
|
|
22
23
|
|
|
23
|
-
if (Array.isArray(f))
|
|
24
|
+
if (Array.isArray(f)) {
|
|
24
25
|
return !isList ? f : f.map(field => parseField(field, key, opts, true))
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
// now we know it's a string
|
|
27
29
|
f = f.trim()
|
|
@@ -29,12 +31,14 @@ const parseField = (f, key, opts, listElement = false) => {
|
|
|
29
31
|
// list types get put in the environment separated by double-\n
|
|
30
32
|
// usually a single \n would suffice, but ca/cert configs can contain
|
|
31
33
|
// line breaks and multiple entries.
|
|
32
|
-
if (isList)
|
|
34
|
+
if (isList) {
|
|
33
35
|
return parseField(f.split('\n\n'), key, opts)
|
|
36
|
+
}
|
|
34
37
|
|
|
35
38
|
// --foo is like --foo=true for boolean types
|
|
36
|
-
if (isBool && !isString && f === '')
|
|
39
|
+
if (isBool && !isString && f === '') {
|
|
37
40
|
return true
|
|
41
|
+
}
|
|
38
42
|
|
|
39
43
|
// string types can be the string 'true', 'false', etc.
|
|
40
44
|
// otherwise, parse these values out
|
|
@@ -51,10 +55,11 @@ const parseField = (f, key, opts, listElement = false) => {
|
|
|
51
55
|
|
|
52
56
|
if (isPath) {
|
|
53
57
|
const homePattern = platform === 'win32' ? /^~(\/|\\)/ : /^~\//
|
|
54
|
-
if (homePattern.test(f) && home)
|
|
58
|
+
if (homePattern.test(f) && home) {
|
|
55
59
|
f = resolve(home, f.substr(2))
|
|
56
|
-
else
|
|
60
|
+
} else {
|
|
57
61
|
f = resolve(f)
|
|
62
|
+
}
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
if (isUmask) {
|
|
@@ -66,8 +71,9 @@ const parseField = (f, key, opts, listElement = false) => {
|
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
if (isNumber && !isNaN(f))
|
|
74
|
+
if (isNumber && !isNaN(f)) {
|
|
70
75
|
f = +f
|
|
76
|
+
}
|
|
71
77
|
|
|
72
78
|
return f
|
|
73
79
|
}
|
package/lib/set-envs.js
CHANGED
|
@@ -22,15 +22,17 @@ const sameConfigValue = (def, val) =>
|
|
|
22
22
|
: sameArrayValue(def, val)
|
|
23
23
|
|
|
24
24
|
const sameArrayValue = (def, val) => {
|
|
25
|
-
if (def.length !== val.length)
|
|
25
|
+
if (def.length !== val.length) {
|
|
26
26
|
return false
|
|
27
|
+
}
|
|
27
28
|
|
|
28
29
|
for (let i = 0; i < def.length; i++) {
|
|
29
30
|
/* istanbul ignore next - there are no array configs where the default
|
|
30
31
|
* is not an empty array, so this loop is a no-op, but it's the correct
|
|
31
32
|
* thing to do if we ever DO add a config like that. */
|
|
32
|
-
if (def[i] !== val[i])
|
|
33
|
+
if (def[i] !== val[i]) {
|
|
33
34
|
return false
|
|
35
|
+
}
|
|
34
36
|
}
|
|
35
37
|
return true
|
|
36
38
|
}
|
|
@@ -38,16 +40,15 @@ const sameArrayValue = (def, val) => {
|
|
|
38
40
|
const setEnv = (env, rawKey, rawVal) => {
|
|
39
41
|
const val = envVal(rawVal)
|
|
40
42
|
const key = envKey(rawKey, val)
|
|
41
|
-
if (key && val !== null)
|
|
43
|
+
if (key && val !== null) {
|
|
42
44
|
env[key] = val
|
|
45
|
+
}
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
const setEnvs = (config) => {
|
|
46
49
|
// This ensures that all npm config values that are not the defaults are
|
|
47
50
|
// shared appropriately with child processes, without false positives.
|
|
48
51
|
const {
|
|
49
|
-
globalPrefix,
|
|
50
|
-
platform,
|
|
51
52
|
env,
|
|
52
53
|
defaults,
|
|
53
54
|
definitions,
|
|
@@ -68,19 +69,22 @@ const setEnvs = (config) => {
|
|
|
68
69
|
const envSet = new Set(Object.keys(envConf))
|
|
69
70
|
for (const key in cliConf) {
|
|
70
71
|
const { deprecated, envExport = true } = definitions[key] || {}
|
|
71
|
-
if (deprecated || envExport === false)
|
|
72
|
+
if (deprecated || envExport === false) {
|
|
72
73
|
continue
|
|
74
|
+
}
|
|
73
75
|
|
|
74
76
|
if (sameConfigValue(defaults[key], cliConf[key])) {
|
|
75
77
|
// config is the default, if the env thought different, then we
|
|
76
78
|
// have to set it BACK to the default in the environment.
|
|
77
|
-
if (!sameConfigValue(envConf[key], cliConf[key]))
|
|
79
|
+
if (!sameConfigValue(envConf[key], cliConf[key])) {
|
|
78
80
|
setEnv(env, key, cliConf[key])
|
|
81
|
+
}
|
|
79
82
|
} else {
|
|
80
83
|
// config is not the default. if the env wasn't the one to set
|
|
81
84
|
// it that way, then we have to put it in the env
|
|
82
|
-
if (!(envSet.has(key) && !cliSet.has(key)))
|
|
85
|
+
if (!(envSet.has(key) && !cliSet.has(key))) {
|
|
83
86
|
setEnv(env, key, cliConf[key])
|
|
87
|
+
}
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
|
|
@@ -88,16 +92,19 @@ const setEnvs = (config) => {
|
|
|
88
92
|
env.HOME = config.home
|
|
89
93
|
env.npm_config_global_prefix = config.globalPrefix
|
|
90
94
|
env.npm_config_local_prefix = config.localPrefix
|
|
91
|
-
if (cliConf.editor)
|
|
95
|
+
if (cliConf.editor) {
|
|
92
96
|
env.EDITOR = cliConf.editor
|
|
97
|
+
}
|
|
93
98
|
|
|
94
99
|
// note: this doesn't afect the *current* node process, of course, since
|
|
95
100
|
// it's already started, but it does affect the options passed to scripts.
|
|
96
|
-
if (cliConf['node-options'])
|
|
101
|
+
if (cliConf['node-options']) {
|
|
97
102
|
env.NODE_OPTIONS = cliConf['node-options']
|
|
103
|
+
}
|
|
98
104
|
|
|
99
|
-
if (require.main && require.main.filename)
|
|
105
|
+
if (require.main && require.main.filename) {
|
|
100
106
|
env.npm_execpath = require.main.filename
|
|
107
|
+
}
|
|
101
108
|
env.NODE = env.npm_node_execpath = config.execPath
|
|
102
109
|
}
|
|
103
110
|
|
package/lib/type-defs.js
CHANGED
|
@@ -5,15 +5,17 @@ const { Umask, validate: validateUmask } = require('./umask.js')
|
|
|
5
5
|
const semver = require('semver')
|
|
6
6
|
const validateSemver = (data, k, val) => {
|
|
7
7
|
const valid = semver.valid(val)
|
|
8
|
-
if (!valid)
|
|
8
|
+
if (!valid) {
|
|
9
9
|
return false
|
|
10
|
+
}
|
|
10
11
|
data[k] = valid
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const noptValidatePath = nopt.typeDefs.path.validate
|
|
14
15
|
const validatePath = (data, k, val) => {
|
|
15
|
-
if (typeof val !== 'string')
|
|
16
|
+
if (typeof val !== 'string') {
|
|
16
17
|
return false
|
|
18
|
+
}
|
|
17
19
|
return noptValidatePath(data, k, val)
|
|
18
20
|
}
|
|
19
21
|
|
package/lib/type-description.js
CHANGED
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
// returns a string for one thing, or an array of descriptions
|
|
3
3
|
const typeDefs = require('./type-defs.js')
|
|
4
4
|
const typeDescription = t => {
|
|
5
|
-
if (!t || typeof t !== 'function' && typeof t !== 'object')
|
|
5
|
+
if (!t || typeof t !== 'function' && typeof t !== 'object') {
|
|
6
6
|
return t
|
|
7
|
+
}
|
|
7
8
|
|
|
8
|
-
if (Array.isArray(t))
|
|
9
|
+
if (Array.isArray(t)) {
|
|
9
10
|
return t.map(t => typeDescription(t))
|
|
11
|
+
}
|
|
10
12
|
|
|
11
13
|
for (const { type, description } of Object.values(typeDefs)) {
|
|
12
|
-
if (type === t)
|
|
14
|
+
if (type === t) {
|
|
13
15
|
return description || type
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
return t
|
package/lib/umask.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
class Umask {}
|
|
2
2
|
const parse = val => {
|
|
3
3
|
if (typeof val === 'string') {
|
|
4
|
-
if (/^0o?[0-7]+$/.test(val))
|
|
4
|
+
if (/^0o?[0-7]+$/.test(val)) {
|
|
5
5
|
return parseInt(val.replace(/^0o?/, ''), 8)
|
|
6
|
-
else if (/^[1-9][0-9]*$/.test(val))
|
|
6
|
+
} else if (/^[1-9][0-9]*$/.test(val)) {
|
|
7
7
|
return parseInt(val, 10)
|
|
8
|
-
else
|
|
8
|
+
} else {
|
|
9
9
|
throw new Error(`invalid umask value: ${val}`)
|
|
10
|
+
}
|
|
10
11
|
}
|
|
11
|
-
if (typeof val !== 'number')
|
|
12
|
+
if (typeof val !== 'number') {
|
|
12
13
|
throw new Error(`invalid umask value: ${val}`)
|
|
14
|
+
}
|
|
13
15
|
val = Math.floor(val)
|
|
14
|
-
if (val < 0 || val > 511)
|
|
16
|
+
if (val < 0 || val > 511) {
|
|
15
17
|
throw new Error(`invalid umask value: ${val}`)
|
|
18
|
+
}
|
|
16
19
|
return val
|
|
17
20
|
}
|
|
18
21
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"files": [
|
|
5
|
+
"bin",
|
|
5
6
|
"lib"
|
|
6
7
|
],
|
|
7
8
|
"main": "lib/index.js",
|
|
@@ -10,30 +11,42 @@
|
|
|
10
11
|
"type": "git",
|
|
11
12
|
"url": "git+https://github.com/npm/config"
|
|
12
13
|
},
|
|
13
|
-
"author": "
|
|
14
|
+
"author": "GitHub Inc.",
|
|
14
15
|
"license": "ISC",
|
|
15
16
|
"scripts": {
|
|
16
17
|
"test": "tap",
|
|
17
18
|
"snap": "tap",
|
|
18
19
|
"preversion": "npm test",
|
|
19
20
|
"postversion": "npm publish",
|
|
20
|
-
"prepublishOnly": "git push origin --follow-tags"
|
|
21
|
+
"prepublishOnly": "git push origin --follow-tags",
|
|
22
|
+
"lint": "eslint '**/*.js'",
|
|
23
|
+
"postlint": "npm-template-check",
|
|
24
|
+
"lintfix": "npm run lint -- --fix",
|
|
25
|
+
"posttest": "npm run lint",
|
|
26
|
+
"template-copy": "npm-template-copy --force"
|
|
21
27
|
},
|
|
22
28
|
"tap": {
|
|
23
29
|
"check-coverage": true,
|
|
24
30
|
"coverage-map": "map.js"
|
|
25
31
|
},
|
|
26
32
|
"devDependencies": {
|
|
27
|
-
"
|
|
33
|
+
"@npmcli/template-oss": "^2.7.1",
|
|
34
|
+
"tap": "^15.1.6"
|
|
28
35
|
},
|
|
29
36
|
"dependencies": {
|
|
37
|
+
"@npmcli/map-workspaces": "^2.0.0",
|
|
30
38
|
"ini": "^2.0.0",
|
|
31
39
|
"mkdirp-infer-owner": "^2.0.0",
|
|
32
40
|
"nopt": "^5.0.0",
|
|
33
|
-
"
|
|
41
|
+
"proc-log": "^2.0.0",
|
|
42
|
+
"read-package-json-fast": "^2.0.3",
|
|
43
|
+
"semver": "^7.3.5",
|
|
34
44
|
"walk-up-path": "^1.0.0"
|
|
35
45
|
},
|
|
36
46
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
47
|
+
"node": "^12.13.0 || ^14.15.0 || >=16"
|
|
48
|
+
},
|
|
49
|
+
"templateOSS": {
|
|
50
|
+
"version": "2.7.1"
|
|
38
51
|
}
|
|
39
52
|
}
|
package/lib/proc-log.js
DELETED