@npmcli/config 2.3.1 → 3.0.1

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 CHANGED
@@ -80,7 +80,7 @@ const conf = new Config({
80
80
  cwd: process.cwd(),
81
81
  // optional, defaults to emitting 'log' events on process object
82
82
  // only silly, verbose, warn, and error are logged by this module
83
- log: require('npmlog')
83
+ log: require('proc-log')
84
84
  })
85
85
 
86
86
  // returns a promise that fails if config loading fails, and
@@ -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
@@ -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] : `\$\{${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()
@@ -86,12 +88,11 @@ class Config {
86
88
  // options just to override in tests, mostly
87
89
  env = process.env,
88
90
  argv = process.argv,
89
- log = require('./proc-log.js'),
91
+ log = require('proc-log'),
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
@@ -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, source } = this.data.get(where || 'cli')
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
- if (!confTypes.has(where))
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
- if (!confTypes.has(where))
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 { this.setCredentialsByURI(reg, creds) } catch (_) {}
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, obj] of this.data.entries()) {
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
  }
@@ -401,6 +421,20 @@ class Config {
401
421
  }
402
422
  }
403
423
 
424
+ // Returns true if the value is coming directly from the source defined
425
+ // in default definitions, if the current value for the key config is
426
+ // coming from any other different source, returns false
427
+ isDefault (key) {
428
+ const [defaultType, ...types] = [...confTypes]
429
+ const defaultData = this.data.get(defaultType).data
430
+
431
+ return hasOwnProperty(defaultData, key)
432
+ && types.every(type => {
433
+ const typeData = this.data.get(type).data
434
+ return !hasOwnProperty(typeData, key)
435
+ })
436
+ }
437
+
404
438
  invalidHandler (k, val, type, source, where) {
405
439
  this.log.warn(
406
440
  'invalid config',
@@ -410,14 +444,15 @@ class Config {
410
444
  this.data.get(where)[_valid] = false
411
445
 
412
446
  if (Array.isArray(type)) {
413
- if (type.includes(typeDefs.url.type))
447
+ if (type.includes(typeDefs.url.type)) {
414
448
  type = typeDefs.url.type
415
- else {
449
+ } else {
416
450
  /* istanbul ignore if - no actual configs matching this, but
417
451
  * path types SHOULD be handled this way, like URLs, for the
418
452
  * same reason */
419
- if (type.includes(typeDefs.path.type))
453
+ if (type.includes(typeDefs.path.type)) {
420
454
  type = typeDefs.path.type
455
+ }
421
456
  }
422
457
  }
423
458
 
@@ -455,15 +490,17 @@ class Config {
455
490
  this.sources.set(source, where)
456
491
  if (er) {
457
492
  conf.loadError = er
458
- if (er.code !== 'ENOENT')
493
+ if (er.code !== 'ENOENT') {
459
494
  this.log.verbose('config', `error loading ${where} config`, er)
495
+ }
460
496
  } else {
461
497
  conf.raw = obj
462
498
  for (const [key, value] of Object.entries(obj)) {
463
499
  const k = envReplace(key, this.env)
464
500
  const v = this.parseField(value, k)
465
- if (where !== 'default')
501
+ if (where !== 'default') {
466
502
  this[_checkDeprecated](k, where, obj, [key, value])
503
+ }
467
504
  conf.data[k] = v
468
505
  }
469
506
  }
@@ -497,24 +534,26 @@ class Config {
497
534
  }
498
535
 
499
536
  async loadProjectConfig () {
537
+ // the localPrefix can be set by the CLI config, but otherwise is
538
+ // found by walking up the folder tree. either way, we load it before
539
+ // we return to make sure localPrefix is set
540
+ await this.loadLocalPrefix()
541
+
500
542
  if (this[_get]('global') === true || this[_get]('location') === 'global') {
501
543
  this.data.get('project').source = '(global mode enabled, ignored)'
502
544
  this.sources.set(this.data.get('project').source, 'project')
503
545
  return
504
546
  }
505
547
 
506
- // the localPrefix can be set by the CLI config, but otherwise is
507
- // found by walking up the folder tree
508
- await this.loadLocalPrefix()
509
548
  const projectFile = resolve(this.localPrefix, '.npmrc')
510
549
  // if we're in the ~ directory, and there happens to be a node_modules
511
550
  // folder (which is not TOO uncommon, it turns out), then we can end
512
551
  // up loading the "project" config where the "userconfig" will be,
513
552
  // which causes some calamaties. So, we only load project config if
514
553
  // it doesn't match what the userconfig will be.
515
- if (projectFile !== this[_get]('userconfig'))
554
+ if (projectFile !== this[_get]('userconfig')) {
516
555
  return this[_loadFile](projectFile, 'project')
517
- else {
556
+ } else {
518
557
  this.data.get('project').source = '(same as "user" config, ignored)'
519
558
  this.sources.set(this.data.get('project').source, 'project')
520
559
  }
@@ -527,23 +566,65 @@ class Config {
527
566
  return
528
567
  }
529
568
 
569
+ const cliWorkspaces = this[_get]('workspaces', 'cli')
570
+
530
571
  for (const p of walkUp(this.cwd)) {
531
- // walk up until we have a nm dir or a pj file
532
- const hasAny = (await Promise.all([
533
- stat(resolve(p, 'node_modules'))
534
- .then(st => st.isDirectory())
535
- .catch(() => false),
536
- stat(resolve(p, 'package.json'))
537
- .then(st => st.isFile())
538
- .catch(() => false),
539
- ])).some(is => is)
540
- 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)) {
541
581
  this.localPrefix = p
542
- return
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
+ }
543
622
  }
544
623
  }
545
624
 
546
- this.localPrefix = this.cwd
625
+ if (!this.localPrefix) {
626
+ this.localPrefix = this.cwd
627
+ }
547
628
  }
548
629
 
549
630
  loadUserConfig () {
@@ -555,10 +636,12 @@ class Config {
555
636
  }
556
637
 
557
638
  async save (where) {
558
- if (!this.loaded)
639
+ if (!this.loaded) {
559
640
  throw new Error('call config.load() before saving')
560
- if (!confFileTypes.has(where))
641
+ }
642
+ if (!confFileTypes.has(where)) {
561
643
  throw new Error('invalid config location param: ' + where)
644
+ }
562
645
  const conf = this.data.get(where)
563
646
  conf[_raw] = { ...conf.data }
564
647
  conf[_loadError] = null
@@ -570,7 +653,9 @@ class Config {
570
653
  // we ignore this error because the failed set already removed
571
654
  // anything that might be a security hazard, and it won't be
572
655
  // saved back to the .npmrc file, so we're good.
573
- try { this.setCredentialsByURI(reg, creds) } catch (_) {}
656
+ try {
657
+ this.setCredentialsByURI(reg, creds)
658
+ } catch (_) {}
574
659
  }
575
660
 
576
661
  const iniData = ini.stringify(conf.data).trim() + '\n'
@@ -586,8 +671,9 @@ class Config {
586
671
  /* istanbul ignore if - this is best-effort and a pita to test */
587
672
  if (myUid === 0) {
588
673
  const st = await stat(dir).catch(() => null)
589
- if (st && (st.uid !== myUid || st.gid !== myGid))
674
+ if (st && (st.uid !== myUid || st.gid !== myGid)) {
590
675
  await chown(conf.source, st.uid, st.gid).catch(() => {})
676
+ }
591
677
  }
592
678
  const mode = where === 'user' ? 0o600 : 0o666
593
679
  await chmod(conf.source, mode)
@@ -635,8 +721,9 @@ class Config {
635
721
  email = email ||
636
722
  this.get('email', 'user') ||
637
723
  this.get(`${nerfed}:email`, 'user')
638
- if (email)
724
+ if (email) {
639
725
  this.set('email', email, 'user')
726
+ }
640
727
  }
641
728
 
642
729
  // field that hasn't been used as documented for a LONG time,
@@ -652,10 +739,12 @@ class Config {
652
739
  this.delete(`${nerfed}:_password`, 'user')
653
740
  this.delete(`${nerfed}:username`, 'user')
654
741
  } else if (username || password) {
655
- if (!username)
742
+ if (!username) {
656
743
  throw new Error('must include username')
657
- if (!password)
744
+ }
745
+ if (!password) {
658
746
  throw new Error('must include password')
747
+ }
659
748
  this.delete(`${nerfed}:_authToken`, 'user')
660
749
  this.set(`${nerfed}:username`, username, 'user')
661
750
  // note: not encrypted, no idea why we bothered to do this, but oh well
@@ -673,8 +762,9 @@ class Config {
673
762
  const creds = {}
674
763
 
675
764
  const email = this.get(`${nerfed}:email`) || this.get('email')
676
- if (email)
765
+ if (email) {
677
766
  creds.email = email
767
+ }
678
768
 
679
769
  const tokenReg = this.get(`${nerfed}:_authToken`) ||
680
770
  this.get(`${nerfed}:_authtoken`) ||
@@ -709,8 +799,9 @@ class Config {
709
799
  // at this point, we can only use the values if the URI is the
710
800
  // default registry.
711
801
  const defaultNerf = nerfDart(this.get('registry'))
712
- if (nerfed !== defaultNerf)
802
+ if (nerfed !== defaultNerf) {
713
803
  return creds
804
+ }
714
805
 
715
806
  const userDef = this.get('username')
716
807
  const passDef = this.get('_password')
@@ -725,8 +816,9 @@ class Config {
725
816
  // Handle the old-style _auth=<base64> style for the default
726
817
  // registry, if set.
727
818
  const auth = this.get('_auth')
728
- if (!auth)
819
+ if (!auth) {
729
820
  return creds
821
+ }
730
822
 
731
823
  const authDecode = Buffer.from(auth, 'base64').toString('utf8')
732
824
  const authSplit = authDecode.split(':')
@@ -739,7 +831,9 @@ class Config {
739
831
  // set up the environment object we have with npm_config_* environs
740
832
  // for all configs that are different from their default values, and
741
833
  // set EDITOR and HOME.
742
- setEnvs () { setEnvs(this) }
834
+ setEnvs () {
835
+ setEnvs(this)
836
+ }
743
837
  }
744
838
 
745
839
  const _data = Symbol('data')
@@ -765,25 +859,37 @@ class ConfigData {
765
859
  }
766
860
 
767
861
  set source (s) {
768
- if (this[_source])
862
+ if (this[_source]) {
769
863
  throw new Error('cannot set ConfigData source more than once')
864
+ }
770
865
  this[_source] = s
771
866
  }
772
- get source () { return this[_source] }
867
+
868
+ get source () {
869
+ return this[_source]
870
+ }
773
871
 
774
872
  set loadError (e) {
775
- if (this[_loadError] || this[_raw])
873
+ if (this[_loadError] || this[_raw]) {
776
874
  throw new Error('cannot set ConfigData loadError after load')
875
+ }
777
876
  this[_loadError] = e
778
877
  }
779
- get loadError () { return this[_loadError] }
878
+
879
+ get loadError () {
880
+ return this[_loadError]
881
+ }
780
882
 
781
883
  set raw (r) {
782
- if (this[_raw] || this[_loadError])
884
+ if (this[_raw] || this[_loadError]) {
783
885
  throw new Error('cannot set ConfigData raw after load')
886
+ }
784
887
  this[_raw] = r
785
888
  }
786
- get raw () { return this[_raw] }
889
+
890
+ get raw () {
891
+ return this[_raw]
892
+ }
787
893
  }
788
894
 
789
895
  module.exports = Config
@@ -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, log, home, env } = opts
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
 
@@ -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": "2.3.1",
3
+ "version": "3.0.1",
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": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)",
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
- "tap": "^15.0.4"
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
- "semver": "^7.3.4",
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": ">=10"
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
@@ -1,4 +0,0 @@
1
- const log = (level) => (...args) => process.emit('log', level, ...args)
2
- for (const level of ['silly', 'verbose', 'warn', 'error']) {
3
- exports[level] = log(level)
4
- }