@npmcli/config 1.2.7 → 2.1.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 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
- types,
81
+ definitions,
80
82
  shorthands,
81
- defaults,
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
- this.npmPath = npmPath
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,10 +197,36 @@ 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
- this.data.get(where).data[key] = val
200
+ if (key === '_auth') {
201
+ const { email } = this.getCredentialsByURI(this.get('registry'))
202
+ if (!email)
203
+ throw new Error('Cannot set _auth without first setting email')
204
+ }
205
+ this[_checkDeprecated](key)
206
+ const { data } = this.data.get(where)
207
+ data[key] = val
182
208
 
183
209
  // this is now dirty, the next call to this.valid will have to check it
184
210
  this.data.get(where)[_valid] = null
211
+
212
+ // the flat options are invalidated, regenerate next time they're needed
213
+ this[_flatOptions] = null
214
+ }
215
+
216
+ get flat () {
217
+ if (this[_flatOptions])
218
+ return this[_flatOptions]
219
+
220
+ // create the object for flat options passed to deps
221
+ process.emit('time', 'config:load:flatten')
222
+ this[_flatOptions] = {}
223
+ // walk from least priority to highest
224
+ for (const { data } of this.data.values()) {
225
+ this[_flatten](data, this[_flatOptions])
226
+ }
227
+ process.emit('timeEnd', 'config:load:flatten')
228
+
229
+ return this[_flatOptions]
185
230
  }
186
231
 
