@npmcli/config 1.2.8 → 2.2.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/index.js +110 -97
- package/lib/set-envs.js +7 -7
- package/package.json +2 -2
- package/lib/get-user-agent.js +0 -13
package/lib/index.js
CHANGED
|
@@ -47,7 +47,6 @@ const envReplace = require('./env-replace.js')
|
|
|
47
47
|
const parseField = require('./parse-field.js')
|
|
48
48
|
const typeDescription = require('./type-description.js')
|
|
49
49
|
const setEnvs = require('./set-envs.js')
|
|
50
|
-
const getUserAgent = require('./get-user-agent.js')
|
|
51
50
|
|
|
52
51
|
// types that can be saved back to
|
|
53
52
|
const confFileTypes = new Set([
|
|
@@ -69,6 +68,9 @@ const _get = Symbol('get')
|
|
|
69
68
|
const _find = Symbol('find')
|
|
70
69
|
const _loadObject = Symbol('loadObject')
|
|
71
70
|
const _loadFile = Symbol('loadFile')
|
|
71
|
+
const _checkDeprecated = Symbol('checkDeprecated')
|
|
72
|
+
const _flatten = Symbol('flatten')
|
|
73
|
+
const _flatOptions = Symbol('flatOptions')
|
|
72
74
|
|
|
73
75
|
class Config {
|
|
74
76
|
static get typeDefs () {
|
|
@@ -76,9 +78,9 @@ class Config {
|
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
constructor ({
|
|
79
|
-
|
|
81
|
+
definitions,
|
|
80
82
|
shorthands,
|
|
81
|
-
|
|
83
|
+
flatten,
|
|
82
84
|
npmPath,
|
|
83
85
|
|
|
84
86
|
// options just to override in tests, mostly
|
|
@@ -89,10 +91,27 @@ class Config {
|
|
|
89
91
|
execPath = process.execPath,
|
|
90
92
|
cwd = process.cwd(),
|
|
91
93
|
}) {
|
|
92
|
-
|
|
94
|
+
|
|
95
|
+
// turn the definitions into nopt's weirdo syntax
|
|
96
|
+
this.definitions = definitions
|
|
97
|
+
const types = {}
|
|
98
|
+
const defaults = {}
|
|
99
|
+
this.deprecated = {}
|
|
100
|
+
for (const [key, def] of Object.entries(definitions)) {
|
|
101
|
+
defaults[key] = def.default
|
|
102
|
+
types[key] = def.type
|
|
103
|
+
if (def.deprecated)
|
|
104
|
+
this.deprecated[key] = def.deprecated.trim().replace(/\n +/, '\n')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// populated the first time we flatten the object
|
|
108
|
+
this[_flatOptions] = null
|
|
109
|
+
this[_flatten] = flatten
|
|
93
110
|
this.types = types
|
|
94
111
|
this.shorthands = shorthands
|
|
95
112
|
this.defaults = defaults
|
|
113
|
+
|
|
114
|
+
this.npmPath = npmPath
|
|
96
115
|
this.log = log
|
|
97
116
|
this.argv = argv
|
|
98
117
|
this.env = env
|
|
@@ -178,15 +197,31 @@ class Config {
|
|
|
178
197
|
throw new Error('call config.load() before setting values')
|
|
179
198
|
if (!confTypes.has(where))
|
|
180
199
|
throw new Error('invalid config location param: ' + where)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
throw new Error('Cannot set _auth without first setting email')
|
|
185
|
-
}
|
|
186
|
-
this.data.get(where).data[key] = val
|
|
200
|
+
this[_checkDeprecated](key)
|
|
201
|
+
const { data } = this.data.get(where)
|
|
202
|
+
data[key] = val
|
|
187
203
|
|
|
188
204
|
// this is now dirty, the next call to this.valid will have to check it
|
|
189
205
|
this.data.get(where)[_valid] = null
|
|
206
|
+
|
|
207
|
+
// the flat options are invalidated, regenerate next time they're needed
|
|
208
|
+
this[_flatOptions] = null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get flat () {
|
|
212
|
+
if (this[_flatOptions])
|
|
213
|
+
return this[_flatOptions]
|
|
214
|
+
|
|
215
|
+
// create the object for flat options passed to deps
|
|
216
|
+
process.emit('time', 'config:load:flatten')
|
|
217
|
+
this[_flatOptions] = {}
|
|
218
|
+
// walk from least priority to highest
|
|
219
|
+
for (const { data } of this.data.values()) {
|
|
220
|
+
this[_flatten](data, this[_flatOptions])
|
|
221
|
+
}
|
|
222
|
+
process.emit('timeEnd', 'config:load:flatten')
|
|
223
|
+
|
|
224
|
+
return this[_flatOptions]
|
|
190
225
|
}
|
|
191
226
|
|
|
192
227
|
delete (key, where = 'cli') {
|
|
@@ -233,11 +268,6 @@ class Config {
|
|
|
233
268
|
await this.loadGlobalConfig()
|
|
234
269
|
process.emit('timeEnd', 'config:load:global')
|
|
235
270
|
|
|
236
|
-
// now the extras
|
|
237
|
-
process.emit('time', 'config:load:cafile')
|
|
238
|
-
await this.loadCAFile()
|
|
239
|
-
process.emit('timeEnd', 'config:load:cafile')
|
|
240
|
-
|
|
241
271
|
// warn if anything is not valid
|
|
242
272
|
process.emit('time', 'config:load:validate')
|
|
243
273
|
this.validate()
|
|
@@ -247,13 +277,17 @@ class Config {
|
|
|
247
277
|
// symbols, as that module also does a bunch of get operations
|
|
248
278
|
this[_loaded] = true
|
|
249
279
|
|
|
280
|
+
process.emit('time', 'config:load:credentials')
|
|
281
|
+
const reg = this.get('registry')
|
|
282
|
+
const creds = this.getCredentialsByURI(reg)
|
|
283
|
+
// ignore this error because a failed set will strip out anything that
|
|
284
|
+
// might be a security hazard, which was the intention.
|
|
285
|
+
try { this.setCredentialsByURI(reg, creds) } catch (_) {}
|
|
286
|
+
process.emit('timeEnd', 'config:load:credentials')
|
|
287
|
+
|
|
250
288
|
// set proper globalPrefix now that everything is loaded
|
|
251
289
|
this.globalPrefix = this.get('prefix')
|
|
252
290
|
|
|
253
|
-
process.emit('time', 'config:load:setUserAgent')
|
|
254
|
-
this.setUserAgent()
|
|
255
|
-
process.emit('timeEnd', 'config:load:setUserAgent')
|
|
256
|
-
|
|
257
291
|
process.emit('time', 'config:load:setEnvs')
|
|
258
292
|
this.setEnvs()
|
|
259
293
|
process.emit('timeEnd', 'config:load:setEnvs')
|
|
@@ -376,13 +410,13 @@ class Config {
|
|
|
376
410
|
this.data.get(where)[_valid] = false
|
|
377
411
|
|
|
378
412
|
if (Array.isArray(type)) {
|
|
379
|
-
if (type.
|
|
413
|
+
if (type.includes(typeDefs.url.type))
|
|
380
414
|
type = typeDefs.url.type
|
|
381
415
|
else {
|
|
382
416
|
/* istanbul ignore if - no actual configs matching this, but
|
|
383
417
|
* path types SHOULD be handled this way, like URLs, for the
|
|
384
418
|
* same reason */
|
|
385
|
-
if (type.
|
|
419
|
+
if (type.includes(typeDefs.path.type))
|
|
386
420
|
type = typeDefs.path.type
|
|
387
421
|
}
|
|
388
422
|
}
|
|
@@ -428,11 +462,21 @@ class Config {
|
|
|
428
462
|
for (const [key, value] of Object.entries(obj)) {
|
|
429
463
|
const k = envReplace(key, this.env)
|
|
430
464
|
const v = this.parseField(value, k)
|
|
465
|
+
if (where !== 'default')
|
|
466
|
+
this[_checkDeprecated](k, where, obj, [key, value])
|
|
431
467
|
conf.data[k] = v
|
|
432
468
|
}
|
|
433
469
|
}
|
|
434
470
|
}
|
|
435
471
|
|
|
472
|
+
[_checkDeprecated] (key, where, obj, kv) {
|
|
473
|
+
// XXX a future npm version will make this a warning.
|
|
474
|
+
// An even more future npm version will make this an error.
|
|
475
|
+
if (this.deprecated[key]) {
|
|
476
|
+
this.log.verbose('config', key, this.deprecated[key])
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
436
480
|
// Parse a field, coercing it to the best type available.
|
|
437
481
|
parseField (f, key, listElement = false) {
|
|
438
482
|
return parseField(f, key, this, listElement)
|
|
@@ -547,14 +591,17 @@ class Config {
|
|
|
547
591
|
const nerfed = nerfDart(uri)
|
|
548
592
|
const def = nerfDart(this.get('registry'))
|
|
549
593
|
if (def === nerfed) {
|
|
594
|
+
// do not delete email, that shouldn't be nerfed any more.
|
|
595
|
+
// just delete the nerfed copy, if one exists.
|
|
550
596
|
this.delete(`-authtoken`, 'user')
|
|
551
597
|
this.delete(`_authToken`, 'user')
|
|
598
|
+
this.delete(`_authtoken`, 'user')
|
|
552
599
|
this.delete(`_auth`, 'user')
|
|
553
600
|
this.delete(`_password`, 'user')
|
|
554
601
|
this.delete(`username`, 'user')
|
|
555
|
-
this.delete(`email`, 'user')
|
|
556
602
|
}
|
|
557
603
|
this.delete(`${nerfed}:-authtoken`, 'user')
|
|
604
|
+
this.delete(`${nerfed}:_authtoken`, 'user')
|
|
558
605
|
this.delete(`${nerfed}:_authToken`, 'user')
|
|
559
606
|
this.delete(`${nerfed}:_auth`, 'user')
|
|
560
607
|
this.delete(`${nerfed}:_password`, 'user')
|
|
@@ -562,7 +609,7 @@ class Config {
|
|
|
562
609
|
this.delete(`${nerfed}:email`, 'user')
|
|
563
610
|
}
|
|
564
611
|
|
|
565
|
-
setCredentialsByURI (uri, { token, username, password, email
|
|
612
|
+
setCredentialsByURI (uri, { token, username, password, email }) {
|
|
566
613
|
const nerfed = nerfDart(uri)
|
|
567
614
|
const def = nerfDart(this.get('registry'))
|
|
568
615
|
|
|
@@ -570,41 +617,45 @@ class Config {
|
|
|
570
617
|
// remove old style auth info not limited to a single registry
|
|
571
618
|
this.delete('_password', 'user')
|
|
572
619
|
this.delete('username', 'user')
|
|
573
|
-
this.delete('email', 'user')
|
|
574
620
|
this.delete('_auth', 'user')
|
|
575
621
|
this.delete('_authtoken', 'user')
|
|
622
|
+
this.delete('-authtoken', 'user')
|
|
576
623
|
this.delete('_authToken', 'user')
|
|
577
624
|
}
|
|
578
625
|
|
|
579
|
-
|
|
626
|
+
// email used to be nerfed always. if we're using the default
|
|
627
|
+
// registry, de-nerf it.
|
|
628
|
+
if (nerfed === def) {
|
|
629
|
+
email = email ||
|
|
630
|
+
this.get('email', 'user') ||
|
|
631
|
+
this.get(`${nerfed}:email`, 'user')
|
|
632
|
+
if (email)
|
|
633
|
+
this.set('email', email, 'user')
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// field that hasn't been used as documented for a LONG time,
|
|
637
|
+
// and as of npm 7.10.0, isn't used at all. We just always
|
|
638
|
+
// send auth if we have it, only to the URIs under the nerf dart.
|
|
639
|
+
this.delete(`${nerfed}:always-auth`, 'user')
|
|
640
|
+
|
|
641
|
+
this.delete(`${nerfed}:-authtoken`, 'user')
|
|
642
|
+
this.delete(`${nerfed}:_authtoken`, 'user')
|
|
643
|
+
this.delete(`${nerfed}:email`, 'user')
|
|
580
644
|
if (token) {
|
|
581
645
|
this.set(`${nerfed}:_authToken`, token, 'user')
|
|
582
646
|
this.delete(`${nerfed}:_password`, 'user')
|
|
583
647
|
this.delete(`${nerfed}:username`, 'user')
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
if (
|
|
588
|
-
|
|
589
|
-
throw new Error('must include username')
|
|
590
|
-
if (!password)
|
|
591
|
-
throw new Error('must include password')
|
|
592
|
-
}
|
|
593
|
-
if (!email)
|
|
594
|
-
throw new Error('must include email')
|
|
648
|
+
} else if (username || password) {
|
|
649
|
+
if (!username)
|
|
650
|
+
throw new Error('must include username')
|
|
651
|
+
if (!password)
|
|
652
|
+
throw new Error('must include password')
|
|
595
653
|
this.delete(`${nerfed}:_authToken`, 'user')
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
this.set(`${nerfed}:_password`, encoded, 'user')
|
|
602
|
-
}
|
|
603
|
-
this.set(`${nerfed}:email`, email, 'user')
|
|
604
|
-
if (alwaysAuth !== undefined)
|
|
605
|
-
this.set(`${nerfed}:always-auth`, alwaysAuth, 'user')
|
|
606
|
-
else
|
|
607
|
-
this.delete(`${nerfed}:always-auth`, 'user')
|
|
654
|
+
this.set(`${nerfed}:username`, username, 'user')
|
|
655
|
+
// note: not encrypted, no idea why we bothered to do this, but oh well
|
|
656
|
+
// protects against shoulder-hacks if password is memorable, I guess?
|
|
657
|
+
const encoded = Buffer.from(password, 'utf8').toString('base64')
|
|
658
|
+
this.set(`${nerfed}:_password`, encoded, 'user')
|
|
608
659
|
} else {
|
|
609
660
|
throw new Error('No credentials to set.')
|
|
610
661
|
}
|
|
@@ -615,18 +666,12 @@ class Config {
|
|
|
615
666
|
const nerfed = nerfDart(uri)
|
|
616
667
|
const creds = {}
|
|
617
668
|
|
|
618
|
-
// you can set always-auth for a single registry, or as a default
|
|
619
|
-
const alwaysAuthReg = this.get(`${nerfed}:always-auth`)
|
|
620
|
-
if (alwaysAuthReg !== undefined)
|
|
621
|
-
creds.alwaysAuth = !!alwaysAuthReg
|
|
622
|
-
else
|
|
623
|
-
creds.alwaysAuth = this.get('always-auth')
|
|
624
|
-
|
|
625
669
|
const email = this.get(`${nerfed}:email`) || this.get('email')
|
|
626
670
|
if (email)
|
|
627
671
|
creds.email = email
|
|
628
672
|
|
|
629
673
|
const tokenReg = this.get(`${nerfed}:_authToken`) ||
|
|
674
|
+
this.get(`${nerfed}:_authtoken`) ||
|
|
630
675
|
this.get(`${nerfed}:-authtoken`) ||
|
|
631
676
|
nerfed === nerfDart(this.get('registry')) && this.get('_authToken')
|
|
632
677
|
|
|
@@ -645,6 +690,16 @@ class Config {
|
|
|
645
690
|
return creds
|
|
646
691
|
}
|
|
647
692
|
|
|
693
|
+
const authReg = this.get(`${nerfed}:_auth`)
|
|
694
|
+
if (authReg) {
|
|
695
|
+
const authDecode = Buffer.from(authReg, 'base64').toString('utf8')
|
|
696
|
+
const authSplit = authDecode.split(':')
|
|
697
|
+
creds.username = authSplit.shift()
|
|
698
|
+
creds.password = authSplit.join(':')
|
|
699
|
+
creds.auth = authReg
|
|
700
|
+
return creds
|
|
701
|
+
}
|
|
702
|
+
|
|
648
703
|
// at this point, we can only use the values if the URI is the
|
|
649
704
|
// default registry.
|
|
650
705
|
const defaultNerf = nerfDart(this.get('registry'))
|
|
@@ -675,48 +730,6 @@ class Config {
|
|
|
675
730
|
return creds
|
|
676
731
|
}
|
|
677
732
|
|
|
678
|
-
async loadCAFile () {
|
|
679
|
-
const where = this[_find]('cafile')
|
|
680
|
-
|
|
681
|
-
/* istanbul ignore if - it'll always be set in the defaults */
|
|
682
|
-
if (!where)
|
|
683
|
-
return
|
|
684
|
-
|
|
685
|
-
const cafile = this[_get]('cafile', where)
|
|
686
|
-
const ca = this[_get]('ca', where)
|
|
687
|
-
|
|
688
|
-
// if you have a ca, or cafile is set to null, then nothing to do here.
|
|
689
|
-
if (ca || !cafile)
|
|
690
|
-
return
|
|
691
|
-
|
|
692
|
-
const raw = await readFile(cafile, 'utf8').catch(er => {
|
|
693
|
-
if (er.code !== 'ENOENT')
|
|
694
|
-
throw er
|
|
695
|
-
})
|
|
696
|
-
if (!raw)
|
|
697
|
-
return
|
|
698
|
-
|
|
699
|
-
const delim = '-----END CERTIFICATE-----'
|
|
700
|
-
const output = raw.replace(/\r\n/g, '\n').split(delim)
|
|
701
|
-
.filter(section => section.trim())
|
|
702
|
-
.map(section => section.trimLeft() + delim)
|
|
703
|
-
|
|
704
|
-
// make it non-enumerable so we don't save it back by accident
|
|
705
|
-
const { data } = this.data.get(where)
|
|
706
|
-
Object.defineProperty(data, 'ca', {
|
|
707
|
-
value: output,
|
|
708
|
-
enumerable: false,
|
|
709
|
-
configurable: true,
|
|
710
|
-
writable: true,
|
|
711
|
-
})
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
// the user-agent configuration is a template that gets populated
|
|
715
|
-
// with some variables, that takes place here
|
|
716
|
-
setUserAgent () {
|
|
717
|
-
this.set('user-agent', getUserAgent(this))
|
|
718
|
-
}
|
|
719
|
-
|
|
720
733
|
// set up the environment object we have with npm_config_* environs
|
|
721
734
|
// for all configs that are different from their default values, and
|
|
722
735
|
// set EDITOR and HOME.
|
package/lib/set-envs.js
CHANGED
|
@@ -50,17 +50,13 @@ const setEnvs = (config) => {
|
|
|
50
50
|
platform,
|
|
51
51
|
env,
|
|
52
52
|
defaults,
|
|
53
|
+
definitions,
|
|
53
54
|
list: [cliConf, envConf],
|
|
54
55
|
} = config
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
if (platform !== 'win32' && DESTDIR && globalPrefix.indexOf(DESTDIR) === 0)
|
|
58
|
-
env.PREFIX = globalPrefix.substr(DESTDIR.length)
|
|
59
|
-
else
|
|
60
|
-
env.PREFIX = globalPrefix
|
|
61
|
-
|
|
62
|
-
env.INIT_CWD = env.INIT_CWD || process.cwd()
|
|
57
|
+
env.INIT_CWD = process.cwd()
|
|
63
58
|
|
|
59
|
+
// if the key is deprecated, skip it always.
|
|
64
60
|
// if the key is the default value,
|
|
65
61
|
// if the environ is NOT the default value,
|
|
66
62
|
// set the environ
|
|
@@ -71,6 +67,10 @@ const setEnvs = (config) => {
|
|
|
71
67
|
const cliSet = new Set(Object.keys(cliConf))
|
|
72
68
|
const envSet = new Set(Object.keys(envConf))
|
|
73
69
|
for (const key in cliConf) {
|
|
70
|
+
const { deprecated, envExport = true } = definitions[key] || {}
|
|
71
|
+
if (deprecated || envExport === false)
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
74
|
if (sameConfigValue(defaults[key], cliConf[key])) {
|
|
75
75
|
// config is the default, if the env thought different, then we
|
|
76
76
|
// have to set it BACK to the default in the environment.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"lib"
|
|
6
6
|
],
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"coverage-map": "map.js"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"tap": "^
|
|
27
|
+
"tap": "^15.0.4"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"ini": "^2.0.0",
|
package/lib/get-user-agent.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Accepts a config object, returns a user-agent string
|
|
2
|
-
const getUserAgent = (config) => {
|
|
3
|
-
const ciName = config.get('ci-name')
|
|
4
|
-
return (config.get('user-agent') || '')
|
|
5
|
-
.replace(/\{node-version\}/gi, config.get('node-version'))
|
|
6
|
-
.replace(/\{npm-version\}/gi, config.get('npm-version'))
|
|
7
|
-
.replace(/\{platform\}/gi, process.platform)
|
|
8
|
-
.replace(/\{arch\}/gi, process.arch)
|
|
9
|
-
.replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '')
|
|
10
|
-
.trim()
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
module.exports = getUserAgent
|