@npmcli/arborist 6.0.0-pre.3 → 6.0.0-pre.5

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.
@@ -1,5 +1,4 @@
1
1
  const log = require('proc-log')
2
- const mkdirp = require('mkdirp')
3
2
  const fs = require('fs')
4
3
  const { dirname } = require('path')
5
4
  const os = require('os')
@@ -70,7 +69,7 @@ if (options.loglevel !== 'silent') {
70
69
 
71
70
  if (options.logfile) {
72
71
  log.silly('logfile', options.logfile)
73
- mkdirp.sync(dirname(options.logfile))
72
+ fs.mkdirSync(dirname(options.logfile), { recursive: true })
74
73
  const fd = fs.openSync(options.logfile, 'a')
75
74
  addLogListener((str) => fs.writeSync(fd, str))
76
75
  }
@@ -35,8 +35,8 @@ const add = ({ pkg, add, saveBundle, saveType }) => {
35
35
  const depType = saveTypeMap.get(addSaveType)
36
36
 
37
37
  pkg[depType] = pkg[depType] || {}
38
- if (rawSpec !== '' || pkg[depType][name] === undefined) {
39
- pkg[depType][name] = rawSpec || '*'
38
+ if (rawSpec !== '*' || pkg[depType][name] === undefined) {
39
+ pkg[depType][name] = rawSpec
40
40
  }
41
41
  if (addSaveType === 'optional') {
42
42
  // Affordance for previous npm versions that require this behaviour
@@ -10,9 +10,7 @@ const { resolve, dirname } = require('path')
10
10
  const { promisify } = require('util')
11
11
  const treeCheck = require('../tree-check.js')
12
12
  const readdir = promisify(require('readdir-scoped-modules'))
13
- const fs = require('fs')
14
- const lstat = promisify(fs.lstat)
15
- const readlink = promisify(fs.readlink)
13
+ const { lstat, readlink } = require('fs/promises')
16
14
  const { depth } = require('treeverse')
17
15
  const log = require('proc-log')
18
16
 
@@ -48,7 +46,6 @@ const _flagsSuspect = Symbol.for('flagsSuspect')
48
46
  const _workspaces = Symbol.for('workspaces')
49
47
  const _prune = Symbol('prune')
50
48
  const _preferDedupe = Symbol('preferDedupe')
51
- const _legacyBundling = Symbol('legacyBundling')
52
49
  const _parseSettings = Symbol('parseSettings')
53
50
  const _initTree = Symbol('initTree')
54
51
  const _applyUserRequests = Symbol('applyUserRequests')
@@ -79,7 +76,7 @@ const _loadFailures = Symbol('loadFailures')
79
76
  const _pruneFailedOptional = Symbol('pruneFailedOptional')
80
77
  const _linkNodes = Symbol('linkNodes')
81
78
  const _follow = Symbol('follow')
82
- const _globalStyle = Symbol('globalStyle')
79
+ const _installStrategy = Symbol('installStrategy')
83
80
  const _globalRootNode = Symbol('globalRootNode')
84
81
  const _usePackageLock = Symbol.for('usePackageLock')
85
82
  const _rpcache = Symbol.for('realpathCache')
@@ -114,7 +111,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
114
111
  follow = false,
115
112
  force = false,
116
113
  global = false,
117
- globalStyle = false,
114
+ installStrategy = 'hoisted',
118
115
  idealTree = null,
119
116
  includeWorkspaceRoot = false,
120
117
  installLinks = false,
@@ -134,7 +131,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
134
131
 
135
132
  this[_usePackageLock] = packageLock
136
133
  this[_global] = !!global
137
- this[_globalStyle] = this[_global] || globalStyle
134
+ this[_installStrategy] = global ? 'shallow' : installStrategy
138
135
  this[_follow] = !!follow
139
136
 
140
137
  if (this[_workspaces].length && this[_global]) {
@@ -143,7 +140,6 @@ module.exports = cls => class IdealTreeBuilder extends cls {
143
140
 
144
141
  this[_explicitRequests] = new Set()
145
142
  this[_preferDedupe] = false
146
- this[_legacyBundling] = false
147
143
  this[_depsSeen] = new Set()
148
144
  this[_depsQueue] = []
149
145
  this[_currentDep] = null
@@ -252,20 +248,18 @@ module.exports = cls => class IdealTreeBuilder extends cls {
252
248
 
253
249
  this[_complete] = !!options.complete
254
250
  this[_preferDedupe] = !!options.preferDedupe
255
- this[_legacyBundling] = !!options.legacyBundling
256
251
 
257
252
  // validates list of update names, they must
258
253
  // be dep names only, no semver ranges are supported
259
254
  for (const name of update.names) {
260
255
  const spec = npa(name)
261
256
  const validationError =
262
- new TypeError(`Update arguments must not contain package version specifiers
263
-
264
- Try using the package name instead, e.g:
257
+ new TypeError(`Update arguments must only contain package names, eg:
265
258
  npm update ${spec.name}`)
266
259
  validationError.code = 'EUPDATEARGS'
267
260
 
268
- if (spec.fetchSpec !== 'latest') {
261
+ // If they gave us anything other than a bare package name
262
+ if (spec.raw !== spec.name) {
269
263
  throw validationError
270
264
  }
271
265
  }
@@ -330,8 +324,7 @@ Try using the package name instead, e.g:
330
324
  if (tree.children.size) {
331
325
  root.meta.loadedFromDisk = true
332
326
  // set these so that we don't try to ancient lockfile reload it
333
- root.meta.originalLockfileVersion = defaultLockfileVersion
334
- root.meta.lockfileVersion = defaultLockfileVersion
327
+ root.meta.originalLockfileVersion = root.meta.lockfileVersion = this.options.lockfileVersion || defaultLockfileVersion
335
328
  }
336
329
  }
337
330
  root.meta.inferFormattingOptions(root.package)
@@ -758,7 +751,9 @@ This is a one-time fix-up, please be patient...
758
751
  // yes, yes, this isn't the "original" version, but now that it's been
759
752
  // upgraded, we need to make sure we don't do the work to upgrade it
760
753
  // again, since it's now as new as can be.
761
- meta.originalLockfileVersion = defaultLockfileVersion
754
+ if (!this.options.lockfileVersion && !meta.hiddenLockfile) {
755
+ meta.originalLockfileVersion = defaultLockfileVersion
756
+ }
762
757
  this.finishTracker('idealTree:inflate')
763
758
  process.emit('timeEnd', 'idealTree:inflate')
764
759
  }
@@ -951,11 +946,10 @@ This is a one-time fix-up, please be patient...
951
946
  auditReport: this.auditReport,
952
947
  force: this[_force],
953
948
  preferDedupe: this[_preferDedupe],
954
- legacyBundling: this[_legacyBundling],
955
949
  strictPeerDeps: this[_strictPeerDeps],
956
950
  installLinks: this.installLinks,
957
951
  legacyPeerDeps: this.legacyPeerDeps,
958
- globalStyle: this[_globalStyle],
952
+ installStrategy: this[_installStrategy],
959
953
  }))
960
954
 
961
955
  const promises = []
@@ -76,6 +76,7 @@ class Arborist extends Base {
76
76
  workspacesEnabled: options.workspacesEnabled !== false,
77
77
  replaceRegistryHost: options.replaceRegistryHost,
78
78
  lockfileVersion: lockfileVersion(options.lockfileVersion),
79
+ installStrategy: options.global ? 'shallow' : (options.installStrategy ? options.installStrategy : 'hoisted'),
79
80
  }
80
81
  this.replaceRegistryHost = this.options.replaceRegistryHost =
81
82
  (!this.options.replaceRegistryHost || this.options.replaceRegistryHost === 'npmjs') ?
@@ -12,14 +12,13 @@ const log = require('proc-log')
12
12
 
13
13
  const { dirname, resolve, relative } = require('path')
14
14
  const { depth: dfwalk } = require('treeverse')
15
- const fs = require('fs')
16
- const { promisify } = require('util')
17
- const lstat = promisify(fs.lstat)
18
- const symlink = promisify(fs.symlink)
19
- const mkdirp = require('mkdirp-infer-owner')
20
- const justMkdirp = require('mkdirp')
15
+ const {
16
+ lstat,
17
+ mkdir,
18
+ rm,
19
+ symlink,
20
+ } = require('fs/promises')
21
21
  const moveFile = require('@npmcli/move-file')
22
- const rimraf = promisify(require('rimraf'))
23
22
  const PackageJson = require('@npmcli/package-json')
24
23
  const packageContents = require('@npmcli/installed-package-contents')
25
24
  const runScript = require('@npmcli/run-script')
@@ -35,6 +34,9 @@ const optionalSet = require('../optional-set.js')
35
34
  const calcDepFlags = require('../calc-dep-flags.js')
36
35
  const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js')
37
36
 
37
+ const Shrinkwrap = require('../shrinkwrap.js')
38
+ const { defaultLockfileVersion } = Shrinkwrap
39
+
38
40
  const _retiredPaths = Symbol('retiredPaths')
39
41
  const _retiredUnchanged = Symbol('retiredUnchanged')
40
42
  const _sparseTreeDirs = Symbol('sparseTreeDirs')
@@ -172,7 +174,7 @@ module.exports = cls => class Reifier extends cls {
172
174
  // we do NOT want to set ownership on this folder, especially
173
175
  // recursively, because it can have other side effects to do that
174
176
  // in a project directory. We just want to make it if it's missing.
175
- await justMkdirp(resolve(this.path))
177
+ await mkdir(resolve(this.path), { recursive: true })
176
178
 
177
179
  // do not allow the top-level node_modules to be a symlink
178
180
  await this[_validateNodeModules](resolve(this.path, 'node_modules'))
@@ -430,10 +432,10 @@ module.exports = cls => class Reifier extends cls {
430
432
  // handled the most common cause of ENOENT (dir doesn't exist yet),
431
433
  // then just ignore any ENOENT.
432
434
  if (er.code === 'ENOENT') {
433
- return didMkdirp ? null : mkdirp(dirname(to)).then(() =>
435
+ return didMkdirp ? null : mkdir(dirname(to), { recursive: true }).then(() =>
434
436
  this[_renamePath](from, to, true))
435
437
  } else if (er.code === 'EEXIST') {
436
- return rimraf(to).then(() => moveFile(from, to))
438
+ return rm(to, { recursive: true, force: true }).then(() => moveFile(from, to))
437
439
  } else {
438
440
  throw er
439
441
  }
@@ -515,7 +517,7 @@ module.exports = cls => class Reifier extends cls {
515
517
  await this[_renamePath](d, retired)
516
518
  }
517
519
  }
518
- const made = await mkdirp(node.path)
520
+ const made = await mkdir(node.path, { recursive: true })
519
521
  this[_sparseTreeDirs].add(node.path)
520
522
  this[_sparseTreeRoots].add(made)
521
523
  }))
@@ -530,7 +532,7 @@ module.exports = cls => class Reifier extends cls {
530
532
  const failures = []
531
533
  const targets = [...roots, ...Object.keys(this[_retiredPaths])]
532
534
  const unlinks = targets
533
- .map(path => rimraf(path).catch(er => failures.push([path, er])))
535
+ .map(path => rm(path, { recursive: true, force: true }).catch(er => failures.push([path, er])))
534
536
  return promiseAllRejectLate(unlinks).then(() => {
535
537
  // eslint-disable-next-line promise/always-return
536
538
  if (failures.length) {
@@ -627,7 +629,7 @@ module.exports = cls => class Reifier extends cls {
627
629
  return
628
630
  }
629
631
  log.warn('reify', 'Removing non-directory', nm)
630
- await rimraf(nm)
632
+ await rm(nm, { recursive: true, force: true })
631
633
  }
632
634
 
633
635
  async [_extractOrLink] (node) {
@@ -661,7 +663,7 @@ module.exports = cls => class Reifier extends cls {
661
663
  await this[_validateNodeModules](nm)
662
664
 
663
665
  if (node.isLink) {
664
- await rimraf(node.path)
666
+ await rm(node.path, { recursive: true, force: true })
665
667
  await this[_symlink](node)
666
668
  } else {
667
669
  await debug(async () => {
@@ -687,7 +689,7 @@ module.exports = cls => class Reifier extends cls {
687
689
  const dir = dirname(node.path)
688
690
  const target = node.realpath
689
691
  const rel = relative(dir, target)
690
- await mkdirp(dir)
692
+ await mkdir(dir, { recursive: true })
691
693
  return symlink(rel, node.path, 'junction')
692
694
  }
693
695
 
@@ -950,7 +952,7 @@ module.exports = cls => class Reifier extends cls {
950
952
 
951
953
  // ok! actually unpack stuff into their target locations!
952
954
  // The sparse tree has already been created, so we walk the diff
953
- // kicking off each unpack job. If any fail, we rimraf the sparse
955
+ // kicking off each unpack job. If any fail, we rm the sparse
954
956
  // tree entirely and try to put everything back where it was.
955
957
  [_unpackNewModules] () {
956
958
  process.emit('time', 'reify:unpack')
@@ -1031,7 +1033,8 @@ module.exports = cls => class Reifier extends cls {
1031
1033
  return promiseAllRejectLate(diff.unchanged.map(node => {
1032
1034
  // no need to roll back links, since we'll just delete them anyway
1033
1035
  if (node.isLink) {
1034
- return mkdirp(dirname(node.path)).then(() => this[_reifyNode](node))
1036
+ return mkdir(dirname(node.path), { recursive: true, force: true })
1037
+ .then(() => this[_reifyNode](node))
1035
1038
  }
1036
1039
 
1037
1040
  // will have been moved/unpacked along with bundler
@@ -1047,7 +1050,7 @@ module.exports = cls => class Reifier extends cls {
1047
1050
  // skip it.
1048
1051
  const bd = node.package.bundleDependencies
1049
1052
  const dir = bd && bd.length ? node.path + '/node_modules' : node.path
1050
- return mkdirp(dir).then(() => this[_moveContents](node, fromPath))
1053
+ return mkdir(dir, { recursive: true }).then(() => this[_moveContents](node, fromPath))
1051
1054
  }))
1052
1055
  }))
1053
1056
  .then(() => process.emit('timeEnd', 'reify:unretire'))
@@ -1121,15 +1124,15 @@ module.exports = cls => class Reifier extends cls {
1121
1124
  // the tree is pretty much built now, so it's cleanup time.
1122
1125
  // remove the retired folders, and any deleted nodes
1123
1126
  // If this fails, there isn't much we can do but tell the user about it.
1124
- // Thankfully, it's pretty unlikely that it'll fail, since rimraf is a tank.
1127
+ // Thankfully, it's pretty unlikely that it'll fail, since rm is a node builtin.
1125
1128
  async [_removeTrash] () {
1126
1129
  process.emit('time', 'reify:trash')
1127
1130
  const promises = []
1128
1131
  const failures = []
1129
- const rm = path => rimraf(path).catch(er => failures.push([path, er]))
1132
+ const _rm = path => rm(path, { recursive: true, force: true }).catch(er => failures.push([path, er]))
1130
1133
 
1131
1134
  for (const path of this[_trashList]) {
1132
- promises.push(rm(path))
1135
+ promises.push(_rm(path))
1133
1136
  }
1134
1137
 
1135
1138
  await promiseAllRejectLate(promises)
@@ -1514,11 +1517,16 @@ module.exports = cls => class Reifier extends cls {
1514
1517
  this.idealTree.meta.filename =
1515
1518
  this.idealTree.realpath + '/node_modules/.package-lock.json'
1516
1519
  this.idealTree.meta.hiddenLockfile = true
1520
+ const resetMeta = this.idealTree.meta && this.idealTree.meta.lockfileVersion !== defaultLockfileVersion
1521
+ this.idealTree.meta.lockfileVersion = defaultLockfileVersion
1517
1522
 
1518
1523
  this.actualTree = this.idealTree
1519
1524
  this.idealTree = null
1520
1525
 
1521
1526
  if (!this[_global]) {
1527
+ if (resetMeta) {
1528
+ await this.actualTree.meta.reset()
1529
+ }
1522
1530
  await this.actualTree.meta.save()
1523
1531
  const ignoreScripts = !!this.options.ignoreScripts
1524
1532
  // if we aren't doing a dry run or ignoring scripts and we actually made changes to the dep
@@ -19,17 +19,23 @@ const consistentResolve = (resolved, fromPath, toPath, relPaths = false) => {
19
19
  rawSpec,
20
20
  raw,
21
21
  } = npa(resolved, fromPath)
22
- const isPath = type === 'file' || type === 'directory'
23
- return isPath && !relPaths ? `file:${fetchSpec.replace(/#/g, '%23')}`
24
- : isPath ? 'file:' + (toPath ? relpath(toPath, fetchSpec.replace(/#/g, '%23')) : fetchSpec.replace(/#/g, '%23'))
25
- : hosted ? `git+${
26
- hosted.auth ? hosted.https(hostedOpt) : hosted.sshurl(hostedOpt)
27
- }`
28
- : type === 'git' ? saveSpec
29
- // always return something. 'foo' is interpreted as 'foo@' otherwise.
30
- : rawSpec === '' && raw.slice(-1) !== '@' ? raw
31
- // just strip off the name, but otherwise return as-is
32
- : rawSpec
22
+ if (type === 'file' || type === 'directory') {
23
+ const cleanFetchSpec = fetchSpec.replace(/#/g, '%23')
24
+ if (relPaths && toPath) {
25
+ return `file:${relpath(toPath, cleanFetchSpec)}`
26
+ }
27
+ return `file:${cleanFetchSpec}`
28
+ }
29
+ if (hosted) {
30
+ return `git+${hosted.auth ? hosted.https(hostedOpt) : hosted.sshurl(hostedOpt)}`
31
+ }
32
+ if (type === 'git') {
33
+ return saveSpec
34
+ }
35
+ if (rawSpec === '*') {
36
+ return raw
37
+ }
38
+ return rawSpec
33
39
  } catch (_) {
34
40
  // whatever we passed in was not acceptable to npa.
35
41
  // leave it 100% untouched.
package/lib/edge.js CHANGED
@@ -166,7 +166,7 @@ class Edge {
166
166
  }
167
167
 
168
168
  get spec () {
169
- if (this.overrides && this.overrides.value && this.overrides.name === this.name) {
169
+ if (this.overrides?.value && this.overrides.value !== '*' && this.overrides.name === this.name) {
170
170
  if (this.overrides.value.startsWith('$')) {
171
171
  const ref = this.overrides.value.slice(1)
172
172
  // we may be a virtual root, if we are we want to resolve reference overrides
@@ -25,7 +25,7 @@ class OverrideSet {
25
25
  this.name = spec.name
26
26
  spec.name = ''
27
27
  this.key = key
28
- this.keySpec = spec.rawSpec === '' ? '' : spec.toString()
28
+ this.keySpec = spec.toString()
29
29
  this.value = overrides['.'] || this.keySpec
30
30
  }
31
31
 
@@ -50,8 +50,7 @@ class OverrideSet {
50
50
  continue
51
51
  }
52
52
 
53
- if (rule.keySpec === '' ||
54
- semver.intersects(edge.spec, rule.keySpec)) {
53
+ if (semver.intersects(edge.spec, rule.keySpec)) {
55
54
  return rule
56
55
  }
57
56
  }
@@ -65,8 +64,7 @@ class OverrideSet {
65
64
  continue
66
65
  }
67
66
 
68
- if (rule.keySpec === '' ||
69
- semver.satisfies(node.version, rule.keySpec) ||
67
+ if (semver.satisfies(node.version, rule.keySpec) ||
70
68
  semver.satisfies(node.version, rule.value)) {
71
69
  return rule
72
70
  }
@@ -81,8 +79,7 @@ class OverrideSet {
81
79
  continue
82
80
  }
83
81
 
84
- if (rule.keySpec === '' ||
85
- semver.satisfies(node.version, rule.keySpec) ||
82
+ if (semver.satisfies(node.version, rule.keySpec) ||
86
83
  semver.satisfies(node.version, rule.value)) {
87
84
  return rule
88
85
  }
package/lib/place-dep.js CHANGED
@@ -43,11 +43,10 @@ class PlaceDep {
43
43
  explicitRequest,
44
44
  updateNames,
45
45
  auditReport,
46
- legacyBundling,
47
46
  strictPeerDeps,
48
47
  installLinks,
49
48
  legacyPeerDeps,
50
- globalStyle,
49
+ installStrategy,
51
50
  } = parent || options
52
51
  Object.assign(this, {
53
52
  preferDedupe,
@@ -55,11 +54,10 @@ class PlaceDep {
55
54
  explicitRequest,
56
55
  updateNames,
57
56
  auditReport,
58
- legacyBundling,
59
57
  strictPeerDeps,
60
58
  installLinks,
59
+ installStrategy,
61
60
  legacyPeerDeps,
62
- globalStyle,
63
61
  })
64
62
 
65
63
  this.children = []
@@ -78,10 +76,9 @@ class PlaceDep {
78
76
  edge,
79
77
  dep,
80
78
  preferDedupe,
81
- globalStyle,
82
- legacyBundling,
83
79
  explicitRequest,
84
80
  updateNames,
81
+ installStrategy,
85
82
  checks,
86
83
  } = this
87
84
 
@@ -170,13 +167,13 @@ class PlaceDep {
170
167
 
171
168
  // nest packages like npm v1 and v2
172
169
  // very disk-inefficient
173
- if (legacyBundling) {
170
+ if (installStrategy === 'nested') {
174
171
  break
175
172
  }
176
173
 
177
174
  // when installing globally, or just in global style, we never place
178
175
  // deps above the first level.
179
- if (globalStyle) {
176
+ if (installStrategy === 'shallow') {
180
177
  const rp = target.resolveParent
181
178
  if (rp && rp.isProjectRoot) {
182
179
  break
@@ -463,7 +460,7 @@ class PlaceDep {
463
460
  // prune all the nodes in a branch of the tree that can be safely removed
464
461
  // This is only the most basic duplication detection; it finds if there
465
462
  // is another satisfying node further up the tree, and if so, dedupes.
466
- // Even in legacyBundling mode, we do this amount of deduplication.
463
+ // Even in installStategy is nested, we do this amount of deduplication.
467
464
  pruneDedupable (node, descend = true) {
468
465
  if (node.canDedupe(this.preferDedupe)) {
469
466
  // gather up all deps that have no valid edges in from outside
@@ -120,13 +120,13 @@ class Results {
120
120
  this.#pendingCombinator = combinators[String(this.currentAstNode)]
121
121
  }
122
122
 
123
- // name selectors (i.e. #foo, #foo@1.0.0)
123
+ // name selectors (i.e. #foo)
124
124
  // css calls this id, we interpret it as name
125
125
  idType () {
126
- const spec = npa(this.currentAstNode.value)
126
+ const name = this.currentAstNode.value
127
127
  const nextResults = this.initialItems.filter(node =>
128
- (node.name === spec.name || node.package.name === spec.name) &&
129
- (semver.satisfies(node.version, spec.fetchSpec) || !spec.rawSpec))
128
+ (name === node.name) || (name === node.package.name)
129
+ )
130
130
  this.processPendingCombinator(nextResults)
131
131
  }
132
132
 
package/lib/realpath.js CHANGED
@@ -5,10 +5,7 @@
5
5
  // built-in fs.realpath, because we only care about symbolic links,
6
6
  // so we can handle many fewer edge cases.
7
7
 
8
- const fs = require('fs')
9
- const promisify = require('util').promisify
10
- const readlink = promisify(fs.readlink)
11
- const lstat = promisify(fs.lstat)
8
+ const { lstat, readlink } = require('fs/promises')
12
9
  const { resolve, basename, dirname } = require('path')
13
10
 
14
11
  const realpathCached = (path, rpcache, stcache, depth) => {
package/lib/shrinkwrap.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // definitely not before npm v8.
11
11
 
12
12
  const localeCompare = require('@isaacs/string-locale-compare')('en')
13
- const defaultLockfileVersion = 2
13
+ const defaultLockfileVersion = 3
14
14
 
15
15
  // for comparing nodes to yarn.lock entries
16
16
  const mismatch = (a, b) => a && b && a !== b
@@ -35,32 +35,16 @@ const mismatch = (a, b) => a && b && a !== b
35
35
 
36
36
  const log = require('proc-log')
37
37
  const YarnLock = require('./yarn-lock.js')
38
- const { promisify } = require('util')
39
- const rimraf = promisify(require('rimraf'))
40
- const fs = require('fs')
41
- const readFile = promisify(fs.readFile)
42
- const writeFile = promisify(fs.writeFile)
43
- const stat = promisify(fs.stat)
44
- const readdir_ = promisify(fs.readdir)
45
- const readlink = promisify(fs.readlink)
46
-
47
- // XXX remove when drop support for node v10
48
- const lstat = promisify(fs.lstat)
49
- /* istanbul ignore next - version specific polyfill */
50
- const readdir = async (path, opt) => {
51
- if (!opt || !opt.withFileTypes) {
52
- return readdir_(path, opt)
53
- }
54
- const ents = await readdir_(path, opt)
55
- if (typeof ents[0] === 'string') {
56
- return Promise.all(ents.map(async ent => {
57
- return Object.assign(await lstat(path + '/' + ent), { name: ent })
58
- }))
59
- }
60
- return ents
61
- }
62
-
63
- const { resolve, basename } = require('path')
38
+ const {
39
+ readFile,
40
+ readdir,
41
+ readlink,
42
+ rm,
43
+ stat,
44
+ writeFile,
45
+ } = require('fs/promises')
46
+
47
+ const { resolve, basename, relative } = require('path')
64
48
  const specFromLock = require('./spec-from-lock.js')
65
49
  const versionFromTgz = require('./version-from-tgz.js')
66
50
  const npa = require('npm-package-arg')
@@ -224,6 +208,7 @@ const _buildLegacyLockfile = Symbol('_buildLegacyLockfile')
224
208
  const _filenameSet = Symbol('_filenameSet')
225
209
  const _maybeRead = Symbol('_maybeRead')
226
210
  const _maybeStat = Symbol('_maybeStat')
211
+
227
212
  class Shrinkwrap {
228
213
  static get defaultLockfileVersion () {
229
214
  return defaultLockfileVersion
@@ -252,17 +237,6 @@ class Shrinkwrap {
252
237
  s.loadedFromDisk = !!(sw || lock)
253
238
  s.type = basename(s.filename)
254
239
 
255
- try {
256
- if (s.loadedFromDisk && !s.lockfileVersion) {
257
- const json = parseJSON(await maybeReadFile(s.filename))
258
- if (json.lockfileVersion > defaultLockfileVersion) {
259
- s.lockfileVersion = json.lockfileVersion
260
- }
261
- }
262
- } catch {
263
- // ignore errors
264
- }
265
-
266
240
  return s
267
241
  }
268
242
 
@@ -342,6 +316,7 @@ class Shrinkwrap {
342
316
  this.lockfileVersion = hiddenLockfile ? 3
343
317
  : lockfileVersion ? parseInt(lockfileVersion, 10)
344
318
  : null
319
+
345
320
  this[_awaitingUpdate] = new Map()
346
321
  this.tree = null
347
322
  this.path = resolve(path || '.')
@@ -398,6 +373,7 @@ class Shrinkwrap {
398
373
  this[_awaitingUpdate] = new Map()
399
374
  const lockfileVersion = this.lockfileVersion || defaultLockfileVersion
400
375
  this.originalLockfileVersion = lockfileVersion
376
+
401
377
  this.data = {
402
378
  lockfileVersion,
403
379
  requires: true,
@@ -496,8 +472,14 @@ class Shrinkwrap {
496
472
  this.ancientLockfile = false
497
473
  return {}
498
474
  }).then(lock => {
499
- const lockfileVersion = this.lockfileVersion ? this.lockfileVersion
500
- : Math.max(lock.lockfileVersion || 0, defaultLockfileVersion)
475
+ // auto convert v1 lockfiles to v3
476
+ // leave v2 in place unless configured
477
+ // v3 by default
478
+ const lockfileVersion =
479
+ this.lockfileVersion ? this.lockfileVersion
480
+ : lock.lockfileVersion === 1 ? defaultLockfileVersion
481
+ : lock.lockfileVersion || defaultLockfileVersion
482
+
501
483
  this.data = {
502
484
  ...lock,
503
485
  lockfileVersion: lockfileVersion,
@@ -507,6 +489,7 @@ class Shrinkwrap {
507
489
  }
508
490
 
509
491
  this.originalLockfileVersion = lock.lockfileVersion
492
+
510
493
  // use default if it wasn't explicitly set, and the current file is
511
494
  // less than our default. otherwise, keep whatever is in the file,
512
495
  // unless we had an explicit setting already.
@@ -1135,7 +1118,17 @@ class Shrinkwrap {
1135
1118
  if (!this.data) {
1136
1119
  throw new Error('run load() before saving data')
1137
1120
  }
1121
+
1138
1122
  const json = this.toString(options)
1123
+ if (
1124
+ !this.hiddenLockfile
1125
+ && this.originalLockfileVersion !== undefined
1126
+ && this.originalLockfileVersion !== this.lockfileVersion
1127
+ ) {
1128
+ log.warn(
1129
+ `Converting lock file (${relative(process.cwd(), this.filename)}) from v${this.originalLockfileVersion} -> v${this.lockfileVersion}`
1130
+ )
1131
+ }
1139
1132
  return Promise.all([
1140
1133
  writeFile(this.filename, json).catch(er => {
1141
1134
  if (this.hiddenLockfile) {
@@ -1144,7 +1137,7 @@ class Shrinkwrap {
1144
1137
  // a node_modules folder, but then the lockfile is not important.
1145
1138
  // Remove the file, so that in case there WERE deps, but we just
1146
1139
  // failed to update the file for some reason, it's not out of sync.
1147
- return rimraf(this.filename)
1140
+ return rm(this.filename, { recursive: true, force: true })
1148
1141
  }
1149
1142
  throw er
1150
1143
  }),
package/package.json CHANGED
@@ -1,48 +1,45 @@
1
1
  {
2
2
  "name": "@npmcli/arborist",
3
- "version": "6.0.0-pre.3",
3
+ "version": "6.0.0-pre.5",
4
4
  "description": "Manage node_modules trees",
5
5
  "dependencies": {
6
6
  "@isaacs/string-locale-compare": "^1.1.0",
7
- "@npmcli/installed-package-contents": "^1.0.7",
8
- "@npmcli/map-workspaces": "^2.0.3",
9
- "@npmcli/metavuln-calculator": "^4.0.0-pre.0",
10
- "@npmcli/move-file": "^2.0.0",
7
+ "@npmcli/installed-package-contents": "^2.0.0",
8
+ "@npmcli/map-workspaces": "^3.0.0",
9
+ "@npmcli/metavuln-calculator": "^5.0.0",
10
+ "@npmcli/move-file": "^3.0.0",
11
11
  "@npmcli/name-from-folder": "^1.0.1",
12
- "@npmcli/node-gyp": "^2.0.0",
13
- "@npmcli/package-json": "^2.0.0",
14
- "@npmcli/query": "^2.0.0",
15
- "@npmcli/run-script": "^4.1.3",
16
- "bin-links": "^3.0.3",
17
- "cacache": "^16.1.3",
12
+ "@npmcli/node-gyp": "^3.0.0",
13
+ "@npmcli/package-json": "^3.0.0",
14
+ "@npmcli/query": "^3.0.0",
15
+ "@npmcli/run-script": "^5.0.0",
16
+ "bin-links": "^4.0.1",
17
+ "cacache": "^17.0.1",
18
18
  "common-ancestor-path": "^1.0.1",
19
- "json-parse-even-better-errors": "^2.3.1",
19
+ "json-parse-even-better-errors": "^3.0.0",
20
20
  "json-stringify-nice": "^1.1.4",
21
21
  "minimatch": "^5.1.0",
22
- "mkdirp": "^1.0.4",
23
- "mkdirp-infer-owner": "^2.0.0",
24
22
  "nopt": "^6.0.0",
25
- "npm-install-checks": "^5.0.0",
26
- "npm-package-arg": "^9.0.0",
27
- "npm-pick-manifest": "^7.0.2",
28
- "npm-registry-fetch": "^13.0.0",
29
- "npmlog": "^6.0.2",
30
- "pacote": "^14.0.0-pre.3",
31
- "parse-conflict-json": "^2.0.1",
32
- "proc-log": "^2.0.0",
23
+ "npm-install-checks": "^6.0.0",
24
+ "npm-package-arg": "^10.0.0",
25
+ "npm-pick-manifest": "^8.0.1",
26
+ "npm-registry-fetch": "^14.0.2",
27
+ "npmlog": "^7.0.1",
28
+ "pacote": "^15.0.2",
29
+ "parse-conflict-json": "^3.0.0",
30
+ "proc-log": "^3.0.0",
33
31
  "promise-all-reject-late": "^1.0.0",
34
32
  "promise-call-limit": "^1.0.1",
35
- "read-package-json-fast": "^2.0.2",
33
+ "read-package-json-fast": "^3.0.1",
36
34
  "readdir-scoped-modules": "^1.1.0",
37
- "rimraf": "^3.0.2",
38
35
  "semver": "^7.3.7",
39
- "ssri": "^9.0.0",
40
- "treeverse": "^2.0.0",
36
+ "ssri": "^10.0.0",
37
+ "treeverse": "^3.0.0",
41
38
  "walk-up-path": "^1.0.0"
42
39
  },
43
40
  "devDependencies": {
44
- "@npmcli/eslint-config": "^3.1.0",
45
- "@npmcli/template-oss": "4.4.1",
41
+ "@npmcli/eslint-config": "^4.0.0",
42
+ "@npmcli/template-oss": "4.6.2",
46
43
  "benchmark": "^2.1.4",
47
44
  "chalk": "^4.1.0",
48
45
  "minify-registry-metadata": "^2.1.0",
@@ -104,7 +101,7 @@
104
101
  },
105
102
  "templateOSS": {
106
103
  "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
107
- "version": "4.4.1",
104
+ "version": "4.6.2",
108
105
  "content": "../../scripts/template-oss/index.js"
109
106
  }
110
107
  }