Skip to content
This repository was archived by the owner on Jan 20, 2022. It is now read-only.

Commit c7f2370

Browse files
committed
feat: Do not implicitly downgrade lockfileVersion from 3 to 2
If lockfileVersion is explicitly set to 3, and then later it is not explicitly set, then we continue to use a version 3 lockfile. If the lockfileVersion is explicitly set, we always use the version in the options. Also adds validation to the lockfileVersion config option, and cleans up the logic in old/ancient lockfile inflation, so that we're not doing that extra work if there just isn't a lockfile at all. Finally, this also corrects a weird edge case where we were doing the "inflate old lockfile" behavior when there is no lockfile present and we load the initial idealTree state from the actualTree. That would result in adding integrity values for packages found present in the tree. However, as we cannot be sure that `node_modules/foo` came from `{registry}/foo`, this is an assertion we can't guarantee. No one has yet reported this obscure edge case as causing problems. PR-URL: #329 Credit: @isaacs Close: #329 Reviewed-by: @ljharb, @wraithgar
1 parent e840b85 commit c7f2370

File tree

13 files changed

+14481
-23
lines changed

13 files changed

+14481
-23
lines changed

lib/arborist/build-ideal-tree.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const debug = require('../debug.js')
2626
const fromPath = require('../from-path.js')
2727
const calcDepFlags = require('../calc-dep-flags.js')
2828
const Shrinkwrap = require('../shrinkwrap.js')
29+
const { defaultLockfileVersion } = Shrinkwrap
2930
const Node = require('../node.js')
3031
const Link = require('../link.js')
3132
const addRmPkgDeps = require('../add-rm-pkg-deps.js')
@@ -328,6 +329,9 @@ module.exports = cls => class IdealTreeBuilder extends cls {
328329
// dep flags before assuming that any mutations were reflected.
329330
if (tree.children.size) {
330331
root.meta.loadedFromDisk = true
332+
// set these so that we don't try to ancient lockfile reload it
333+
root.meta.originalLockfileVersion = defaultLockfileVersion
334+
root.meta.lockfileVersion = defaultLockfileVersion
331335
}
332336
}
333337
return root
@@ -701,14 +705,18 @@ module.exports = cls => class IdealTreeBuilder extends cls {
701705
// least it's just a one-time hit.
702706
process.emit('time', 'idealTree:inflate')
703707

708+
// don't warn if we're not gonna actually write it back anyway.
704709
const heading = ancient ? 'ancient lockfile' : 'old lockfile'
705-
this.log.warn(heading,
706-
`
710+
if (ancient || !this.options.lockfileVersion ||
711+
this.options.lockfileVersion >= defaultLockfileVersion) {
712+
this.log.warn(heading,
713+
`
707714
The ${meta.type} file was created with an old version of npm,
708715
so supplemental metadata must be fetched from the registry.
709716
710717
This is a one-time fix-up, please be patient...
711718
`)
719+
}
712720

713721
this.addTracker('idealTree:inflate')
714722
const queue = []
@@ -753,7 +761,7 @@ This is a one-time fix-up, please be patient...
753761
// yes, yes, this isn't the "original" version, but now that it's been
754762
// upgraded, we need to make sure we don't do the work to upgrade it
755763
// again, since it's now as new as can be.
756-
meta.originalLockfileVersion = 2
764+
meta.originalLockfileVersion = defaultLockfileVersion
757765
this.finishTracker('idealTree:inflate')
758766
process.emit('timeEnd', 'idealTree:inflate')
759767
}

lib/arborist/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ const _workspacesEnabled = Symbol.for('workspacesEnabled')
4848
const Base = mixins.reduce((a, b) => b(a), require('events'))
4949
const getWorkspaceNodes = require('../get-workspace-nodes.js')
5050

51+
// if it's 1, 2, or 3, set it explicitly that.
52+
// if undefined or null, set it null
53+
// otherwise, throw.
54+
const lockfileVersion = lfv => {
55+
if (lfv === 1 || lfv === 2 || lfv === 3) {
56+
return lfv
57+
}
58+
59+
if (lfv === undefined || lfv === null) {
60+
return null
61+
}
62+
63+
throw new TypeError('Invalid lockfileVersion config: ' + lfv)
64+
}
65+
5166
class Arborist extends Base {
5267
constructor (options = {}) {
5368
process.emit('time', 'arborist:ctor')
@@ -60,6 +75,7 @@ class Arborist extends Base {
6075
packumentCache: options.packumentCache || new Map(),
6176
log: options.log || procLog,
6277
workspacesEnabled: options.workspacesEnabled !== false,
78+
lockfileVersion: lockfileVersion(options.lockfileVersion),
6379
}
6480

6581
this[_workspacesEnabled] = this.options.workspacesEnabled

lib/arborist/load-actual.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,12 @@ module.exports = cls => class ActualLoader extends cls {
137137
real: await realpath(this.path, this[_rpcache], this[_stcache]),
138138
})
139139

140-
// XXX only rely on this if the hidden lockfile is the newest thing?
141-
// need some kind of heuristic, like if the package.json or sw have
142-
// been touched more recently, then ignore it? This is a hazard if
143-
// user switches back and forth between Arborist and another way of
144-
// mutating the node_modules folder.
140+
// Note: hidden lockfile will be rejected if it's not the latest thing
141+
// in the folder, or if any of the entries in the hidden lockfile are
142+
// missing.
145143
const meta = await Shrinkwrap.load({
146144
path: this[_actualTree].path,
147145
hiddenLockfile: true,
148-
lockfileVersion: 3,
149146
})
150147
if (meta.loadedFromDisk) {
151148
this[_actualTree].meta = meta

lib/shrinkwrap.js

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ const _filenameSet = Symbol('_filenameSet')
226226
const _maybeRead = Symbol('_maybeRead')
227227
const _maybeStat = Symbol('_maybeStat')
228228
class Shrinkwrap {
229+
static get defaultLockfileVersion () {
230+
return defaultLockfileVersion
231+
}
232+
229233
static load (options) {
230234
return new Shrinkwrap(options).load()
231235
}
@@ -316,10 +320,12 @@ class Shrinkwrap {
316320
shrinkwrapOnly = false,
317321
hiddenLockfile = false,
318322
log = procLog,
319-
lockfileVersion = defaultLockfileVersion,
323+
lockfileVersion,
320324
} = options
321325

322-
this.lockfileVersion = parseInt(hiddenLockfile ? 3 : lockfileVersion, 10)
326+
this.lockfileVersion = hiddenLockfile ? 3
327+
: lockfileVersion ? parseInt(lockfileVersion, 10)
328+
: null
323329
this.log = log
324330
this[_awaitingUpdate] = new Map()
325331
this.tree = null
@@ -376,7 +382,7 @@ class Shrinkwrap {
376382
this[_awaitingUpdate] = new Map()
377383
this.originalLockfileVersion = this.lockfileVersion
378384
this.data = {
379-
lockfileVersion: this.lockfileVersion,
385+
lockfileVersion: this.lockfileVersion || defaultLockfileVersion,
380386
requires: true,
381387
packages: {},
382388
dependencies: {},
@@ -462,15 +468,23 @@ class Shrinkwrap {
462468
this.ancientLockfile = false
463469
return {}
464470
}).then(lock => {
471+
const lockfileVersion = this.lockfileVersion ? this.lockfileVersion
472+
: Math.max(lock.lockfileVersion || 0, defaultLockfileVersion)
465473
this.data = {
466474
...lock,
467-
lockfileVersion: this.lockfileVersion,
475+
lockfileVersion: lockfileVersion,
468476
requires: true,
469477
packages: lock.packages || {},
470478
dependencies: lock.dependencies || {},
471479
}
472480

473481
this.originalLockfileVersion = lock.lockfileVersion
482+
// use default if it wasn't explicitly set, and the current file is
483+
// less than our default. otherwise, keep whatever is in the file,
484+
// unless we had an explicit setting already.
485+
if (!this.lockfileVersion) {
486+
this.lockfileVersion = this.data.lockfileVersion = lockfileVersion
487+
}
474488
this.ancientLockfile = this.loadedFromDisk &&
475489
!(lock.lockfileVersion >= 2) && !lock.requires
476490

@@ -881,25 +895,32 @@ class Shrinkwrap {
881895
}
882896
}
883897

898+
// if we haven't set it by now, use the default
899+
if (!this.lockfileVersion) {
900+
this.lockfileVersion = defaultLockfileVersion
901+
}
902+
this.data.lockfileVersion = this.lockfileVersion
903+
884904
// hidden lockfiles don't include legacy metadata or a root entry
885905
if (this.hiddenLockfile) {
886906
delete this.data.packages['']
887907
delete this.data.dependencies
888-
} else if (this.tree) {
908+
} else if (this.tree && this.lockfileVersion <= 3) {
889909
this[_buildLegacyLockfile](this.tree, this.data)
890910
}
891911

892-
const data = { ...this.data }
893912
// lf version 1 = dependencies only
894913
// lf version 2 = dependencies and packages
895914
// lf version 3 = packages only
896915
if (this.lockfileVersion >= 3) {
897-
delete data.dependencies
916+
const { dependencies, ...data } = this.data
917+
return data
898918
} else if (this.lockfileVersion < 2) {
899-
delete data.packages
919+
const { packages, ...data } = this.data
920+
return data
921+
} else {
922+
return { ...this.data }
900923
}
901-
902-
return data
903924
}
904925

905926
[_buildLegacyLockfile] (node, lock, path = []) {

0 commit comments

Comments
 (0)