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

Commit

Permalink
feat: make lockfileVersion configurable
Browse files Browse the repository at this point in the history
This will tell Arborist to save an appropriate lockfile based on
the version specified.

Version 3 = packages section only
Version 2 = packages and dependencies sections (default)
Version 1 = dependencies section only (legacy npm v6 support)

Re: npm/rfcs#434
  • Loading branch information
isaacs committed Oct 12, 2021
1 parent 00e2164 commit d47623c
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 16 deletions.
2 changes: 1 addition & 1 deletion bin/shrinkwrap.js
Expand Up @@ -5,7 +5,7 @@ require('./lib/timers.js')

const { quiet } = options
Shrinkwrap.load(options)
.then(s => quiet || console.log(JSON.stringify(s.data, 0, 2)))
.then(s => quiet || console.log(JSON.stringify(s.commit(), 0, 2)))
.catch(er => {
console.error('shrinkwrap load failure', er)
process.exit(1)
Expand Down
11 changes: 8 additions & 3 deletions lib/arborist/build-ideal-tree.js
Expand Up @@ -307,8 +307,10 @@ module.exports = cls => class IdealTreeBuilder extends cls {
// reconstructing it anyway.
.then(root => this[_global] ? root
: !this[_usePackageLock] || this[_updateAll]
? Shrinkwrap.reset({ path: this.path })
.then(meta => Object.assign(root, {meta}))
? Shrinkwrap.reset({
path: this.path,
lockfileVersion: this.options.lockfileVersion,
}).then(meta => Object.assign(root, {meta}))
: this.loadVirtual({ root }))

// if we don't have a lockfile to go from, then start with the
Expand Down Expand Up @@ -345,7 +347,10 @@ module.exports = cls => class IdealTreeBuilder extends cls {
// this is a gross kludge to handle the fact that we don't save
// metadata on the root node in global installs, because the "root"
// node is something like /usr/local/lib.
const meta = new Shrinkwrap({ path: this.path })
const meta = new Shrinkwrap({
path: this.path,
lockfileVersion: this.options.lockfileVersion,
})
meta.reset()
root.meta = meta
return root
Expand Down
2 changes: 2 additions & 0 deletions lib/arborist/load-actual.js
Expand Up @@ -145,13 +145,15 @@ module.exports = cls => class ActualLoader extends cls {
const meta = await Shrinkwrap.load({
path: this[_actualTree].path,
hiddenLockfile: true,
lockfileVersion: 3,
})
if (meta.loadedFromDisk) {
this[_actualTree].meta = meta
return this[_loadActualVirtually]({ root })
} else {
const meta = await Shrinkwrap.load({
path: this[_actualTree].path,
lockfileVersion: this.options.lockfileVersion,
})
this[_actualTree].meta = meta
return this[_loadActualActually]({ root, ignoreMissing })
Expand Down
5 changes: 4 additions & 1 deletion lib/arborist/load-virtual.js
Expand Up @@ -54,7 +54,10 @@ module.exports = cls => class VirtualLoader extends cls {
return treeCheck(this.virtualTree)
}

const s = await Shrinkwrap.load({ path: this.path })
const s = await Shrinkwrap.load({
path: this.path,
lockfileVersion: this.options.lockfileVersion,
})
if (!s.loadedFromDisk && !options.root) {
const er = new Error('loadVirtual requires existing shrinkwrap file')
throw Object.assign(er, { code: 'ENOLOCK' })
Expand Down
25 changes: 19 additions & 6 deletions lib/shrinkwrap.js
Expand Up @@ -10,7 +10,7 @@
// definitely not before npm v8.

const localeCompare = require('@isaacs/string-locale-compare')('en')
const lockfileVersion = 2
const defaultLockfileVersion = 2

// for comparing nodes to yarn.lock entries
const mismatch = (a, b) => a && b && a !== b
Expand Down Expand Up @@ -316,8 +316,10 @@ class Shrinkwrap {
shrinkwrapOnly = false,
hiddenLockfile = false,
log = procLog,
lockfileVersion = defaultLockfileVersion,
} = options

this.lockfileVersion = parseInt(hiddenLockfile ? 3 : lockfileVersion, 10)
this.log = log
this[_awaitingUpdate] = new Map()
this.tree = null
Expand Down Expand Up @@ -372,9 +374,9 @@ class Shrinkwrap {
reset () {
this.tree = null
this[_awaitingUpdate] = new Map()
this.originalLockfileVersion = lockfileVersion
this.originalLockfileVersion = this.lockfileVersion
this.data = {
lockfileVersion,
lockfileVersion: this.lockfileVersion,
requires: true,
packages: {},
dependencies: {},
Expand Down Expand Up @@ -462,11 +464,12 @@ class Shrinkwrap {
}).then(lock => {
this.data = {
...lock,
lockfileVersion,
lockfileVersion: this.lockfileVersion,
requires: true,
packages: lock.packages || {},
...(this.hiddenLockfile ? {} : {dependencies: lock.dependencies || {}}),
dependencies: lock.dependencies || {},
}

this.originalLockfileVersion = lock.lockfileVersion
this.ancientLockfile = this.loadedFromDisk &&
!(lock.lockfileVersion >= 2) && !lock.requires
Expand Down Expand Up @@ -886,7 +889,17 @@ class Shrinkwrap {
this[_buildLegacyLockfile](this.tree, this.data)
}

return this.data
const data = { ...this.data }
// lf version 1 = dependencies only
// lf version 2 = dependencies and packages
// lf version 3 = packages only
if (this.lockfileVersion >= 3) {
delete data.dependencies
} else if (this.lockfileVersion < 2) {
delete data.packages
}

return data
}

[_buildLegacyLockfile] (node, lock, path = []) {
Expand Down
3 changes: 2 additions & 1 deletion tap-snapshots/test/shrinkwrap.js.test.cjs
Expand Up @@ -692,7 +692,8 @@ Object {

exports[`test/shrinkwrap.js TAP load a hidden lockfile > must match snapshot 1`] = `
Object {
"lockfileVersion": 2,
"dependencies": Object {},
"lockfileVersion": 3,
"name": "hidden-lockfile",
"packages": Object {
"": Object {
Expand Down
24 changes: 20 additions & 4 deletions test/shrinkwrap.js
Expand Up @@ -38,6 +38,22 @@ t.test('path defaults to .', async t => {
t.equal(sw.path, process.cwd())
})

t.test('load and change lockfileVersion', async t => {
const vDefault = await Shrinkwrap.load({ path: fixture })
const v1 = await Shrinkwrap.load({ path: fixture, lockfileVersion: 1 })
const v2 = await Shrinkwrap.load({ path: fixture, lockfileVersion: 2 })
const v3 = await Shrinkwrap.load({ path: fixture, lockfileVersion: 3 })

t.strictSame(vDefault, v2, 'default is same as version 2')
const v1Data = await v1.commit()
const v2Data = await v2.commit()
const v3Data = await v3.commit()
t.strictSame(v2Data, { ...v1Data, ...v3Data, lockfileVersion: 2 },
'v2 is superset of v1 and v3')
t.equal(v1Data.packages, undefined, 'v1 data does not have packages')
t.equal(v3Data.dependencies, undefined, 'v3 data does not have dependencies')
})

t.test('load and then reset gets empty lockfile', t =>
Shrinkwrap.load({ path: fixture }).then(sw => {
sw.reset()
Expand Down Expand Up @@ -680,9 +696,9 @@ t.test('load a hidden lockfile', t => {
},
}))
t.strictSame(s.data.dependencies, {}, 'did not add to legacy data')
s.commit()
t.equal(s.data.packages[''], undefined, 'no root entry')
t.equal(s.data.dependencies, undefined, 'deleted legacy metadata')
const data = s.commit()
t.equal(data.packages[''], undefined, 'no root entry')
t.equal(data.dependencies, undefined, 'deleted legacy metadata')
})
})

Expand All @@ -691,7 +707,7 @@ t.test('load a fresh hidden lockfile', t => Shrinkwrap.reset({
hiddenLockfile: true,
}).then(sw => {
t.strictSame(sw.data, {
lockfileVersion: 2,
lockfileVersion: 3,
requires: true,
dependencies: {},
packages: {},
Expand Down

0 comments on commit d47623c

Please sign in to comment.