@npmcli/arborist 9.0.0 → 9.0.2
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/arborist/build-ideal-tree.js +51 -13
- package/lib/arborist/isolated-reifier.js +3 -3
- package/lib/arborist/load-actual.js +1 -1
- package/lib/arborist/load-virtual.js +2 -1
- package/lib/arborist/reify.js +40 -16
- package/lib/consistent-resolve.js +2 -3
- package/lib/dep-valid.js +1 -1
- package/lib/edge.js +82 -25
- package/lib/link.js +1 -1
- package/lib/node.js +134 -17
- package/lib/override-set.js +63 -1
- package/lib/place-dep.js +1 -1
- package/lib/shrinkwrap.js +19 -4
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@ const { lstat, readlink } = require('node:fs/promises')
|
|
|
13
13
|
const { depth } = require('treeverse')
|
|
14
14
|
const { log, time } = require('proc-log')
|
|
15
15
|
const { redact } = require('@npmcli/redact')
|
|
16
|
+
const semver = require('semver')
|
|
16
17
|
|
|
17
18
|
const {
|
|
18
19
|
OK,
|
|
@@ -279,14 +280,23 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
279
280
|
// When updating all, we load the shrinkwrap, but don't bother
|
|
280
281
|
// to build out the full virtual tree from it, since we'll be
|
|
281
282
|
// reconstructing it anyway.
|
|
282
|
-
.then(root =>
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
283
|
+
.then(root => {
|
|
284
|
+
if (this.options.global) {
|
|
285
|
+
return root
|
|
286
|
+
} else if (!this[_usePackageLock] || this[_updateAll]) {
|
|
287
|
+
return Shrinkwrap.reset({
|
|
288
|
+
path: this.path,
|
|
289
|
+
lockfileVersion: this.options.lockfileVersion,
|
|
290
|
+
resolveOptions: this.options,
|
|
291
|
+
}).then(meta => Object.assign(root, { meta }))
|
|
292
|
+
} else {
|
|
293
|
+
return this.loadVirtual({ root })
|
|
294
|
+
.then(tree => {
|
|
295
|
+
this.#applyRootOverridesToWorkspaces(tree)
|
|
296
|
+
return tree
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
})
|
|
290
300
|
|
|
291
301
|
// if we don't have a lockfile to go from, then start with the
|
|
292
302
|
// actual tree, so we only make the minimum required changes.
|
|
@@ -447,7 +457,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
447
457
|
.catch(/* istanbul ignore next */ () => null)
|
|
448
458
|
if (st && st.isSymbolicLink()) {
|
|
449
459
|
const target = await readlink(dir)
|
|
450
|
-
const real = resolve(dirname(dir), target)
|
|
460
|
+
const real = resolve(dirname(dir), target)
|
|
451
461
|
tree.package.dependencies[name] = `file:${real}`
|
|
452
462
|
} else {
|
|
453
463
|
tree.package.dependencies[name] = '*'
|
|
@@ -522,12 +532,12 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
522
532
|
|
|
523
533
|
const { name } = spec
|
|
524
534
|
if (spec.type === 'file') {
|
|
525
|
-
spec = npa(`file:${relpath(path, spec.fetchSpec)
|
|
535
|
+
spec = npa(`file:${relpath(path, spec.fetchSpec)}`, path)
|
|
526
536
|
spec.name = name
|
|
527
537
|
} else if (spec.type === 'directory') {
|
|
528
538
|
try {
|
|
529
539
|
const real = await realpath(spec.fetchSpec, this[_rpcache], this[_stcache])
|
|
530
|
-
spec = npa(`file:${relpath(path, real)
|
|
540
|
+
spec = npa(`file:${relpath(path, real)}`, path)
|
|
531
541
|
spec.name = name
|
|
532
542
|
} catch {
|
|
533
543
|
// TODO: create synthetic test case to simulate realpath failure
|
|
@@ -799,7 +809,8 @@ This is a one-time fix-up, please be patient...
|
|
|
799
809
|
const crackOpen = this.#complete &&
|
|
800
810
|
node !== this.idealTree &&
|
|
801
811
|
node.resolved &&
|
|
802
|
-
(hasBundle || hasShrinkwrap)
|
|
812
|
+
(hasBundle || hasShrinkwrap) &&
|
|
813
|
+
!node.ideallyInert
|
|
803
814
|
if (crackOpen) {
|
|
804
815
|
const Arborist = this.constructor
|
|
805
816
|
const opt = { ...this.options }
|
|
@@ -1475,6 +1486,32 @@ This is a one-time fix-up, please be patient...
|
|
|
1475
1486
|
timeEnd()
|
|
1476
1487
|
}
|
|
1477
1488
|
|
|
1489
|
+
#applyRootOverridesToWorkspaces (tree) {
|
|
1490
|
+
const rootOverrides = tree.root.package.overrides || {}
|
|
1491
|
+
|
|
1492
|
+
for (const node of tree.root.inventory.values()) {
|
|
1493
|
+
if (!node.isWorkspace) {
|
|
1494
|
+
continue
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
for (const depName of Object.keys(rootOverrides)) {
|
|
1498
|
+
const edge = node.edgesOut.get(depName)
|
|
1499
|
+
const rootNode = tree.root.children.get(depName)
|
|
1500
|
+
|
|
1501
|
+
// safely skip if either edge or rootNode doesn't exist yet
|
|
1502
|
+
if (!edge || !rootNode) {
|
|
1503
|
+
continue
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
const resolvedRootVersion = rootNode.package.version
|
|
1507
|
+
if (!semver.satisfies(resolvedRootVersion, edge.spec)) {
|
|
1508
|
+
edge.detach()
|
|
1509
|
+
node.children.delete(depName)
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1478
1515
|
#idealTreePrune () {
|
|
1479
1516
|
for (const node of this.idealTree.inventory.values()) {
|
|
1480
1517
|
if (node.extraneous) {
|
|
@@ -1491,7 +1528,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1491
1528
|
|
|
1492
1529
|
const set = optionalSet(node)
|
|
1493
1530
|
for (const node of set) {
|
|
1494
|
-
node.
|
|
1531
|
+
node.ideallyInert = true
|
|
1495
1532
|
}
|
|
1496
1533
|
}
|
|
1497
1534
|
}
|
|
@@ -1512,6 +1549,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1512
1549
|
node.parent !== null
|
|
1513
1550
|
&& !node.isProjectRoot
|
|
1514
1551
|
&& !excludeNodes.has(node)
|
|
1552
|
+
&& !node.ideallyInert
|
|
1515
1553
|
) {
|
|
1516
1554
|
this[_addNodeToTrashList](node)
|
|
1517
1555
|
}
|
|
@@ -81,7 +81,7 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
81
81
|
}
|
|
82
82
|
queue.push(e.to)
|
|
83
83
|
})
|
|
84
|
-
if (!next.isProjectRoot && !next.isWorkspace) {
|
|
84
|
+
if (!next.isProjectRoot && !next.isWorkspace && !next.ideallyInert) {
|
|
85
85
|
root.external.push(await this.externalProxyMemo(next))
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -147,8 +147,8 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
147
147
|
const nonOptionalDeps = edges.filter(e => !e.optional).map(e => e.to.target)
|
|
148
148
|
|
|
149
149
|
result.localDependencies = await Promise.all(nonOptionalDeps.filter(n => n.isWorkspace).map(this.workspaceProxyMemo))
|
|
150
|
-
result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !n.isWorkspace).map(this.externalProxyMemo))
|
|
151
|
-
result.externalOptionalDependencies = await Promise.all(optionalDeps.map(this.externalProxyMemo))
|
|
150
|
+
result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !n.isWorkspace && !n.ideallyInert).map(this.externalProxyMemo))
|
|
151
|
+
result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.ideallyInert).map(this.externalProxyMemo))
|
|
152
152
|
result.dependencies = [
|
|
153
153
|
...result.externalDependencies,
|
|
154
154
|
...result.localDependencies,
|
|
@@ -216,7 +216,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
216
216
|
const actualRoot = tree.isLink ? tree.target : tree
|
|
217
217
|
const { dependencies = {} } = actualRoot.package
|
|
218
218
|
for (const [name, kid] of actualRoot.children.entries()) {
|
|
219
|
-
const def = kid.isLink ? `file:${kid.realpath
|
|
219
|
+
const def = kid.isLink ? `file:${kid.realpath}` : '*'
|
|
220
220
|
dependencies[name] = dependencies[name] || def
|
|
221
221
|
}
|
|
222
222
|
actualRoot.package = { ...actualRoot.package, dependencies }
|
|
@@ -149,7 +149,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
149
149
|
})
|
|
150
150
|
|
|
151
151
|
for (const [name, path] of workspaces.entries()) {
|
|
152
|
-
lockWS[name] = `file:${path
|
|
152
|
+
lockWS[name] = `file:${path}`
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
// Should rootNames exclude optional?
|
|
@@ -267,6 +267,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
267
267
|
integrity: sw.integrity,
|
|
268
268
|
resolved: consistentResolve(sw.resolved, this.path, path),
|
|
269
269
|
pkg: sw,
|
|
270
|
+
ideallyInert: sw.ideallyInert,
|
|
270
271
|
hasShrinkwrap: sw.hasShrinkwrap,
|
|
271
272
|
dev,
|
|
272
273
|
optional,
|
package/lib/arborist/reify.js
CHANGED
|
@@ -8,7 +8,6 @@ const semver = require('semver')
|
|
|
8
8
|
const debug = require('../debug.js')
|
|
9
9
|
const { walkUp } = require('walk-up-path')
|
|
10
10
|
const { log, time } = require('proc-log')
|
|
11
|
-
const hgi = require('hosted-git-info')
|
|
12
11
|
const rpj = require('read-package-json-fast')
|
|
13
12
|
|
|
14
13
|
const { dirname, resolve, relative, join } = require('node:path')
|
|
@@ -150,7 +149,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
150
149
|
for (const path of this[_trashList]) {
|
|
151
150
|
const loc = relpath(this.idealTree.realpath, path)
|
|
152
151
|
const node = this.idealTree.inventory.get(loc)
|
|
153
|
-
if (node && node.root === this.idealTree) {
|
|
152
|
+
if (node && node.root === this.idealTree && !node.ideallyInert) {
|
|
154
153
|
node.parent = null
|
|
155
154
|
}
|
|
156
155
|
}
|
|
@@ -238,6 +237,18 @@ module.exports = cls => class Reifier extends cls {
|
|
|
238
237
|
this.idealTree.meta.hiddenLockfile = true
|
|
239
238
|
this.idealTree.meta.lockfileVersion = defaultLockfileVersion
|
|
240
239
|
|
|
240
|
+
// Preserve inertness for failed stuff.
|
|
241
|
+
if (this.actualTree) {
|
|
242
|
+
for (const [loc, actual] of this.actualTree.inventory.entries()) {
|
|
243
|
+
if (actual.ideallyInert) {
|
|
244
|
+
const ideal = this.idealTree.inventory.get(loc)
|
|
245
|
+
if (ideal) {
|
|
246
|
+
ideal.ideallyInert = true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
241
252
|
this.actualTree = this.idealTree
|
|
242
253
|
this.idealTree = null
|
|
243
254
|
|
|
@@ -600,6 +611,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
600
611
|
// retire the same path at the same time.
|
|
601
612
|
const dirsChecked = new Set()
|
|
602
613
|
return promiseAllRejectLate(leaves.map(async node => {
|
|
614
|
+
if (node.ideallyInert) {
|
|
615
|
+
return
|
|
616
|
+
}
|
|
603
617
|
for (const d of walkUp(node.path)) {
|
|
604
618
|
if (d === node.top.path) {
|
|
605
619
|
break
|
|
@@ -744,6 +758,10 @@ module.exports = cls => class Reifier extends cls {
|
|
|
744
758
|
}
|
|
745
759
|
|
|
746
760
|
async #extractOrLink (node) {
|
|
761
|
+
if (node.ideallyInert) {
|
|
762
|
+
return
|
|
763
|
+
}
|
|
764
|
+
|
|
747
765
|
const nm = resolve(node.parent.path, 'node_modules')
|
|
748
766
|
await this.#validateNodeModules(nm)
|
|
749
767
|
|
|
@@ -819,6 +837,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
819
837
|
const set = optionalSet(node)
|
|
820
838
|
for (node of set) {
|
|
821
839
|
log.verbose('reify', 'failed optional dependency', node.path)
|
|
840
|
+
node.ideallyInert = true
|
|
822
841
|
this[_addNodeToTrashList](node)
|
|
823
842
|
}
|
|
824
843
|
}) : p).then(() => node)
|
|
@@ -833,21 +852,23 @@ module.exports = cls => class Reifier extends cls {
|
|
|
833
852
|
// ${REGISTRY} or something. This has to be threaded through the
|
|
834
853
|
// Shrinkwrap and Node classes carefully, so for now, just treat
|
|
835
854
|
// the default reg as the magical animal that it has been.
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
855
|
+
try {
|
|
856
|
+
const resolvedURL = new URL(resolved)
|
|
857
|
+
|
|
858
|
+
if ((this.options.replaceRegistryHost === resolvedURL.hostname) ||
|
|
859
|
+
this.options.replaceRegistryHost === 'always') {
|
|
860
|
+
const registryURL = new URL(this.registry)
|
|
861
|
+
// Replace the host with the registry host while keeping the path intact
|
|
862
|
+
resolvedURL.hostname = registryURL.hostname
|
|
863
|
+
resolvedURL.port = registryURL.port
|
|
864
|
+
return resolvedURL.toString()
|
|
865
|
+
}
|
|
866
|
+
return resolved
|
|
867
|
+
} catch (e) {
|
|
839
868
|
// if we could not parse the url at all then returning nothing
|
|
840
869
|
// here means it will get removed from the tree in the next step
|
|
841
|
-
return
|
|
870
|
+
return undefined
|
|
842
871
|
}
|
|
843
|
-
|
|
844
|
-
if ((this.options.replaceRegistryHost === resolvedURL.hostname)
|
|
845
|
-
|| this.options.replaceRegistryHost === 'always') {
|
|
846
|
-
// this.registry always has a trailing slash
|
|
847
|
-
return `${this.registry.slice(0, -1)}${resolvedURL.pathname}${resolvedURL.searchParams}`
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
return resolved
|
|
851
872
|
}
|
|
852
873
|
|
|
853
874
|
// bundles are *sort of* like shrinkwraps, in that the branch is defined
|
|
@@ -1151,6 +1172,9 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1151
1172
|
|
|
1152
1173
|
this.#retiredUnchanged[retireFolder] = []
|
|
1153
1174
|
return promiseAllRejectLate(diff.unchanged.map(node => {
|
|
1175
|
+
if (node.ideallyInert) {
|
|
1176
|
+
return
|
|
1177
|
+
}
|
|
1154
1178
|
// no need to roll back links, since we'll just delete them anyway
|
|
1155
1179
|
if (node.isLink) {
|
|
1156
1180
|
return mkdir(dirname(node.path), { recursive: true, force: true })
|
|
@@ -1230,7 +1254,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1230
1254
|
// skip links that only live within node_modules as they are most
|
|
1231
1255
|
// likely managed by packages we installed, we only want to rebuild
|
|
1232
1256
|
// unchanged links we directly manage
|
|
1233
|
-
const linkedFromRoot = node.parent === tree || node.target.fsTop === tree
|
|
1257
|
+
const linkedFromRoot = (node.parent === tree && !node.ideallyInert) || node.target.fsTop === tree
|
|
1234
1258
|
if (node.isLink && linkedFromRoot) {
|
|
1235
1259
|
nodes.push(node)
|
|
1236
1260
|
}
|
|
@@ -1364,7 +1388,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1364
1388
|
// path initially, in which case we can end up with the wrong
|
|
1365
1389
|
// thing, so just get the ultimate fetchSpec and relativize it.
|
|
1366
1390
|
const p = req.fetchSpec.replace(/^file:/, '')
|
|
1367
|
-
const rel = relpath(addTree.realpath, p)
|
|
1391
|
+
const rel = relpath(addTree.realpath, p)
|
|
1368
1392
|
newSpec = `file:${rel}`
|
|
1369
1393
|
}
|
|
1370
1394
|
} else {
|
|
@@ -20,11 +20,10 @@ const consistentResolve = (resolved, fromPath, toPath, relPaths = false) => {
|
|
|
20
20
|
raw,
|
|
21
21
|
} = npa(resolved, fromPath)
|
|
22
22
|
if (type === 'file' || type === 'directory') {
|
|
23
|
-
const cleanFetchSpec = fetchSpec.replace(/#/g, '%23')
|
|
24
23
|
if (relPaths && toPath) {
|
|
25
|
-
return `file:${relpath(toPath,
|
|
24
|
+
return `file:${relpath(toPath, fetchSpec)}`
|
|
26
25
|
}
|
|
27
|
-
return `file:${
|
|
26
|
+
return `file:${fetchSpec}`
|
|
28
27
|
}
|
|
29
28
|
if (hosted) {
|
|
30
29
|
return `git+${hosted.auth ? hosted.https(hostedOpt) : hosted.sshurl(hostedOpt)}`
|
package/lib/dep-valid.js
CHANGED
package/lib/edge.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const util = require('node:util')
|
|
5
5
|
const npa = require('npm-package-arg')
|
|
6
6
|
const depValid = require('./dep-valid.js')
|
|
7
|
+
const OverrideSet = require('./override-set.js')
|
|
7
8
|
|
|
8
9
|
class ArboristEdge {
|
|
9
10
|
constructor (edge) {
|
|
@@ -103,7 +104,7 @@ class Edge {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
satisfiedBy (node) {
|
|
106
|
-
if (node.name !== this.#name) {
|
|
107
|
+
if (node.name !== this.#name || !this.#from) {
|
|
107
108
|
return false
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -112,7 +113,31 @@ class Edge {
|
|
|
112
113
|
if (node.hasShrinkwrap || node.inShrinkwrap || node.inBundle) {
|
|
113
114
|
return depValid(node, this.rawSpec, this.#accept, this.#from)
|
|
114
115
|
}
|
|
115
|
-
|
|
116
|
+
|
|
117
|
+
// If there's no override we just use the spec.
|
|
118
|
+
if (!this.overrides?.keySpec) {
|
|
119
|
+
return depValid(node, this.spec, this.#accept, this.#from)
|
|
120
|
+
}
|
|
121
|
+
// There's some override. If the target node satisfies the overriding spec
|
|
122
|
+
// then it's okay.
|
|
123
|
+
if (depValid(node, this.spec, this.#accept, this.#from)) {
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
// If it doesn't, then it should at least satisfy the original spec.
|
|
127
|
+
if (!depValid(node, this.rawSpec, this.#accept, this.#from)) {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
// It satisfies the original spec, not the overriding spec. We need to make
|
|
131
|
+
// sure it doesn't use the overridden spec.
|
|
132
|
+
// For example:
|
|
133
|
+
// we might have an ^8.0.0 rawSpec, and an override that makes
|
|
134
|
+
// keySpec=8.23.0 and the override value spec=9.0.0.
|
|
135
|
+
// If the node is 9.0.0, then it's okay because it's consistent with spec.
|
|
136
|
+
// If the node is 8.24.0, then it's okay because it's consistent with the rawSpec.
|
|
137
|
+
// If the node is 8.23.0, then it's not okay because even though it's consistent
|
|
138
|
+
// with the rawSpec, it's also consistent with the keySpec.
|
|
139
|
+
// So we're looking for ^8.0.0 or 9.0.0 and not 8.23.0.
|
|
140
|
+
return !depValid(node, this.overrides.keySpec, this.#accept, this.#from)
|
|
116
141
|
}
|
|
117
142
|
|
|
118
143
|
// return the edge data, and an explanation of how that edge came to be here
|
|
@@ -181,24 +206,21 @@ class Edge {
|
|
|
181
206
|
if (this.overrides?.value && this.overrides.value !== '*' && this.overrides.name === this.#name) {
|
|
182
207
|
if (this.overrides.value.startsWith('$')) {
|
|
183
208
|
const ref = this.overrides.value.slice(1)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
if (pkg.dependencies?.[ref]) {
|
|
196
|
-
return pkg.dependencies[ref]
|
|
197
|
-
}
|
|
198
|
-
if (pkg.peerDependencies?.[ref]) {
|
|
199
|
-
return pkg.peerDependencies[ref]
|
|
209
|
+
let pkg = this.#from?.sourceReference
|
|
210
|
+
? this.#from?.sourceReference.root.package
|
|
211
|
+
: this.#from?.root?.package
|
|
212
|
+
|
|
213
|
+
let specValue = this.#calculateReferentialOverrideSpec(ref, pkg)
|
|
214
|
+
|
|
215
|
+
// If the package isn't found in the root package, fall back to the local package.
|
|
216
|
+
if (!specValue) {
|
|
217
|
+
pkg = this.#from?.package
|
|
218
|
+
specValue = this.#calculateReferentialOverrideSpec(ref, pkg)
|
|
200
219
|
}
|
|
201
220
|
|
|
221
|
+
if (specValue) {
|
|
222
|
+
return specValue
|
|
223
|
+
}
|
|
202
224
|
throw new Error(`Unable to resolve reference ${this.overrides.value}`)
|
|
203
225
|
}
|
|
204
226
|
return this.overrides.value
|
|
@@ -206,6 +228,21 @@ class Edge {
|
|
|
206
228
|
return this.#spec
|
|
207
229
|
}
|
|
208
230
|
|
|
231
|
+
#calculateReferentialOverrideSpec (ref, pkg) {
|
|
232
|
+
if (pkg.devDependencies?.[ref]) {
|
|
233
|
+
return pkg.devDependencies[ref]
|
|
234
|
+
}
|
|
235
|
+
if (pkg.optionalDependencies?.[ref]) {
|
|
236
|
+
return pkg.optionalDependencies[ref]
|
|
237
|
+
}
|
|
238
|
+
if (pkg.dependencies?.[ref]) {
|
|
239
|
+
return pkg.dependencies[ref]
|
|
240
|
+
}
|
|
241
|
+
if (pkg.peerDependencies?.[ref]) {
|
|
242
|
+
return pkg.peerDependencies[ref]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
209
246
|
get accept () {
|
|
210
247
|
return this.#accept
|
|
211
248
|
}
|
|
@@ -234,10 +271,15 @@ class Edge {
|
|
|
234
271
|
} else {
|
|
235
272
|
this.#error = 'MISSING'
|
|
236
273
|
}
|
|
237
|
-
} else if (this.peer && this.#from === this.#to.parent && !this.#from
|
|
274
|
+
} else if (this.peer && this.#from === this.#to.parent && !this.#from?.isTop) {
|
|
238
275
|
this.#error = 'PEER LOCAL'
|
|
239
276
|
} else if (!this.satisfiedBy(this.#to)) {
|
|
240
277
|
this.#error = 'INVALID'
|
|
278
|
+
} else if (this.overrides && this.#to.edgesOut.size && OverrideSet.doOverrideSetsConflict(this.overrides, this.#to.overrides)) {
|
|
279
|
+
// Any inconsistency between the edge's override set and the target's override set is potentially problematic.
|
|
280
|
+
// But we only say the edge is in error if the override sets are plainly conflicting.
|
|
281
|
+
// Note that if the target doesn't have any dependencies of their own, then this inconsistency is irrelevant.
|
|
282
|
+
this.#error = 'INVALID'
|
|
241
283
|
} else {
|
|
242
284
|
this.#error = 'OK'
|
|
243
285
|
}
|
|
@@ -250,15 +292,26 @@ class Edge {
|
|
|
250
292
|
|
|
251
293
|
reload (hard = false) {
|
|
252
294
|
this.#explanation = null
|
|
253
|
-
|
|
254
|
-
|
|
295
|
+
|
|
296
|
+
let needToUpdateOverrideSet = false
|
|
297
|
+
let newOverrideSet
|
|
298
|
+
let oldOverrideSet
|
|
299
|
+
if (this.#from?.overrides) {
|
|
300
|
+
newOverrideSet = this.#from.overrides.getEdgeRule(this)
|
|
301
|
+
if (newOverrideSet && !newOverrideSet.isEqual(this.overrides)) {
|
|
302
|
+
// If there's a new different override set we need to propagate it to the nodes.
|
|
303
|
+
// If we're deleting the override set then there's no point propagating it right now since it will be filled with another value later.
|
|
304
|
+
needToUpdateOverrideSet = true
|
|
305
|
+
oldOverrideSet = this.overrides
|
|
306
|
+
this.overrides = newOverrideSet
|
|
307
|
+
}
|
|
255
308
|
} else {
|
|
256
309
|
delete this.overrides
|
|
257
310
|
}
|
|
258
|
-
const newTo = this.#from
|
|
311
|
+
const newTo = this.#from?.resolve(this.#name)
|
|
259
312
|
if (newTo !== this.#to) {
|
|
260
313
|
if (this.#to) {
|
|
261
|
-
this.#to.
|
|
314
|
+
this.#to.deleteEdgeIn(this)
|
|
262
315
|
}
|
|
263
316
|
this.#to = newTo
|
|
264
317
|
this.#error = null
|
|
@@ -267,15 +320,19 @@ class Edge {
|
|
|
267
320
|
}
|
|
268
321
|
} else if (hard) {
|
|
269
322
|
this.#error = null
|
|
323
|
+
} else if (needToUpdateOverrideSet && this.#to) {
|
|
324
|
+
// Propagate the new override set to the target node.
|
|
325
|
+
this.#to.updateOverridesEdgeInRemoved(oldOverrideSet)
|
|
326
|
+
this.#to.updateOverridesEdgeInAdded(newOverrideSet)
|
|
270
327
|
}
|
|
271
328
|
}
|
|
272
329
|
|
|
273
330
|
detach () {
|
|
274
331
|
this.#explanation = null
|
|
275
332
|
if (this.#to) {
|
|
276
|
-
this.#to.
|
|
333
|
+
this.#to.deleteEdgeIn(this)
|
|
277
334
|
}
|
|
278
|
-
this.#from
|
|
335
|
+
this.#from?.edgesOut.delete(this.#name)
|
|
279
336
|
this.#to = null
|
|
280
337
|
this.#error = 'DETACHED'
|
|
281
338
|
this.#from = null
|
package/lib/link.js
CHANGED
|
@@ -99,7 +99,7 @@ class Link extends Node {
|
|
|
99
99
|
// the path/realpath guard is there for the benefit of setting
|
|
100
100
|
// these things in the "wrong" order
|
|
101
101
|
return this.path && this.realpath
|
|
102
|
-
? `file:${relpath(dirname(this.path), this.realpath)
|
|
102
|
+
? `file:${relpath(dirname(this.path), this.realpath)}`
|
|
103
103
|
: null
|
|
104
104
|
}
|
|
105
105
|
|
package/lib/node.js
CHANGED
|
@@ -40,6 +40,7 @@ const debug = require('./debug.js')
|
|
|
40
40
|
const gatherDepSet = require('./gather-dep-set.js')
|
|
41
41
|
const treeCheck = require('./tree-check.js')
|
|
42
42
|
const { walkUp } = require('walk-up-path')
|
|
43
|
+
const { log } = require('proc-log')
|
|
43
44
|
|
|
44
45
|
const { resolve, relative, dirname, basename } = require('node:path')
|
|
45
46
|
const util = require('node:util')
|
|
@@ -102,6 +103,7 @@ class Node {
|
|
|
102
103
|
global = false,
|
|
103
104
|
dummy = false,
|
|
104
105
|
sourceReference = null,
|
|
106
|
+
ideallyInert = false,
|
|
105
107
|
} = options
|
|
106
108
|
// this object gives querySelectorAll somewhere to stash context about a node
|
|
107
109
|
// while processing a query
|
|
@@ -196,6 +198,8 @@ class Node {
|
|
|
196
198
|
this.extraneous = false
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
this.ideallyInert = ideallyInert
|
|
202
|
+
|
|
199
203
|
this.edgesIn = new Set()
|
|
200
204
|
this.edgesOut = new CaseInsensitiveMap()
|
|
201
205
|
|
|
@@ -344,7 +348,28 @@ class Node {
|
|
|
344
348
|
}
|
|
345
349
|
|
|
346
350
|
get overridden () {
|
|
347
|
-
|
|
351
|
+
if (!this.overrides) {
|
|
352
|
+
return false
|
|
353
|
+
}
|
|
354
|
+
if (!this.overrides.value) {
|
|
355
|
+
return false
|
|
356
|
+
}
|
|
357
|
+
if (this.overrides.name !== this.name) {
|
|
358
|
+
return false
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// The overrides rule is for a package with this name, but some override rules only apply to specific
|
|
362
|
+
// versions. To make sure this package was actually overridden, we check whether any edge going in
|
|
363
|
+
// had the rule applied to it, in which case its overrides set is different than its source node.
|
|
364
|
+
for (const edge of this.edgesIn) {
|
|
365
|
+
if (edge.overrides && edge.overrides.name === this.name && edge.overrides.value === this.version) {
|
|
366
|
+
if (!edge.overrides.isEqual(edge.from.overrides)) {
|
|
367
|
+
return true
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return false
|
|
348
373
|
}
|
|
349
374
|
|
|
350
375
|
get package () {
|
|
@@ -822,9 +847,6 @@ class Node {
|
|
|
822
847
|
target.root = root
|
|
823
848
|
}
|
|
824
849
|
|
|
825
|
-
if (!this.overrides && this.parent && this.parent.overrides) {
|
|
826
|
-
this.overrides = this.parent.overrides.getNodeRule(this)
|
|
827
|
-
}
|
|
828
850
|
// tree should always be valid upon root setter completion.
|
|
829
851
|
treeCheck(this)
|
|
830
852
|
if (this !== root) {
|
|
@@ -842,7 +864,7 @@ class Node {
|
|
|
842
864
|
}
|
|
843
865
|
|
|
844
866
|
for (const [name, path] of this.#workspaces.entries()) {
|
|
845
|
-
new Edge({ from: this, name, spec: `file:${path
|
|
867
|
+
new Edge({ from: this, name, spec: `file:${path}`, type: 'workspace' })
|
|
846
868
|
}
|
|
847
869
|
}
|
|
848
870
|
|
|
@@ -1006,10 +1028,21 @@ class Node {
|
|
|
1006
1028
|
return false
|
|
1007
1029
|
}
|
|
1008
1030
|
|
|
1009
|
-
//
|
|
1010
|
-
|
|
1011
|
-
|
|
1031
|
+
// If this node has no dependencies, then it's irrelevant to check the override
|
|
1032
|
+
// rules of the replacement node.
|
|
1033
|
+
if (this.edgesOut.size) {
|
|
1034
|
+
// XXX need to check for two root nodes?
|
|
1035
|
+
if (node.overrides) {
|
|
1036
|
+
if (!node.overrides.isEqual(this.overrides)) {
|
|
1037
|
+
return false
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
if (this.overrides) {
|
|
1041
|
+
return false
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1012
1044
|
}
|
|
1045
|
+
|
|
1013
1046
|
ignorePeers = new Set(ignorePeers)
|
|
1014
1047
|
|
|
1015
1048
|
// gather up all the deps of this node and that are only depended
|
|
@@ -1044,7 +1077,7 @@ class Node {
|
|
|
1044
1077
|
// return true if it's safe to remove this node, because anything that
|
|
1045
1078
|
// is depending on it would be fine with the thing that they would resolve
|
|
1046
1079
|
// to if it was removed, or nothing is depending on it in the first place.
|
|
1047
|
-
canDedupe (preferDedupe = false) {
|
|
1080
|
+
canDedupe (preferDedupe = false, explicitRequest = false) {
|
|
1048
1081
|
// not allowed to mess with shrinkwraps or bundles
|
|
1049
1082
|
if (this.inDepBundle || this.inShrinkwrap) {
|
|
1050
1083
|
return false
|
|
@@ -1077,8 +1110,18 @@ class Node {
|
|
|
1077
1110
|
return false
|
|
1078
1111
|
}
|
|
1079
1112
|
|
|
1080
|
-
// if we prefer dedupe, or if the version is
|
|
1081
|
-
if (preferDedupe || semver.
|
|
1113
|
+
// if we prefer dedupe, or if the version is equal, take the other
|
|
1114
|
+
if (preferDedupe || semver.eq(other.version, this.version)) {
|
|
1115
|
+
return true
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// if our current version isn't the result of an override, then prefer to take the greater version
|
|
1119
|
+
if (!this.overridden && semver.gt(other.version, this.version)) {
|
|
1120
|
+
return true
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// if the other version was an explicit request, then prefer to take the other version
|
|
1124
|
+
if (explicitRequest) {
|
|
1082
1125
|
return true
|
|
1083
1126
|
}
|
|
1084
1127
|
|
|
@@ -1249,10 +1292,6 @@ class Node {
|
|
|
1249
1292
|
this[_changePath](newPath)
|
|
1250
1293
|
}
|
|
1251
1294
|
|
|
1252
|
-
if (parent.overrides) {
|
|
1253
|
-
this.overrides = parent.overrides.getNodeRule(this)
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
1295
|
// clobbers anything at that path, resets all appropriate references
|
|
1257
1296
|
this.root = parent.root
|
|
1258
1297
|
}
|
|
@@ -1346,9 +1385,87 @@ class Node {
|
|
|
1346
1385
|
this.edgesOut.set(edge.name, edge)
|
|
1347
1386
|
}
|
|
1348
1387
|
|
|
1349
|
-
|
|
1388
|
+
recalculateOutEdgesOverrides () {
|
|
1389
|
+
// For each edge out propogate the new overrides through.
|
|
1390
|
+
for (const edge of this.edgesOut.values()) {
|
|
1391
|
+
edge.reload(true)
|
|
1392
|
+
if (edge.to) {
|
|
1393
|
+
edge.to.updateOverridesEdgeInAdded(edge.overrides)
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
updateOverridesEdgeInRemoved (otherOverrideSet) {
|
|
1399
|
+
// If this edge's overrides isn't equal to this node's overrides, then removing it won't change newOverrideSet later.
|
|
1400
|
+
if (!this.overrides || !this.overrides.isEqual(otherOverrideSet)) {
|
|
1401
|
+
return false
|
|
1402
|
+
}
|
|
1403
|
+
let newOverrideSet
|
|
1404
|
+
for (const edge of this.edgesIn) {
|
|
1405
|
+
if (newOverrideSet && edge.overrides) {
|
|
1406
|
+
newOverrideSet = OverrideSet.findSpecificOverrideSet(edge.overrides, newOverrideSet)
|
|
1407
|
+
} else {
|
|
1408
|
+
newOverrideSet = edge.overrides
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
if (this.overrides.isEqual(newOverrideSet)) {
|
|
1412
|
+
return false
|
|
1413
|
+
}
|
|
1414
|
+
this.overrides = newOverrideSet
|
|
1415
|
+
if (this.overrides) {
|
|
1416
|
+
// Optimization: if there's any override set at all, then no non-extraneous node has an empty override set. So if we temporarily have no
|
|
1417
|
+
// override set (for example, we removed all the edges in), there's no use updating all the edges out right now. Let's just wait until
|
|
1418
|
+
// we have an actual override set later.
|
|
1419
|
+
this.recalculateOutEdgesOverrides()
|
|
1420
|
+
}
|
|
1421
|
+
return true
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// This logic isn't perfect either. When we have two edges in that have different override sets, then we have to decide which set is correct.
|
|
1425
|
+
// This function assumes the more specific override set is applicable, so if we have dependencies A->B->C and A->C
|
|
1426
|
+
// and an override set that specifies what happens for C under A->B, this will work even if the new A->C edge comes along and tries to change
|
|
1427
|
+
// the override set.
|
|
1428
|
+
// The strictly correct logic is not to allow two edges with different overrides to point to the same node, because even if this node can satisfy
|
|
1429
|
+
// both, one of its dependencies might need to be different depending on the edge leading to it.
|
|
1430
|
+
// However, this might cause a lot of duplication, because the conflict in the dependencies might never actually happen.
|
|
1431
|
+
updateOverridesEdgeInAdded (otherOverrideSet) {
|
|
1432
|
+
if (!otherOverrideSet) {
|
|
1433
|
+
// Assuming there are any overrides at all, the overrides field is never undefined for any node at the end state of the tree.
|
|
1434
|
+
// So if the new edge's overrides is undefined it will be updated later. So we can wait with updating the node's overrides field.
|
|
1435
|
+
return false
|
|
1436
|
+
}
|
|
1437
|
+
if (!this.overrides) {
|
|
1438
|
+
this.overrides = otherOverrideSet
|
|
1439
|
+
this.recalculateOutEdgesOverrides()
|
|
1440
|
+
return true
|
|
1441
|
+
}
|
|
1442
|
+
if (this.overrides.isEqual(otherOverrideSet)) {
|
|
1443
|
+
return false
|
|
1444
|
+
}
|
|
1445
|
+
const newOverrideSet = OverrideSet.findSpecificOverrideSet(this.overrides, otherOverrideSet)
|
|
1446
|
+
if (newOverrideSet) {
|
|
1447
|
+
if (!this.overrides.isEqual(newOverrideSet)) {
|
|
1448
|
+
this.overrides = newOverrideSet
|
|
1449
|
+
this.recalculateOutEdgesOverrides()
|
|
1450
|
+
return true
|
|
1451
|
+
}
|
|
1452
|
+
return false
|
|
1453
|
+
}
|
|
1454
|
+
// This is an error condition. We can only get here if the new override set is in conflict with the existing.
|
|
1455
|
+
log.silly('Conflicting override sets', this.name)
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
deleteEdgeIn (edge) {
|
|
1459
|
+
this.edgesIn.delete(edge)
|
|
1350
1460
|
if (edge.overrides) {
|
|
1351
|
-
this.
|
|
1461
|
+
this.updateOverridesEdgeInRemoved(edge.overrides)
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
addEdgeIn (edge) {
|
|
1466
|
+
// We need to handle the case where the new edge in has an overrides field which is different from the current value.
|
|
1467
|
+
if (!this.overrides || !this.overrides.isEqual(edge.overrides)) {
|
|
1468
|
+
this.updateOverridesEdgeInAdded(edge.overrides)
|
|
1352
1469
|
}
|
|
1353
1470
|
|
|
1354
1471
|
this.edgesIn.add(edge)
|
package/lib/override-set.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const npa = require('npm-package-arg')
|
|
2
2
|
const semver = require('semver')
|
|
3
|
+
const { log } = require('proc-log')
|
|
3
4
|
|
|
4
5
|
class OverrideSet {
|
|
5
6
|
constructor ({ overrides, key, parent }) {
|
|
@@ -44,6 +45,43 @@ class OverrideSet {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
childrenAreEqual (other) {
|
|
49
|
+
if (this.children.size !== other.children.size) {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
for (const [key] of this.children) {
|
|
53
|
+
if (!other.children.has(key)) {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
if (this.children.get(key).value !== other.children.get(key).value) {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
if (!this.children.get(key).childrenAreEqual(other.children.get(key))) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
isEqual (other) {
|
|
67
|
+
if (this === other) {
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
if (!other) {
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
if (this.key !== other.key || this.value !== other.value) {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
if (!this.childrenAreEqual(other)) {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
if (!this.parent) {
|
|
80
|
+
return !other.parent
|
|
81
|
+
}
|
|
82
|
+
return this.parent.isEqual(other.parent)
|
|
83
|
+
}
|
|
84
|
+
|
|
47
85
|
getEdgeRule (edge) {
|
|
48
86
|
for (const rule of this.ruleset.values()) {
|
|
49
87
|
if (rule.name !== edge.name) {
|
|
@@ -55,7 +93,9 @@ class OverrideSet {
|
|
|
55
93
|
return rule
|
|
56
94
|
}
|
|
57
95
|
|
|
58
|
-
|
|
96
|
+
// We need to use the rawSpec here, because the spec has the overrides applied to it already.
|
|
97
|
+
// rawSpec can be undefined, so we need to use the fallback value of spec if it is.
|
|
98
|
+
let spec = npa(`${edge.name}@${edge.rawSpec || edge.spec}`)
|
|
59
99
|
if (spec.type === 'alias') {
|
|
60
100
|
spec = spec.subSpec
|
|
61
101
|
}
|
|
@@ -142,6 +182,28 @@ class OverrideSet {
|
|
|
142
182
|
|
|
143
183
|
return ruleset
|
|
144
184
|
}
|
|
185
|
+
|
|
186
|
+
static findSpecificOverrideSet (first, second) {
|
|
187
|
+
for (let overrideSet = second; overrideSet; overrideSet = overrideSet.parent) {
|
|
188
|
+
if (overrideSet.isEqual(first)) {
|
|
189
|
+
return second
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
for (let overrideSet = first; overrideSet; overrideSet = overrideSet.parent) {
|
|
193
|
+
if (overrideSet.isEqual(second)) {
|
|
194
|
+
return first
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// The override sets are incomparable. Neither one contains the other.
|
|
199
|
+
log.silly('Conflicting override sets', first, second)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static doOverrideSetsConflict (first, second) {
|
|
203
|
+
// If override sets contain one another then we can try to use the more specific one.
|
|
204
|
+
// If neither one is more specific, then we consider them to be in conflict.
|
|
205
|
+
return (this.findSpecificOverrideSet(first, second) === undefined)
|
|
206
|
+
}
|
|
145
207
|
}
|
|
146
208
|
|
|
147
209
|
module.exports = OverrideSet
|
package/lib/place-dep.js
CHANGED
|
@@ -423,7 +423,7 @@ class PlaceDep {
|
|
|
423
423
|
// is another satisfying node further up the tree, and if so, dedupes.
|
|
424
424
|
// Even in installStategy is nested, we do this amount of deduplication.
|
|
425
425
|
pruneDedupable (node, descend = true) {
|
|
426
|
-
if (node.canDedupe(this.preferDedupe)) {
|
|
426
|
+
if (node.canDedupe(this.preferDedupe, this.explicitRequest)) {
|
|
427
427
|
// gather up all deps that have no valid edges in from outside
|
|
428
428
|
// the dep set, except for this node we're deduping, so that we
|
|
429
429
|
// also prune deps that would be made extraneous.
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -109,6 +109,7 @@ const nodeMetaKeys = [
|
|
|
109
109
|
'inBundle',
|
|
110
110
|
'hasShrinkwrap',
|
|
111
111
|
'hasInstallScript',
|
|
112
|
+
'ideallyInert',
|
|
112
113
|
]
|
|
113
114
|
|
|
114
115
|
const metaFieldFromPkg = (pkg, key) => {
|
|
@@ -135,6 +136,10 @@ const assertNoNewer = async (path, data, lockTime, dir, seen) => {
|
|
|
135
136
|
|
|
136
137
|
const parent = isParent ? dir : resolve(dir, 'node_modules')
|
|
137
138
|
const rel = relpath(path, dir)
|
|
139
|
+
const inert = data.packages[rel]?.ideallyInert
|
|
140
|
+
if (inert) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
138
143
|
seen.add(rel)
|
|
139
144
|
let entries
|
|
140
145
|
if (dir === path) {
|
|
@@ -173,7 +178,7 @@ const assertNoNewer = async (path, data, lockTime, dir, seen) => {
|
|
|
173
178
|
|
|
174
179
|
// assert that all the entries in the lockfile were seen
|
|
175
180
|
for (const loc in data.packages) {
|
|
176
|
-
if (!seen.has(loc)) {
|
|
181
|
+
if (!seen.has(loc) && !data.packages[loc].ideallyInert) {
|
|
177
182
|
throw new Error(`missing from node_modules: ${loc}`)
|
|
178
183
|
}
|
|
179
184
|
}
|
|
@@ -783,6 +788,10 @@ class Shrinkwrap {
|
|
|
783
788
|
// ok, I did my best! good luck!
|
|
784
789
|
}
|
|
785
790
|
|
|
791
|
+
if (lock.ideallyInert) {
|
|
792
|
+
meta.ideallyInert = true
|
|
793
|
+
}
|
|
794
|
+
|
|
786
795
|
if (lock.bundled) {
|
|
787
796
|
meta.inBundle = true
|
|
788
797
|
}
|
|
@@ -817,7 +826,7 @@ class Shrinkwrap {
|
|
|
817
826
|
if (!/^file:/.test(resolved)) {
|
|
818
827
|
pathFixed = resolved
|
|
819
828
|
} else {
|
|
820
|
-
pathFixed = `file:${resolve(this.path, resolved.slice(5))
|
|
829
|
+
pathFixed = `file:${resolve(this.path, resolved.slice(5))}`
|
|
821
830
|
}
|
|
822
831
|
}
|
|
823
832
|
|
|
@@ -953,6 +962,12 @@ class Shrinkwrap {
|
|
|
953
962
|
this.#buildLegacyLockfile(this.tree, this.data)
|
|
954
963
|
}
|
|
955
964
|
|
|
965
|
+
if (!this.hiddenLockfile) {
|
|
966
|
+
for (const node of Object.values(this.data.packages)) {
|
|
967
|
+
delete node.ideallyInert
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
956
971
|
// lf version 1 = dependencies only
|
|
957
972
|
// lf version 2 = dependencies and packages
|
|
958
973
|
// lf version 3 = packages only
|
|
@@ -1011,7 +1026,7 @@ class Shrinkwrap {
|
|
|
1011
1026
|
}
|
|
1012
1027
|
|
|
1013
1028
|
if (node.isLink) {
|
|
1014
|
-
lock.version = `file:${relpath(this.path, node.realpath)
|
|
1029
|
+
lock.version = `file:${relpath(this.path, node.realpath)}`
|
|
1015
1030
|
} else if (spec && (spec.type === 'file' || spec.type === 'remote')) {
|
|
1016
1031
|
lock.version = spec.saveSpec
|
|
1017
1032
|
} else if (spec && spec.type === 'git' || rSpec.type === 'git') {
|
|
@@ -1089,7 +1104,7 @@ class Shrinkwrap {
|
|
|
1089
1104
|
// this especially shows up with workspace edges when the root
|
|
1090
1105
|
// node is also a workspace in the set.
|
|
1091
1106
|
const p = resolve(node.realpath, spec.slice('file:'.length))
|
|
1092
|
-
set[k] = `file:${relpath(node.realpath, p)
|
|
1107
|
+
set[k] = `file:${relpath(node.realpath, p)}`
|
|
1093
1108
|
} else {
|
|
1094
1109
|
set[k] = spec
|
|
1095
1110
|
}
|