@npmcli/config 6.1.5 → 6.1.7
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 +84 -78
- package/package.json +5 -5
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// TODO: set the scope config from package.json or explicit cli config
|
|
2
|
-
const walkUp = require('walk-up-path')
|
|
2
|
+
const { walkUp } = require('walk-up-path')
|
|
3
3
|
const ini = require('ini')
|
|
4
4
|
const nopt = require('nopt')
|
|
5
5
|
const mapWorkspaces = require('@npmcli/map-workspaces')
|
|
@@ -72,16 +72,12 @@ const confTypes = new Set([
|
|
|
72
72
|
'cli',
|
|
73
73
|
])
|
|
74
74
|
|
|
75
|
-
const _loaded = Symbol('loaded')
|
|
76
|
-
const _get = Symbol('get')
|
|
77
|
-
const _find = Symbol('find')
|
|
78
|
-
const _loadObject = Symbol('loadObject')
|
|
79
|
-
const _loadFile = Symbol('loadFile')
|
|
80
|
-
const _checkDeprecated = Symbol('checkDeprecated')
|
|
81
|
-
const _flatten = Symbol('flatten')
|
|
82
|
-
const _flatOptions = Symbol('flatOptions')
|
|
83
|
-
|
|
84
75
|
class Config {
|
|
76
|
+
#loaded = false
|
|
77
|
+
#flatten
|
|
78
|
+
// populated the first time we flatten the object
|
|
79
|
+
#flatOptions = null
|
|
80
|
+
|
|
85
81
|
static get typeDefs () {
|
|
86
82
|
return typeDefs
|
|
87
83
|
}
|
|
@@ -113,9 +109,7 @@ class Config {
|
|
|
113
109
|
}
|
|
114
110
|
}
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
this[_flatOptions] = null
|
|
118
|
-
this[_flatten] = flatten
|
|
112
|
+
this.#flatten = flatten
|
|
119
113
|
this.types = types
|
|
120
114
|
this.shorthands = shorthands
|
|
121
115
|
this.defaults = defaults
|
|
@@ -159,15 +153,15 @@ class Config {
|
|
|
159
153
|
}
|
|
160
154
|
Object.freeze(this.list)
|
|
161
155
|
|
|
162
|
-
this
|
|
156
|
+
this.#loaded = false
|
|
163
157
|
}
|
|
164
158
|
|
|
165
159
|
get loaded () {
|
|
166
|
-
return this
|
|
160
|
+
return this.#loaded
|
|
167
161
|
}
|
|
168
162
|
|
|
169
163
|
get prefix () {
|
|
170
|
-
return this
|
|
164
|
+
return this.#get('global') ? this.globalPrefix : this.localPrefix
|
|
171
165
|
}
|
|
172
166
|
|
|
173
167
|
// return the location where key is found.
|
|
@@ -175,10 +169,7 @@ class Config {
|
|
|
175
169
|
if (!this.loaded) {
|
|
176
170
|
throw new Error('call config.load() before reading values')
|
|
177
171
|
}
|
|
178
|
-
return this[_find](key)
|
|
179
|
-
}
|
|
180
172
|
|
|
181
|
-
[_find] (key) {
|
|
182
173
|
// have to look in reverse order
|
|
183
174
|
const entries = [...this.data.entries()]
|
|
184
175
|
for (let i = entries.length - 1; i > -1; i--) {
|
|
@@ -194,12 +185,12 @@ class Config {
|
|
|
194
185
|
if (!this.loaded) {
|
|
195
186
|
throw new Error('call config.load() before reading values')
|
|
196
187
|
}
|
|
197
|
-
return this
|
|
188
|
+
return this.#get(key, where)
|
|
198
189
|
}
|
|
199
190
|
|
|
200
191
|
// we need to get values sometimes, so use this internal one to do so
|
|
201
192
|
// while in the process of loading.
|
|
202
|
-
|
|
193
|
+
#get (key, where = null) {
|
|
203
194
|
if (where !== null && !confTypes.has(where)) {
|
|
204
195
|
throw new Error('invalid config location param: ' + where)
|
|
205
196
|
}
|
|
@@ -214,32 +205,35 @@ class Config {
|
|
|
214
205
|
if (!confTypes.has(where)) {
|
|
215
206
|
throw new Error('invalid config location param: ' + where)
|
|
216
207
|
}
|
|
217
|
-
this
|
|
218
|
-
const { data } = this.data.get(where)
|
|
208
|
+
this.#checkDeprecated(key)
|
|
209
|
+
const { data, raw } = this.data.get(where)
|
|
219
210
|
data[key] = val
|
|
211
|
+
if (['global', 'user', 'project'].includes(where)) {
|
|
212
|
+
raw[key] = val
|
|
213
|
+
}
|
|
220
214
|
|
|
221
215
|
// this is now dirty, the next call to this.valid will have to check it
|
|
222
216
|
this.data.get(where)[_valid] = null
|
|
223
217
|
|
|
224
218
|
// the flat options are invalidated, regenerate next time they're needed
|
|
225
|
-
this
|
|
219
|
+
this.#flatOptions = null
|
|
226
220
|
}
|
|
227
221
|
|
|
228
222
|
get flat () {
|
|
229
|
-
if (this
|
|
230
|
-
return this
|
|
223
|
+
if (this.#flatOptions) {
|
|
224
|
+
return this.#flatOptions
|
|
231
225
|
}
|
|
232
226
|
|
|
233
227
|
// create the object for flat options passed to deps
|
|
234
228
|
process.emit('time', 'config:load:flatten')
|
|
235
|
-
this
|
|
229
|
+
this.#flatOptions = {}
|
|
236
230
|
// walk from least priority to highest
|
|
237
231
|
for (const { data } of this.data.values()) {
|
|
238
|
-
this
|
|
232
|
+
this.#flatten(data, this.#flatOptions)
|
|
239
233
|
}
|
|
240
234
|
process.emit('timeEnd', 'config:load:flatten')
|
|
241
235
|
|
|
242
|
-
return this
|
|
236
|
+
return this.#flatOptions
|
|
243
237
|
}
|
|
244
238
|
|
|
245
239
|
delete (key, where = 'cli') {
|
|
@@ -249,7 +243,11 @@ class Config {
|
|
|
249
243
|
if (!confTypes.has(where)) {
|
|
250
244
|
throw new Error('invalid config location param: ' + where)
|
|
251
245
|
}
|
|
252
|
-
|
|
246
|
+
const { data, raw } = this.data.get(where)
|
|
247
|
+
delete data[key]
|
|
248
|
+
if (['global', 'user', 'project'].includes(where)) {
|
|
249
|
+
delete raw[key]
|
|
250
|
+
}
|
|
253
251
|
}
|
|
254
252
|
|
|
255
253
|
async load () {
|
|
@@ -290,8 +288,8 @@ class Config {
|
|
|
290
288
|
process.emit('timeEnd', 'config:load:global')
|
|
291
289
|
|
|
292
290
|
// set this before calling setEnvs, so that we don't have to share
|
|
293
|
-
//
|
|
294
|
-
this
|
|
291
|
+
// private attributes, as that module also does a bunch of get operations
|
|
292
|
+
this.#loaded = true
|
|
295
293
|
|
|
296
294
|
// set proper globalPrefix now that everything is loaded
|
|
297
295
|
this.globalPrefix = this.get('prefix')
|
|
@@ -307,7 +305,7 @@ class Config {
|
|
|
307
305
|
this.loadGlobalPrefix()
|
|
308
306
|
this.loadHome()
|
|
309
307
|
|
|
310
|
-
this
|
|
308
|
+
this.#loadObject({
|
|
311
309
|
...this.defaults,
|
|
312
310
|
prefix: this.globalPrefix,
|
|
313
311
|
}, 'default', 'default values')
|
|
@@ -316,13 +314,13 @@ class Config {
|
|
|
316
314
|
|
|
317
315
|
// the metrics-registry defaults to the current resolved value of
|
|
318
316
|
// the registry, unless overridden somewhere else.
|
|
319
|
-
settableGetter(data, 'metrics-registry', () => this
|
|
317
|
+
settableGetter(data, 'metrics-registry', () => this.#get('registry'))
|
|
320
318
|
|
|
321
319
|
// if the prefix is set on cli, env, or userconfig, then we need to
|
|
322
320
|
// default the globalconfig file to that location, instead of the default
|
|
323
321
|
// global prefix. It's weird that `npm get globalconfig --prefix=/foo`
|
|
324
322
|
// returns `/foo/etc/npmrc`, but better to not change it at this point.
|
|
325
|
-
settableGetter(data, 'globalconfig', () => resolve(this
|
|
323
|
+
settableGetter(data, 'globalconfig', () => resolve(this.#get('prefix'), 'etc/npmrc'))
|
|
326
324
|
}
|
|
327
325
|
|
|
328
326
|
loadHome () {
|
|
@@ -363,7 +361,7 @@ class Config {
|
|
|
363
361
|
}
|
|
364
362
|
conf[key] = envVal
|
|
365
363
|
}
|
|
366
|
-
this
|
|
364
|
+
this.#loadObject(conf, 'env', 'environment')
|
|
367
365
|
}
|
|
368
366
|
|
|
369
367
|
loadCLI () {
|
|
@@ -373,7 +371,7 @@ class Config {
|
|
|
373
371
|
nopt.invalidHandler = null
|
|
374
372
|
this.parsedArgv = conf.argv
|
|
375
373
|
delete conf.argv
|
|
376
|
-
this
|
|
374
|
+
this.#loadObject(conf, 'cli', 'command line options')
|
|
377
375
|
}
|
|
378
376
|
|
|
379
377
|
get valid () {
|
|
@@ -526,22 +524,30 @@ class Config {
|
|
|
526
524
|
}
|
|
527
525
|
|
|
528
526
|
const typeDesc = typeDescription(type)
|
|
529
|
-
const oneOrMore = typeDesc.indexOf(Array) !== -1
|
|
530
527
|
const mustBe = typeDesc
|
|
531
528
|
.filter(m => m !== undefined && m !== Array)
|
|
532
|
-
const
|
|
533
|
-
: mustBe.length > 1 && oneOrMore ? ' one or more of:'
|
|
534
|
-
: mustBe.length > 1 ? ' one of:'
|
|
535
|
-
: ''
|
|
536
|
-
const msg = 'Must be' + oneOf
|
|
529
|
+
const msg = 'Must be' + this.#getOneOfKeywords(mustBe, typeDesc)
|
|
537
530
|
const desc = mustBe.length === 1 ? mustBe[0]
|
|
538
|
-
: mustBe.
|
|
539
|
-
.map(n => typeof n === 'string' ? n : JSON.stringify(n))
|
|
540
|
-
.join(', ')
|
|
531
|
+
: [...new Set(mustBe.map(n => typeof n === 'string' ? n : JSON.stringify(n)))].join(', ')
|
|
541
532
|
log.warn('invalid config', msg, desc)
|
|
542
533
|
}
|
|
543
534
|
|
|
544
|
-
|
|
535
|
+
#getOneOfKeywords (mustBe, typeDesc) {
|
|
536
|
+
let keyword
|
|
537
|
+
if (mustBe.length === 1 && typeDesc.includes(Array)) {
|
|
538
|
+
keyword = ' one or more'
|
|
539
|
+
} else if (mustBe.length > 1 && typeDesc.includes(Array)) {
|
|
540
|
+
keyword = ' one or more of:'
|
|
541
|
+
} else if (mustBe.length > 1) {
|
|
542
|
+
keyword = ' one of:'
|
|
543
|
+
} else {
|
|
544
|
+
keyword = ''
|
|
545
|
+
}
|
|
546
|
+
return keyword
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
#loadObject (obj, where, source, er = null) {
|
|
550
|
+
// obj is the raw data read from the file
|
|
545
551
|
const conf = this.data.get(where)
|
|
546
552
|
if (conf.source) {
|
|
547
553
|
const m = `double-loading "${where}" configs from ${source}, ` +
|
|
@@ -568,14 +574,14 @@ class Config {
|
|
|
568
574
|
const k = envReplace(key, this.env)
|
|
569
575
|
const v = this.parseField(value, k)
|
|
570
576
|
if (where !== 'default') {
|
|
571
|
-
this
|
|
577
|
+
this.#checkDeprecated(k, where, obj, [key, value])
|
|
572
578
|
}
|
|
573
579
|
conf.data[k] = v
|
|
574
580
|
}
|
|
575
581
|
}
|
|
576
582
|
}
|
|
577
583
|
|
|
578
|
-
|
|
584
|
+
#checkDeprecated (key, where, obj, kv) {
|
|
579
585
|
// XXX(npm9+) make this throw an error
|
|
580
586
|
if (this.deprecated[key]) {
|
|
581
587
|
log.warn('config', key, this.deprecated[key])
|
|
@@ -587,18 +593,18 @@ class Config {
|
|
|
587
593
|
return parseField(f, key, this, listElement)
|
|
588
594
|
}
|
|
589
595
|
|
|
590
|
-
async
|
|
596
|
+
async #loadFile (file, type) {
|
|
591
597
|
process.emit('time', 'config:load:file:' + file)
|
|
592
598
|
// only catch the error from readFile, not from the loadObject call
|
|
593
599
|
await readFile(file, 'utf8').then(
|
|
594
|
-
data => this
|
|
595
|
-
er => this
|
|
600
|
+
data => this.#loadObject(ini.parse(data), type, file),
|
|
601
|
+
er => this.#loadObject(null, type, file, er)
|
|
596
602
|
)
|
|
597
603
|
process.emit('timeEnd', 'config:load:file:' + file)
|
|
598
604
|
}
|
|
599
605
|
|
|
600
606
|
loadBuiltinConfig () {
|
|
601
|
-
return this
|
|
607
|
+
return this.#loadFile(resolve(this.npmPath, 'npmrc'), 'builtin')
|
|
602
608
|
}
|
|
603
609
|
|
|
604
610
|
async loadProjectConfig () {
|
|
@@ -613,7 +619,7 @@ class Config {
|
|
|
613
619
|
this.localPackage = await fileExists(this.localPrefix, 'package.json')
|
|
614
620
|
}
|
|
615
621
|
|
|
616
|
-
if (this
|
|
622
|
+
if (this.#get('global') === true || this.#get('location') === 'global') {
|
|
617
623
|
this.data.get('project').source = '(global mode enabled, ignored)'
|
|
618
624
|
this.sources.set(this.data.get('project').source, 'project')
|
|
619
625
|
return
|
|
@@ -625,8 +631,8 @@ class Config {
|
|
|
625
631
|
// up loading the "project" config where the "userconfig" will be,
|
|
626
632
|
// which causes some calamaties. So, we only load project config if
|
|
627
633
|
// it doesn't match what the userconfig will be.
|
|
628
|
-
if (projectFile !== this
|
|
629
|
-
return this
|
|
634
|
+
if (projectFile !== this.#get('userconfig')) {
|
|
635
|
+
return this.#loadFile(projectFile, 'project')
|
|
630
636
|
} else {
|
|
631
637
|
this.data.get('project').source = '(same as "user" config, ignored)'
|
|
632
638
|
this.sources.set(this.data.get('project').source, 'project')
|
|
@@ -634,14 +640,14 @@ class Config {
|
|
|
634
640
|
}
|
|
635
641
|
|
|
636
642
|
async loadLocalPrefix () {
|
|
637
|
-
const cliPrefix = this
|
|
643
|
+
const cliPrefix = this.#get('prefix', 'cli')
|
|
638
644
|
if (cliPrefix) {
|
|
639
645
|
this.localPrefix = cliPrefix
|
|
640
646
|
return
|
|
641
647
|
}
|
|
642
648
|
|
|
643
|
-
const cliWorkspaces = this
|
|
644
|
-
const isGlobal = this
|
|
649
|
+
const cliWorkspaces = this.#get('workspaces', 'cli')
|
|
650
|
+
const isGlobal = this.#get('global') || this.#get('location') === 'global'
|
|
645
651
|
|
|
646
652
|
for (const p of walkUp(this.cwd)) {
|
|
647
653
|
// HACK: this is an option set in tests to stop the local prefix from being set
|
|
@@ -701,11 +707,11 @@ class Config {
|
|
|
701
707
|
}
|
|
702
708
|
|
|
703
709
|
loadUserConfig () {
|
|
704
|
-
return this
|
|
710
|
+
return this.#loadFile(this.#get('userconfig'), 'user')
|
|
705
711
|
}
|
|
706
712
|
|
|
707
713
|
loadGlobalConfig () {
|
|
708
|
-
return this
|
|
714
|
+
return this.#loadFile(this.#get('globalconfig'), 'global')
|
|
709
715
|
}
|
|
710
716
|
|
|
711
717
|
async save (where) {
|
|
@@ -717,7 +723,6 @@ class Config {
|
|
|
717
723
|
}
|
|
718
724
|
|
|
719
725
|
const conf = this.data.get(where)
|
|
720
|
-
conf[_raw] = { ...conf.data }
|
|
721
726
|
conf[_loadError] = null
|
|
722
727
|
|
|
723
728
|
if (where === 'user') {
|
|
@@ -730,7 +735,9 @@ class Config {
|
|
|
730
735
|
}
|
|
731
736
|
}
|
|
732
737
|
|
|
733
|
-
|
|
738
|
+
// We need the actual raw data before we called parseField so that we are
|
|
739
|
+
// saving the same content back to the file
|
|
740
|
+
const iniData = ini.stringify(conf.raw).trim() + '\n'
|
|
734
741
|
if (!iniData.trim()) {
|
|
735
742
|
// ignore the unlink error (eg, if file doesn't exist)
|
|
736
743
|
await unlink(conf.source).catch(er => {})
|
|
@@ -870,22 +877,21 @@ class Config {
|
|
|
870
877
|
}
|
|
871
878
|
}
|
|
872
879
|
|
|
873
|
-
const _data = Symbol('data')
|
|
874
|
-
const _raw = Symbol('raw')
|
|
875
880
|
const _loadError = Symbol('loadError')
|
|
876
|
-
const _source = Symbol('source')
|
|
877
881
|
const _valid = Symbol('valid')
|
|
882
|
+
|
|
878
883
|
class ConfigData {
|
|
884
|
+
#data
|
|
885
|
+
#source = null
|
|
886
|
+
#raw = null
|
|
879
887
|
constructor (parent) {
|
|
880
|
-
this
|
|
881
|
-
this
|
|
882
|
-
this[_loadError] = null
|
|
883
|
-
this[_raw] = null
|
|
888
|
+
this.#data = Object.create(parent && parent.data)
|
|
889
|
+
this.#raw = {}
|
|
884
890
|
this[_valid] = true
|
|
885
891
|
}
|
|
886
892
|
|
|
887
893
|
get data () {
|
|
888
|
-
return this
|
|
894
|
+
return this.#data
|
|
889
895
|
}
|
|
890
896
|
|
|
891
897
|
get valid () {
|
|
@@ -893,18 +899,18 @@ class ConfigData {
|
|
|
893
899
|
}
|
|
894
900
|
|
|
895
901
|
set source (s) {
|
|
896
|
-
if (this
|
|
902
|
+
if (this.#source) {
|
|
897
903
|
throw new Error('cannot set ConfigData source more than once')
|
|
898
904
|
}
|
|
899
|
-
this
|
|
905
|
+
this.#source = s
|
|
900
906
|
}
|
|
901
907
|
|
|
902
908
|
get source () {
|
|
903
|
-
return this
|
|
909
|
+
return this.#source
|
|
904
910
|
}
|
|
905
911
|
|
|
906
912
|
set loadError (e) {
|
|
907
|
-
if (this[_loadError] || this
|
|
913
|
+
if (this[_loadError] || (Object.keys(this.#raw).length)) {
|
|
908
914
|
throw new Error('cannot set ConfigData loadError after load')
|
|
909
915
|
}
|
|
910
916
|
this[_loadError] = e
|
|
@@ -915,14 +921,14 @@ class ConfigData {
|
|
|
915
921
|
}
|
|
916
922
|
|
|
917
923
|
set raw (r) {
|
|
918
|
-
if (this
|
|
924
|
+
if (Object.keys(this.#raw).length || this[_loadError]) {
|
|
919
925
|
throw new Error('cannot set ConfigData raw after load')
|
|
920
926
|
}
|
|
921
|
-
this
|
|
927
|
+
this.#raw = r
|
|
922
928
|
}
|
|
923
929
|
|
|
924
930
|
get raw () {
|
|
925
|
-
return this
|
|
931
|
+
return this.#raw
|
|
926
932
|
}
|
|
927
933
|
}
|
|
928
934
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/config",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.7",
|
|
4
4
|
"files": [
|
|
5
5
|
"bin/",
|
|
6
6
|
"lib/"
|
|
@@ -33,23 +33,23 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@npmcli/eslint-config": "^4.0.0",
|
|
36
|
-
"@npmcli/template-oss": "4.
|
|
36
|
+
"@npmcli/template-oss": "4.14.1",
|
|
37
37
|
"tap": "^16.3.4"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@npmcli/map-workspaces": "^3.0.2",
|
|
41
|
-
"ini": "^
|
|
41
|
+
"ini": "^4.1.0",
|
|
42
42
|
"nopt": "^7.0.0",
|
|
43
43
|
"proc-log": "^3.0.0",
|
|
44
44
|
"read-package-json-fast": "^3.0.2",
|
|
45
45
|
"semver": "^7.3.5",
|
|
46
|
-
"walk-up-path": "^
|
|
46
|
+
"walk-up-path": "^3.0.1"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
|
50
50
|
},
|
|
51
51
|
"templateOSS": {
|
|
52
52
|
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
|
53
|
-
"version": "4.
|
|
53
|
+
"version": "4.14.1"
|
|
54
54
|
}
|
|
55
55
|
}
|