187
232
  delete (key, where = 'cli') {
@@ -228,11 +273,6 @@ class Config {
228
273
  await this.loadGlobalConfig()
229
274
  process.emit('timeEnd', 'config:load:global')
230
275
 
231
- // now the extras
232
- process.emit('time', 'config:load:cafile')
233
- await this.loadCAFile()
234
- process.emit('timeEnd', 'config:load:cafile')
235
-
236
276
  // warn if anything is not valid
237
277
  process.emit('time', 'config:load:validate')
238
278
  this.validate()
@@ -245,10 +285,6 @@ class Config {
245
285
  // set proper globalPrefix now that everything is loaded
246
286
  this.globalPrefix = this.get('prefix')
247
287
 
248
- process.emit('time', 'config:load:setUserAgent')
249
- this.setUserAgent()
250
- process.emit('timeEnd', 'config:load:setUserAgent')
251
-
252
288
  process.emit('time', 'config:load:setEnvs')
253
289
  this.setEnvs()
254
290
  process.emit('timeEnd', 'config:load:setEnvs')
@@ -371,13 +407,13 @@ class Config {
371
407
  this.data.get(where)[_valid] = false
372
408
 
373
409
  if (Array.isArray(type)) {
374
- if (type.indexOf(typeDefs.url.type) !== -1)
410
+ if (type.includes(typeDefs.url.type))
375
411
  type = typeDefs.url.type
376
412
  else {
377
413
  /* istanbul ignore if - no actual configs matching this, but
378
414
  * path types SHOULD be handled this way, like URLs, for the
379
415
  * same reason */
380
- if (type.indexOf(typeDefs.path.type) !== -1)
416
+ if (type.includes(typeDefs.path.type))
381
417
  type = typeDefs.path.type
382
418
  }
383
419
  }
@@ -423,11 +459,21 @@ class Config {
423
459
  for (const [key, value] of Object.entries(obj)) {
424
460
  const k = envReplace(key, this.env)
425
461
  const v = this.parseField(value, k)
462
+ if (where !== 'default')
463
+ this[_checkDeprecated](k, where, obj, [key, value])
426
464
  conf.data[k] = v
427
465
  }
428
466
  }
429
467
  }
430
468
 
469
+ [_checkDeprecated] (key, where, obj, kv) {
470
+ // XXX a future npm version will make this a warning.
471
+ // An even more future npm version will make this an error.
472
+ if (this.deprecated[key]) {
473
+ this.log.verbose('config', key, this.deprecated[key])
474
+ }
475
+ }
476
+
431
477
  // Parse a field, coercing it to the best type available.
432
478
  parseField (f, key, listElement = false) {
433
479
  return parseField(f, key, this, listElement)
@@ -512,6 +558,9 @@ class Config {
512
558
  if (where === 'user') {
513
559
  const reg = this.get('registry')
514
560
  const creds = this.getCredentialsByURI(reg)
561
+ // we ignore this error because the failed set already removed
562
+ // anything that might be a security hazard, and it won't be
563
+ // saved back to the .npmrc file, so we're good.
515
564
  try { this.setCredentialsByURI(reg, creds) } catch (_) {}
516
565
  }
517
566
 
@@ -576,18 +625,22 @@ class Config {
576
625
  this.delete(`${nerfed}:email`, 'user')
577
626
  this.delete(`${nerfed}:always-auth`, 'user')
578
627
  } else if (username || password || email) {
579
- if (!username)
580
- throw new Error('must include username')
581
- if (!password)
582
- throw new Error('must include password')
628
+ if (username || password) {
629
+ if (!username)
630
+ throw new Error('must include username')
631
+ if (!password)
632
+ throw new Error('must include password')
633
+ }
583
634
  if (!email)
584
635
  throw new Error('must include email')
585
636
  this.delete(`${nerfed}:_authToken`, 'user')
586
- this.set(`${nerfed}:username`, username, 'user')
587
- // note: not encrypted, no idea why we bothered to do this, but oh well
588
- // protects against shoulder-hacks if password is memorable, I guess?
589
- const encoded = Buffer.from(password, 'utf8').toString('base64')
590
- this.set(`${nerfed}:_password`, encoded, 'user')
637
+ if (username || password) {
638
+ this.set(`${nerfed}:username`, username, 'user')
639
+ // note: not encrypted, no idea why we bothered to do this, but oh well
640
+ // protects against shoulder-hacks if password is memorable, I guess?
641
+ const encoded = Buffer.from(password, 'utf8').toString('base64')
642
+ this.set(`${nerfed}:_password`, encoded, 'user')
643
+ }
591
644
  this.set(`${nerfed}:email`, email, 'user')
592
645
  if (alwaysAuth !== undefined)
593
646
  this.set(`${nerfed}:always-auth`, alwaysAuth, 'user')
@@ -663,48 +716,6 @@ class Config {
663
716
  return creds
664
717
  }
665
718
 
666
- async loadCAFile () {
667
- const where = this[_find]('cafile')
668
-
669
- /* istanbul ignore if - it'll always be set in the defaults */
670
- if (!where)
671
- return
672
-
673
- const cafile = this[_get]('cafile', where)
674
- const ca = this[_get]('ca', where)
675
-
676
- // if you have a ca, or cafile is set to null, then nothing to do here.
677
- if (ca || !cafile)
678
- return
679
-
680
- const raw = await readFile(cafile, 'utf8').catch(er => {
681
- if (er.code !== 'ENOENT')
682
- throw er
683
- })
684
- if (!raw)
685
- return
686
-
687
- const delim = '-----END CERTIFICATE-----'
688
- const output = raw.replace(/\r\n/g, '\n').split(delim)
689
- .filter(section => section.trim())
690
- .map(section => section.trimLeft() + delim)
691
-
692
- // make it non-enumerable so we don't save it back by accident
693
- const { data } = this.data.get(where)
694
- Object.defineProperty(data, 'ca', {
695
- value: output,
696
- enumerable: false,
697
- configurable: true,
698
- writable: true,
699
- })
700
- }
701
-
702
- // the user-agent configuration is a template that gets populated
703
- // with some variables, that takes place here
704
- setUserAgent () {
705
- this.set('user-agent', getUserAgent(this))
706
- }
707
-
708
719
  // set up the environment object we have with npm_config_* environs
709
720
  // for all configs that are different from their default values, and
710
721
  // 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
- const { DESTDIR } = env
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": "1.2.7",
3
+ "version": "2.1.0",
4
4
  "files": [
5
5
  "lib"
6
6
  ],
@@ -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