@npmcli/config 2.4.0 → 3.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/lib/env-replace.js +3 -2
- package/lib/index.js +148 -58
- 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 +16 -4
package/lib/env-replace.js
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
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,8 @@ 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')
|
|
6
8
|
|
|
7
9
|
/* istanbul ignore next */
|
|
8
10
|
const myUid = process.getuid && process.getuid()
|
|
@@ -91,7 +93,6 @@ class Config {
|
|
|
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
|
|
@@ -162,41 +164,48 @@ class Config {
|
|
|
162
164
|
|
|
163
165
|
// return the location where key is found.
|
|
164
166
|
find (key) {
|
|
165
|
-
if (!this.loaded)
|
|
167
|
+
if (!this.loaded) {
|
|
166
168
|
throw new Error('call config.load() before reading values')
|
|
169
|
+
}
|
|
167
170
|
return this[_find](key)
|
|
168
171
|
}
|
|
172
|
+
|
|
169
173
|
[_find] (key) {
|
|
170
174
|
// have to look in reverse order
|
|
171
175
|
const entries = [...this.data.entries()]
|
|
172
176
|
for (let i = entries.length - 1; i > -1; i--) {
|
|
173
177
|
const [where, { data }] = entries[i]
|
|
174
|
-
if (hasOwnProperty(data, key))
|
|
178
|
+
if (hasOwnProperty(data, key)) {
|
|
175
179
|
return where
|
|
180
|
+
}
|
|
176
181
|
}
|
|
177
182
|
return null
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
get (key, where) {
|
|
181
|
-
if (!this.loaded)
|
|
186
|
+
if (!this.loaded) {
|
|
182
187
|
throw new Error('call config.load() before reading values')
|
|
188
|
+
}
|
|
183
189
|
return this[_get](key, where)
|
|
184
190
|
}
|
|
191
|
+
|
|
185
192
|
// we need to get values sometimes, so use this internal one to do so
|
|
186
193
|
// while in the process of loading.
|
|
187
194
|
[_get] (key, where = null) {
|
|
188
195
|
if (where !== null && !confTypes.has(where)) {
|
|
189
196
|
throw new Error('invalid config location param: ' + where)
|
|
190
197
|
}
|
|
191
|
-
const { data
|
|
198
|
+
const { data } = this.data.get(where || 'cli')
|
|
192
199
|
return where === null || hasOwnProperty(data, key) ? data[key] : undefined
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
set (key, val, where = 'cli') {
|
|
196
|
-
if (!this.loaded)
|
|
203
|
+
if (!this.loaded) {
|
|
197
204
|
throw new Error('call config.load() before setting values')
|
|
198
|
-
|
|
205
|
+
}
|
|
206
|
+
if (!confTypes.has(where)) {
|
|
199
207
|
throw new Error('invalid config location param: ' + where)
|
|
208
|
+
}
|
|
200
209
|
this[_checkDeprecated](key)
|
|
201
210
|
const { data } = this.data.get(where)
|
|
202
211
|
data[key] = val
|
|
@@ -209,8 +218,9 @@ class Config {
|
|
|
209
218
|
}
|
|
210
219
|
|
|
211
220
|
get flat () {
|
|
212
|
-
if (this[_flatOptions])
|
|
221
|
+
if (this[_flatOptions]) {
|
|
213
222
|
return this[_flatOptions]
|
|
223
|
+
}
|
|
214
224
|
|
|
215
225
|
// create the object for flat options passed to deps
|
|
216
226
|
process.emit('time', 'config:load:flatten')
|
|
@@ -225,16 +235,19 @@ class Config {
|
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
delete (key, where = 'cli') {
|
|
228
|
-
if (!this.loaded)
|
|
238
|
+
if (!this.loaded) {
|
|
229
239
|
throw new Error('call config.load() before deleting values')
|
|
230
|
-
|
|
240
|
+
}
|
|
241
|
+
if (!confTypes.has(where)) {
|
|
231
242
|
throw new Error('invalid config location param: ' + where)
|
|
243
|
+
}
|
|
232
244
|
delete this.data.get(where).data[key]
|
|
233
245
|
}
|
|
234
246
|
|
|
235
247
|
async load () {
|
|
236
|
-
if (this.loaded)
|
|
248
|
+
if (this.loaded) {
|
|
237
249
|
throw new Error('attempting to load npm config multiple times')
|
|
250
|
+
}
|
|
238
251
|
|
|
239
252
|
process.emit('time', 'config:load')
|
|
240
253
|
// first load the defaults, which sets the global prefix
|
|
@@ -282,7 +295,9 @@ class Config {
|
|
|
282
295
|
const creds = this.getCredentialsByURI(reg)
|
|
283
296
|
// ignore this error because a failed set will strip out anything that
|
|
284
297
|
// might be a security hazard, which was the intention.
|
|
285
|
-
try {
|
|
298
|
+
try {
|
|
299
|
+
this.setCredentialsByURI(reg, creds)
|
|
300
|
+
} catch (_) {}
|
|
286
301
|
process.emit('timeEnd', 'config:load:credentials')
|
|
287
302
|
|
|
288
303
|
// set proper globalPrefix now that everything is loaded
|
|
@@ -319,14 +334,16 @@ class Config {
|
|
|
319
334
|
}
|
|
320
335
|
|
|
321
336
|
loadHome () {
|
|
322
|
-
if (this.env.HOME)
|
|
337
|
+
if (this.env.HOME) {
|
|
323
338
|
return this.home = this.env.HOME
|
|
339
|
+
}
|
|
324
340
|
this.home = homedir()
|
|
325
341
|
}
|
|
326
342
|
|
|
327
343
|
loadGlobalPrefix () {
|
|
328
|
-
if (this.globalPrefix)
|
|
344
|
+
if (this.globalPrefix) {
|
|
329
345
|
throw new Error('cannot load default global prefix more than once')
|
|
346
|
+
}
|
|
330
347
|
|
|
331
348
|
if (this.env.PREFIX) {
|
|
332
349
|
this.globalPrefix = this.env.PREFIX
|
|
@@ -338,17 +355,18 @@ class Config {
|
|
|
338
355
|
this.globalPrefix = dirname(dirname(this.execPath))
|
|
339
356
|
|
|
340
357
|
// destdir only is respected on Unix
|
|
341
|
-
if (this.env.DESTDIR)
|
|
358
|
+
if (this.env.DESTDIR) {
|
|
342
359
|
this.globalPrefix = join(this.env.DESTDIR, this.globalPrefix)
|
|
360
|
+
}
|
|
343
361
|
}
|
|
344
362
|
}
|
|
345
363
|
|
|
346
364
|
loadEnv () {
|
|
347
|
-
const prefix = 'npm_config_'
|
|
348
365
|
const conf = Object.create(null)
|
|
349
366
|
for (const [envKey, envVal] of Object.entries(this.env)) {
|
|
350
|
-
if (!/^npm_config_/i.test(envKey) || envVal === '')
|
|
367
|
+
if (!/^npm_config_/i.test(envKey) || envVal === '') {
|
|
351
368
|
continue
|
|
369
|
+
}
|
|
352
370
|
const key = envKey.substr('npm_config_'.length)
|
|
353
371
|
.replace(/(?!^)_/g, '-') // don't replace _ at the start of the key
|
|
354
372
|
.toLowerCase()
|
|
@@ -368,9 +386,10 @@ class Config {
|
|
|
368
386
|
}
|
|
369
387
|
|
|
370
388
|
get valid () {
|
|
371
|
-
for (const [where, {valid}] of this.data.entries()) {
|
|
372
|
-
if (valid === false || valid === null && !this.validate(where))
|
|
389
|
+
for (const [where, { valid }] of this.data.entries()) {
|
|
390
|
+
if (valid === false || valid === null && !this.validate(where)) {
|
|
373
391
|
return false
|
|
392
|
+
}
|
|
374
393
|
}
|
|
375
394
|
return true
|
|
376
395
|
}
|
|
@@ -378,11 +397,12 @@ class Config {
|
|
|
378
397
|
validate (where) {
|
|
379
398
|
if (!where) {
|
|
380
399
|
let valid = true
|
|
381
|
-
for (const [where
|
|
400
|
+
for (const [where] of this.data.entries()) {
|
|
382
401
|
// no need to validate our defaults, we know they're fine
|
|
383
402
|
// cli was already validated when parsed the first time
|
|
384
|
-
if (where === 'default' || where === 'builtin' || where === 'cli')
|
|
403
|
+
if (where === 'default' || where === 'builtin' || where === 'cli') {
|
|
385
404
|
continue
|
|
405
|
+
}
|
|
386
406
|
const ret = this.validate(where)
|
|
387
407
|
valid = valid && ret
|
|
388
408
|
}
|
|
@@ -424,14 +444,15 @@ class Config {
|
|
|
424
444
|
this.data.get(where)[_valid] = false
|
|
425
445
|
|
|
426
446
|
if (Array.isArray(type)) {
|
|
427
|
-
if (type.includes(typeDefs.url.type))
|
|
447
|
+
if (type.includes(typeDefs.url.type)) {
|
|
428
448
|
type = typeDefs.url.type
|
|
429
|
-
else {
|
|
449
|
+
} else {
|
|
430
450
|
/* istanbul ignore if - no actual configs matching this, but
|
|
431
451
|
* path types SHOULD be handled this way, like URLs, for the
|
|
432
452
|
* same reason */
|
|
433
|
-
if (type.includes(typeDefs.path.type))
|
|
453
|
+
if (type.includes(typeDefs.path.type)) {
|
|
434
454
|
type = typeDefs.path.type
|
|
455
|
+
}
|
|
435
456
|
}
|
|
436
457
|
}
|
|
437
458
|
|
|
@@ -469,15 +490,17 @@ class Config {
|
|
|
469
490
|
this.sources.set(source, where)
|
|
470
491
|
if (er) {
|
|
471
492
|
conf.loadError = er
|
|
472
|
-
if (er.code !== 'ENOENT')
|
|
493
|
+
if (er.code !== 'ENOENT') {
|
|
473
494
|
this.log.verbose('config', `error loading ${where} config`, er)
|
|
495
|
+
}
|
|
474
496
|
} else {
|
|
475
497
|
conf.raw = obj
|
|
476
498
|
for (const [key, value] of Object.entries(obj)) {
|
|
477
499
|
const k = envReplace(key, this.env)
|
|
478
500
|
const v = this.parseField(value, k)
|
|
479
|
-
if (where !== 'default')
|
|
501
|
+
if (where !== 'default') {
|
|
480
502
|
this[_checkDeprecated](k, where, obj, [key, value])
|
|
503
|
+
}
|
|
481
504
|
conf.data[k] = v
|
|
482
505
|
}
|
|
483
506
|
}
|
|
@@ -528,9 +551,9 @@ class Config {
|
|
|
528
551
|
// up loading the "project" config where the "userconfig" will be,
|
|
529
552
|
// which causes some calamaties. So, we only load project config if
|
|
530
553
|
// it doesn't match what the userconfig will be.
|
|
531
|
-
if (projectFile !== this[_get]('userconfig'))
|
|
554
|
+
if (projectFile !== this[_get]('userconfig')) {
|
|
532
555
|
return this[_loadFile](projectFile, 'project')
|
|
533
|
-
else {
|
|
556
|
+
} else {
|
|
534
557
|
this.data.get('project').source = '(same as "user" config, ignored)'
|
|
535
558
|
this.sources.set(this.data.get('project').source, 'project')
|
|
536
559
|
}
|
|
@@ -543,23 +566,65 @@ class Config {
|
|
|
543
566
|
return
|
|
544
567
|
}
|
|
545
568
|
|
|
569
|
+
const cliWorkspaces = this[_get]('workspaces', 'cli')
|
|
570
|
+
|
|
546
571
|
for (const p of walkUp(this.cwd)) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
if (hasAny) {
|
|
572
|
+
const hasNodeModules = await stat(resolve(p, 'node_modules'))
|
|
573
|
+
.then((st) => st.isDirectory())
|
|
574
|
+
.catch(() => false)
|
|
575
|
+
|
|
576
|
+
const hasPackageJson = await stat(resolve(p, 'package.json'))
|
|
577
|
+
.then((st) => st.isFile())
|
|
578
|
+
.catch(() => false)
|
|
579
|
+
|
|
580
|
+
if (!this.localPrefix && (hasNodeModules || hasPackageJson)) {
|
|
557
581
|
this.localPrefix = p
|
|
558
|
-
|
|
582
|
+
|
|
583
|
+
// if workspaces are disabled, return now
|
|
584
|
+
if (cliWorkspaces === false) {
|
|
585
|
+
return
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// otherwise, continue the loop
|
|
589
|
+
continue
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (this.localPrefix && hasPackageJson) {
|
|
593
|
+
// if we already set localPrefix but this dir has a package.json
|
|
594
|
+
// then we need to see if `p` is a workspace root by reading its package.json
|
|
595
|
+
// however, if reading it fails then we should just move on
|
|
596
|
+
const pkg = await rpj(resolve(p, 'package.json')).catch(() => false)
|
|
597
|
+
if (!pkg) {
|
|
598
|
+
continue
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const workspaces = await mapWorkspaces({ cwd: p, pkg })
|
|
602
|
+
for (const w of workspaces.values()) {
|
|
603
|
+
if (w === this.localPrefix) {
|
|
604
|
+
// see if there's a .npmrc file in the workspace, if so log a warning
|
|
605
|
+
const hasNpmrc = await stat(resolve(this.localPrefix, '.npmrc'))
|
|
606
|
+
.then((st) => st.isFile())
|
|
607
|
+
.catch(() => false)
|
|
608
|
+
|
|
609
|
+
if (hasNpmrc) {
|
|
610
|
+
this.log.warn(`ignoring workspace config at ${this.localPrefix}/.npmrc`)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// set the workspace in the default layer, which allows it to be overridden easily
|
|
614
|
+
const { data } = this.data.get('default')
|
|
615
|
+
data.workspace = [this.localPrefix]
|
|
616
|
+
this.localPrefix = p
|
|
617
|
+
this.log.info(`found workspace root at ${this.localPrefix}`)
|
|
618
|
+
// we found a root, so we return now
|
|
619
|
+
return
|
|
620
|
+
}
|
|
621
|
+
}
|
|
559
622
|
}
|
|
560
623
|
}
|
|
561
624
|
|
|
562
|
-
this.localPrefix
|
|
625
|
+
if (!this.localPrefix) {
|
|
626
|
+
this.localPrefix = this.cwd
|
|
627
|
+
}
|
|
563
628
|
}
|
|
564
629
|
|
|
565
630
|
loadUserConfig () {
|
|
@@ -571,10 +636,12 @@ class Config {
|
|
|
571
636
|
}
|
|
572
637
|
|
|
573
638
|
async save (where) {
|
|
574
|
-
if (!this.loaded)
|
|
639
|
+
if (!this.loaded) {
|
|
575
640
|
throw new Error('call config.load() before saving')
|
|
576
|
-
|
|
641
|
+
}
|
|
642
|
+
if (!confFileTypes.has(where)) {
|
|
577
643
|
throw new Error('invalid config location param: ' + where)
|
|
644
|
+
}
|
|
578
645
|
const conf = this.data.get(where)
|
|
579
646
|
conf[_raw] = { ...conf.data }
|
|
580
647
|
conf[_loadError] = null
|
|
@@ -586,7 +653,9 @@ class Config {
|
|
|
586
653
|
// we ignore this error because the failed set already removed
|
|
587
654
|
// anything that might be a security hazard, and it won't be
|
|
588
655
|
// saved back to the .npmrc file, so we're good.
|
|
589
|
-
try {
|
|
656
|
+
try {
|
|
657
|
+
this.setCredentialsByURI(reg, creds)
|
|
658
|
+
} catch (_) {}
|
|
590
659
|
}
|
|
591
660
|
|
|
592
661
|
const iniData = ini.stringify(conf.data).trim() + '\n'
|
|
@@ -602,8 +671,9 @@ class Config {
|
|
|
602
671
|
/* istanbul ignore if - this is best-effort and a pita to test */
|
|
603
672
|
if (myUid === 0) {
|
|
604
673
|
const st = await stat(dir).catch(() => null)
|
|
605
|
-
if (st && (st.uid !== myUid || st.gid !== myGid))
|
|
674
|
+
if (st && (st.uid !== myUid || st.gid !== myGid)) {
|
|
606
675
|
await chown(conf.source, st.uid, st.gid).catch(() => {})
|
|
676
|
+
}
|
|
607
677
|
}
|
|
608
678
|
const mode = where === 'user' ? 0o600 : 0o666
|
|
609
679
|
await chmod(conf.source, mode)
|
|
@@ -651,8 +721,9 @@ class Config {
|
|
|
651
721
|
email = email ||
|
|
652
722
|
this.get('email', 'user') ||
|
|
653
723
|
this.get(`${nerfed}:email`, 'user')
|
|
654
|
-
if (email)
|
|
724
|
+
if (email) {
|
|
655
725
|
this.set('email', email, 'user')
|
|
726
|
+
}
|
|
656
727
|
}
|
|
657
728
|
|
|
658
729
|
// field that hasn't been used as documented for a LONG time,
|
|
@@ -668,10 +739,12 @@ class Config {
|
|
|
668
739
|
this.delete(`${nerfed}:_password`, 'user')
|
|
669
740
|
this.delete(`${nerfed}:username`, 'user')
|
|
670
741
|
} else if (username || password) {
|
|
671
|
-
if (!username)
|
|
742
|
+
if (!username) {
|
|
672
743
|
throw new Error('must include username')
|
|
673
|
-
|
|
744
|
+
}
|
|
745
|
+
if (!password) {
|
|
674
746
|
throw new Error('must include password')
|
|
747
|
+
}
|
|
675
748
|
this.delete(`${nerfed}:_authToken`, 'user')
|
|
676
749
|
this.set(`${nerfed}:username`, username, 'user')
|
|
677
750
|
// note: not encrypted, no idea why we bothered to do this, but oh well
|
|
@@ -689,8 +762,9 @@ class Config {
|
|
|
689
762
|
const creds = {}
|
|
690
763
|
|
|
691
764
|
const email = this.get(`${nerfed}:email`) || this.get('email')
|
|
692
|
-
if (email)
|
|
765
|
+
if (email) {
|
|
693
766
|
creds.email = email
|
|
767
|
+
}
|
|
694
768
|
|
|
695
769
|
const tokenReg = this.get(`${nerfed}:_authToken`) ||
|
|
696
770
|
this.get(`${nerfed}:_authtoken`) ||
|
|
@@ -725,8 +799,9 @@ class Config {
|
|
|
725
799
|
// at this point, we can only use the values if the URI is the
|
|
726
800
|
// default registry.
|
|
727
801
|
const defaultNerf = nerfDart(this.get('registry'))
|
|
728
|
-
if (nerfed !== defaultNerf)
|
|
802
|
+
if (nerfed !== defaultNerf) {
|
|
729
803
|
return creds
|
|
804
|
+
}
|
|
730
805
|
|
|
731
806
|
const userDef = this.get('username')
|
|
732
807
|
const passDef = this.get('_password')
|
|
@@ -741,8 +816,9 @@ class Config {
|
|
|
741
816
|
// Handle the old-style _auth=<base64> style for the default
|
|
742
817
|
// registry, if set.
|
|
743
818
|
const auth = this.get('_auth')
|
|
744
|
-
if (!auth)
|
|
819
|
+
if (!auth) {
|
|
745
820
|
return creds
|
|
821
|
+
}
|
|
746
822
|
|
|
747
823
|
const authDecode = Buffer.from(auth, 'base64').toString('utf8')
|
|
748
824
|
const authSplit = authDecode.split(':')
|
|
@@ -755,7 +831,9 @@ class Config {
|
|
|
755
831
|
// set up the environment object we have with npm_config_* environs
|
|
756
832
|
// for all configs that are different from their default values, and
|
|
757
833
|
// set EDITOR and HOME.
|
|
758
|
-
setEnvs () {
|
|
834
|
+
setEnvs () {
|
|
835
|
+
setEnvs(this)
|
|
836
|
+
}
|
|
759
837
|
}
|
|
760
838
|
|
|
761
839
|
const _data = Symbol('data')
|
|
@@ -781,25 +859,37 @@ class ConfigData {
|
|
|
781
859
|
}
|
|
782
860
|
|
|
783
861
|
set source (s) {
|
|
784
|
-
if (this[_source])
|
|
862
|
+
if (this[_source]) {
|
|
785
863
|
throw new Error('cannot set ConfigData source more than once')
|
|
864
|
+
}
|
|
786
865
|
this[_source] = s
|
|
787
866
|
}
|
|
788
|
-
|
|
867
|
+
|
|
868
|
+
get source () {
|
|
869
|
+
return this[_source]
|
|
870
|
+
}
|
|
789
871
|
|
|
790
872
|
set loadError (e) {
|
|
791
|
-
if (this[_loadError] || this[_raw])
|
|
873
|
+
if (this[_loadError] || this[_raw]) {
|
|
792
874
|
throw new Error('cannot set ConfigData loadError after load')
|
|
875
|
+
}
|
|
793
876
|
this[_loadError] = e
|
|
794
877
|
}
|
|
795
|
-
|
|
878
|
+
|
|
879
|
+
get loadError () {
|
|
880
|
+
return this[_loadError]
|
|
881
|
+
}
|
|
796
882
|
|
|
797
883
|
set raw (r) {
|
|
798
|
-
if (this[_raw] || this[_loadError])
|
|
884
|
+
if (this[_raw] || this[_loadError]) {
|
|
799
885
|
throw new Error('cannot set ConfigData raw after load')
|
|
886
|
+
}
|
|
800
887
|
this[_raw] = r
|
|
801
888
|
}
|
|
802
|
-
|
|
889
|
+
|
|
890
|
+
get raw () {
|
|
891
|
+
return this[_raw]
|
|
892
|
+
}
|
|
803
893
|
}
|
|
804
894
|
|
|
805
895
|
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": "3.0.0",
|
|
4
4
|
"files": [
|
|
5
|
+
"bin",
|
|
5
6
|
"lib"
|
|
6
7
|
],
|
|
7
8
|
"main": "lib/index.js",
|
|
@@ -10,30 +11,41 @@
|
|
|
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": {
|
|
33
|
+
"@npmcli/template-oss": "^2.5.1",
|
|
27
34
|
"tap": "^15.0.4"
|
|
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",
|
|
41
|
+
"read-package-json-fast": "^2.0.3",
|
|
33
42
|
"semver": "^7.3.4",
|
|
34
43
|
"walk-up-path": "^1.0.0"
|
|
35
44
|
},
|
|
36
45
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
46
|
+
"node": "^12.13.0 || ^14.15.0 || >=16"
|
|
47
|
+
},
|
|
48
|
+
"templateOSS": {
|
|
49
|
+
"version": "2.6.0"
|
|
38
50
|
}
|
|
39
51
|
}
|