@npmcli/arborist 8.0.2 → 8.0.3
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/isolated-reifier.js +99 -128
- package/lib/arborist/reify.js +31 -66
- package/lib/inventory.js +1 -2
- package/lib/isolated-classes.js +138 -0
- package/lib/node.js +39 -19
- package/lib/query-selector-all.js +7 -2
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ const pacote = require('pacote')
|
|
|
3
3
|
const { join } = require('node:path')
|
|
4
4
|
const { depth } = require('treeverse')
|
|
5
5
|
const crypto = require('node:crypto')
|
|
6
|
+
const { IsolatedNode, IsolatedLink } = require('../isolated-classes.js')
|
|
6
7
|
|
|
7
8
|
// generate short hash key based on the dependency tree starting at this node
|
|
8
9
|
const getKey = (startNode) => {
|
|
@@ -33,43 +34,25 @@ const getKey = (startNode) => {
|
|
|
33
34
|
|
|
34
35
|
module.exports = cls => class IsolatedReifier extends cls {
|
|
35
36
|
#externalProxies = new Map()
|
|
37
|
+
#omit = new Set()
|
|
38
|
+
#rootDeclaredDeps = new Set()
|
|
36
39
|
#processedEdges = new Set()
|
|
37
40
|
#workspaceProxies = new Map()
|
|
38
41
|
|
|
39
|
-
#generateChild (node, location, pkg,
|
|
40
|
-
const newChild = {
|
|
41
|
-
|
|
42
|
-
children: new Map(),
|
|
43
|
-
edgesIn: new Set(),
|
|
44
|
-
edgesOut: new Map(),
|
|
45
|
-
fsChildren: new Set(),
|
|
46
|
-
/* istanbul ignore next -- emulate Node */
|
|
47
|
-
getBundler () {
|
|
48
|
-
return null
|
|
49
|
-
},
|
|
50
|
-
global: false,
|
|
51
|
-
globalTop: false,
|
|
52
|
-
hasShrinkwrap: false,
|
|
53
|
-
inDepBundle: false,
|
|
54
|
-
integrity: null,
|
|
55
|
-
isInStore: inStore,
|
|
56
|
-
isLink: false,
|
|
57
|
-
isProjectRoot: false,
|
|
58
|
-
isRoot: false,
|
|
59
|
-
isTop: false,
|
|
42
|
+
#generateChild (node, location, pkg, isInStore, root) {
|
|
43
|
+
const newChild = new IsolatedNode({
|
|
44
|
+
isInStore,
|
|
60
45
|
location,
|
|
61
46
|
name: node.packageName || node.name,
|
|
62
47
|
optional: node.optional,
|
|
63
48
|
package: pkg,
|
|
64
49
|
parent: root,
|
|
65
|
-
path: join(this.idealGraph.
|
|
66
|
-
realpath: join(this.idealGraph.root.localPath, location),
|
|
50
|
+
path: join(this.idealGraph.localPath, location),
|
|
67
51
|
resolved: node.resolved,
|
|
68
52
|
root,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
newChild.target = newChild
|
|
53
|
+
})
|
|
54
|
+
// XXX top is from place-dep not lib/node.js
|
|
55
|
+
newChild.top = { path: this.idealGraph.localPath }
|
|
73
56
|
root.children.set(newChild.location, newChild)
|
|
74
57
|
root.inventory.set(newChild.location, newChild)
|
|
75
58
|
}
|
|
@@ -90,16 +73,31 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
90
73
|
**/
|
|
91
74
|
async makeIdealGraph () {
|
|
92
75
|
const idealTree = this.idealTree
|
|
93
|
-
|
|
76
|
+
this.#omit = new Set(this.options.omit)
|
|
77
|
+
const omit = this.#omit
|
|
78
|
+
|
|
79
|
+
// npm auto-creates 'workspace' edges from root to all workspaces.
|
|
80
|
+
// For isolated/linked mode, only include workspaces that root explicitly declares as dependencies.
|
|
81
|
+
// When omitting dep types, exclude those from the declared set so their workspaces aren't hoisted.
|
|
82
|
+
const rootPkg = idealTree.package
|
|
83
|
+
this.#rootDeclaredDeps = new Set(Object.keys(Object.assign({},
|
|
84
|
+
rootPkg.dependencies,
|
|
85
|
+
(!omit.has('dev') && rootPkg.devDependencies),
|
|
86
|
+
(!omit.has('optional') && rootPkg.optionalDependencies),
|
|
87
|
+
(!omit.has('peer') && rootPkg.peerDependencies)
|
|
88
|
+
)))
|
|
89
|
+
|
|
90
|
+
// XXX this sometimes acts like a node too
|
|
94
91
|
this.idealGraph = {
|
|
95
92
|
external: [],
|
|
96
93
|
isProjectRoot: true,
|
|
97
94
|
localLocation: idealTree.location,
|
|
98
95
|
localPath: idealTree.path,
|
|
96
|
+
path: idealTree.path,
|
|
99
97
|
}
|
|
100
98
|
this.counter = 0
|
|
101
99
|
|
|
102
|
-
this.idealGraph.workspaces = await Promise.all(Array.from(idealTree.fsChildren.values(), w => this
|
|
100
|
+
this.idealGraph.workspaces = await Promise.all(Array.from(idealTree.fsChildren.values(), w => this.#workspaceProxy(w)))
|
|
103
101
|
const processed = new Set()
|
|
104
102
|
const queue = [idealTree, ...idealTree.fsChildren]
|
|
105
103
|
while (queue.length !== 0) {
|
|
@@ -109,43 +107,43 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
109
107
|
}
|
|
110
108
|
processed.add(next.location)
|
|
111
109
|
next.edgesOut.forEach(edge => {
|
|
112
|
-
if (edge.to && !(next.package.bundleDependencies || next.package.bundledDependencies || []).includes(edge.to.name)) {
|
|
110
|
+
if (edge.to && !(next.package.bundleDependencies || next.package.bundledDependencies || []).includes(edge.to.name) && !edge.to.shouldOmit?.(omit)) {
|
|
113
111
|
queue.push(edge.to)
|
|
114
112
|
}
|
|
115
113
|
})
|
|
116
114
|
// local `file:` deps are in fsChildren but are not workspaces.
|
|
117
115
|
// they are already handled as workspace-like proxies above and should not go through the external/store extraction path.
|
|
118
116
|
if (!next.isProjectRoot && !next.isWorkspace && !next.inert && !idealTree.fsChildren.has(next) && !idealTree.fsChildren.has(next.target)) {
|
|
119
|
-
this.idealGraph.external.push(await this
|
|
117
|
+
this.idealGraph.external.push(await this.#externalProxy(next))
|
|
120
118
|
}
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
await this
|
|
121
|
+
await this.#assignCommonProperties(idealTree, this.idealGraph)
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
async workspaceProxy (node) {
|
|
124
|
+
async #workspaceProxy (node) {
|
|
127
125
|
if (this.#workspaceProxies.has(node)) {
|
|
128
126
|
return this.#workspaceProxies.get(node)
|
|
129
127
|
}
|
|
130
128
|
const result = {}
|
|
131
|
-
// XXX this goes recursive if we don't set here because assignCommonProperties also calls this
|
|
129
|
+
// XXX this goes recursive if we don't set here because assignCommonProperties also calls this.#workspaceProxy
|
|
132
130
|
this.#workspaceProxies.set(node, result)
|
|
133
131
|
result.localLocation = node.location
|
|
134
132
|
result.localPath = node.path
|
|
135
133
|
result.isWorkspace = true
|
|
136
134
|
result.resolved = node.resolved
|
|
137
|
-
await this
|
|
135
|
+
await this.#assignCommonProperties(node, result)
|
|
138
136
|
return result
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
async externalProxy (node) {
|
|
139
|
+
async #externalProxy (node) {
|
|
142
140
|
if (this.#externalProxies.has(node)) {
|
|
143
141
|
return this.#externalProxies.get(node)
|
|
144
142
|
}
|
|
145
143
|
const result = {}
|
|
146
|
-
// XXX this goes recursive if we don't set here because assignCommonProperties also calls this
|
|
144
|
+
// XXX this goes recursive if we don't set here because assignCommonProperties also calls this.#externalProxy
|
|
147
145
|
this.#externalProxies.set(node, result)
|
|
148
|
-
await this
|
|
146
|
+
await this.#assignCommonProperties(node, result, !node.hasShrinkwrap)
|
|
149
147
|
if (node.hasShrinkwrap) {
|
|
150
148
|
const dir = join(
|
|
151
149
|
node.root.path,
|
|
@@ -187,8 +185,9 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
187
185
|
return result
|
|
188
186
|
}
|
|
189
187
|
|
|
190
|
-
async assignCommonProperties (node, result, populateDeps = true) {
|
|
188
|
+
async #assignCommonProperties (node, result, populateDeps = true) {
|
|
191
189
|
result.root = this.idealGraph
|
|
190
|
+
// XXX does anything need this?
|
|
192
191
|
result.id = this.counter++
|
|
193
192
|
/* istanbul ignore next - packageName is always set for real packages */
|
|
194
193
|
result.name = result.isWorkspace ? (node.packageName || node.name) : node.name
|
|
@@ -196,17 +195,39 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
196
195
|
result.packageName = node.packageName || node.name
|
|
197
196
|
result.package = { ...node.package }
|
|
198
197
|
result.package.bundleDependencies = undefined
|
|
199
|
-
result.hasInstallScript = node.hasInstallScript
|
|
200
198
|
|
|
201
199
|
if (!populateDeps) {
|
|
202
200
|
return
|
|
203
201
|
}
|
|
204
202
|
|
|
205
|
-
|
|
203
|
+
let edges = [...node.edgesOut.values()].filter(edge =>
|
|
206
204
|
edge.to?.target &&
|
|
207
205
|
!(node.package.bundledDependencies || node.package.bundleDependencies)?.includes(edge.to.name)
|
|
208
206
|
)
|
|
209
|
-
|
|
207
|
+
|
|
208
|
+
// Only omit edge types for root and workspace nodes (matching shouldOmit scope)
|
|
209
|
+
if ((node.isProjectRoot || node.isWorkspace) && this.#omit.size) {
|
|
210
|
+
edges = edges.filter(edge => {
|
|
211
|
+
if (edge.dev && this.#omit.has('dev')) {
|
|
212
|
+
return false
|
|
213
|
+
}
|
|
214
|
+
if (edge.optional && this.#omit.has('optional')) {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
if (edge.peer && this.#omit.has('peer')) {
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
return true
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let nonOptionalDeps = edges.filter(edge => !edge.optional).map(edge => edge.to.target)
|
|
225
|
+
|
|
226
|
+
// npm auto-creates 'workspace' edges from root to all workspaces.
|
|
227
|
+
// For isolated/linked mode, only include workspaces that root explicitly declares as dependencies.
|
|
228
|
+
if (node.isProjectRoot) {
|
|
229
|
+
nonOptionalDeps = nonOptionalDeps.filter(n => !n.isWorkspace || this.#rootDeclaredDeps.has(n.packageName))
|
|
230
|
+
}
|
|
210
231
|
|
|
211
232
|
// When legacyPeerDeps is enabled, peer dep edges are not created on the node.
|
|
212
233
|
// Resolve them from the tree so they get symlinked in the store.
|
|
@@ -226,9 +247,9 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
226
247
|
// local `file:` deps (non-workspace fsChildren) should be treated as local dependencies, not external, so they get symlinked directly instead of being extracted into the store.
|
|
227
248
|
const isLocal = (n) => n.isWorkspace || node.fsChildren?.has(n)
|
|
228
249
|
const optionalDeps = edges.filter(edge => edge.optional).map(edge => edge.to.target)
|
|
229
|
-
result.localDependencies = await Promise.all(nonOptionalDeps.filter(isLocal).map(n => this
|
|
230
|
-
result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !isLocal(n) && !n.inert).map(n => this
|
|
231
|
-
result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.inert).map(n => this
|
|
250
|
+
result.localDependencies = await Promise.all(nonOptionalDeps.filter(isLocal).map(n => this.#workspaceProxy(n)))
|
|
251
|
+
result.externalDependencies = await Promise.all(nonOptionalDeps.filter(n => !isLocal(n) && !n.inert).map(n => this.#externalProxy(n)))
|
|
252
|
+
result.externalOptionalDependencies = await Promise.all(optionalDeps.filter(n => !n.inert).map(n => this.#externalProxy(n)))
|
|
232
253
|
result.dependencies = [
|
|
233
254
|
...result.externalDependencies,
|
|
234
255
|
...result.localDependencies,
|
|
@@ -288,91 +309,47 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
288
309
|
|
|
289
310
|
async createIsolatedTree () {
|
|
290
311
|
await this.makeIdealGraph()
|
|
291
|
-
|
|
292
312
|
const bundledTree = await this.#createBundledTree()
|
|
293
313
|
|
|
294
|
-
const root =
|
|
295
|
-
binPaths: [],
|
|
296
|
-
children: new Map(),
|
|
297
|
-
edgesIn: new Set(),
|
|
298
|
-
edgesOut: new Map(),
|
|
299
|
-
fsChildren: new Set(),
|
|
300
|
-
global: false,
|
|
301
|
-
hasShrinkwrap: false,
|
|
302
|
-
integrity: null,
|
|
303
|
-
inventory: new Map(),
|
|
304
|
-
isLink: false,
|
|
305
|
-
isProjectRoot: true,
|
|
306
|
-
isRoot: true,
|
|
307
|
-
isTop: true,
|
|
308
|
-
linksIn: new Set(),
|
|
309
|
-
meta: { loadedFromDisk: false },
|
|
310
|
-
package: this.idealGraph.root.package,
|
|
311
|
-
parent: null,
|
|
312
|
-
path: this.idealGraph.root.localPath,
|
|
313
|
-
realpath: this.idealGraph.root.localPath,
|
|
314
|
-
// TODO: we should probably not reference this.idealTree
|
|
315
|
-
resolved: this.idealTree.resolved,
|
|
316
|
-
tops: new Set(),
|
|
317
|
-
workspaces: new Map(),
|
|
318
|
-
}
|
|
319
|
-
root.inventory.set('', root)
|
|
314
|
+
const root = new IsolatedNode(this.idealGraph)
|
|
320
315
|
root.root = root
|
|
321
|
-
root.
|
|
322
|
-
|
|
323
|
-
root.inventory.query = () => {
|
|
324
|
-
return []
|
|
325
|
-
}
|
|
316
|
+
root.top = root
|
|
317
|
+
root.inventory.set('', root)
|
|
326
318
|
const processed = new Set()
|
|
327
319
|
for (const c of this.idealGraph.workspaces) {
|
|
328
320
|
const wsName = c.packageName
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
children: new Map(),
|
|
332
|
-
edgesIn: new Set(),
|
|
333
|
-
edgesOut: new Map(),
|
|
334
|
-
fsChildren: new Set(),
|
|
335
|
-
hasInstallScript: c.hasInstallScript,
|
|
336
|
-
isLink: false,
|
|
337
|
-
isRoot: false,
|
|
338
|
-
linksIn: new Set(),
|
|
321
|
+
// XXX parent? root?
|
|
322
|
+
const workspace = new IsolatedNode({
|
|
339
323
|
location: c.localLocation,
|
|
340
324
|
name: wsName,
|
|
341
325
|
package: c.package,
|
|
342
326
|
path: c.localPath,
|
|
343
|
-
realpath: c.localPath,
|
|
344
327
|
resolved: c.resolved,
|
|
345
|
-
}
|
|
346
|
-
workspace.
|
|
328
|
+
})
|
|
329
|
+
workspace.top = workspace
|
|
330
|
+
workspace.isWorkspace = true
|
|
347
331
|
root.fsChildren.add(workspace)
|
|
348
332
|
root.inventory.set(workspace.location, workspace)
|
|
333
|
+
root.workspaces.set(wsName, workspace.path)
|
|
349
334
|
|
|
350
|
-
// Create workspace Link
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
edgesIn: new Set(),
|
|
355
|
-
edgesOut: new Map(),
|
|
356
|
-
fsChildren: new Set(),
|
|
357
|
-
global: false,
|
|
358
|
-
globalTop: false,
|
|
359
|
-
isLink: true,
|
|
360
|
-
isProjectRoot: false,
|
|
361
|
-
isRoot: false,
|
|
362
|
-
isTop: false,
|
|
363
|
-
linksIn: new Set(),
|
|
364
|
-
location: join('node_modules', wsName),
|
|
335
|
+
// Create workspace Link. For root declared deps, link at root node_modules/. For undeclared deps, link at the workspace's own node_modules/ (self-link).
|
|
336
|
+
const isDeclared = this.#rootDeclaredDeps.has(wsName)
|
|
337
|
+
const wsLink = new IsolatedLink({
|
|
338
|
+
location: isDeclared ? join('node_modules', wsName) : join(c.localLocation, 'node_modules', wsName),
|
|
365
339
|
name: wsName,
|
|
366
340
|
package: workspace.package,
|
|
367
341
|
parent: root,
|
|
368
|
-
path: join(root.path, 'node_modules', wsName),
|
|
342
|
+
path: isDeclared ? join(root.path, 'node_modules', wsName) : join(root.path, c.localLocation, 'node_modules', wsName),
|
|
369
343
|
realpath: workspace.path,
|
|
370
344
|
root,
|
|
371
345
|
target: workspace,
|
|
346
|
+
})
|
|
347
|
+
wsLink.top = root
|
|
348
|
+
if (!isDeclared) {
|
|
349
|
+
workspace.children.set(wsName, wsLink)
|
|
372
350
|
}
|
|
373
|
-
root.children.set(
|
|
351
|
+
root.children.set(wsName, wsLink)
|
|
374
352
|
root.inventory.set(wsLink.location, wsLink)
|
|
375
|
-
root.workspaces.set(wsName, workspace.path)
|
|
376
353
|
workspace.linksIn.add(wsLink)
|
|
377
354
|
}
|
|
378
355
|
|
|
@@ -466,35 +443,29 @@ module.exports = cls => class IsolatedReifier extends cls {
|
|
|
466
443
|
}
|
|
467
444
|
}
|
|
468
445
|
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
isProjectRoot: false,
|
|
479
|
-
isRoot: false,
|
|
446
|
+
const pkg = {
|
|
447
|
+
_id: dep.package._id,
|
|
448
|
+
bin: target.package.bin,
|
|
449
|
+
bundleDependencies: undefined,
|
|
450
|
+
deprecated: undefined,
|
|
451
|
+
scripts: dep.package.scripts,
|
|
452
|
+
version: dep.package.version,
|
|
453
|
+
}
|
|
454
|
+
const link = new IsolatedLink({
|
|
480
455
|
isStoreLink: true,
|
|
481
|
-
isTop: false,
|
|
482
456
|
location: join(nmFolder, dep.name),
|
|
483
457
|
name: toKey,
|
|
484
458
|
optional,
|
|
485
|
-
// TODO _id: 'abc' ?
|
|
486
|
-
package: { _id: 'abc', bundleDependencies: undefined, deprecated: undefined, bin: target.package.bin, scripts: dep.package.scripts },
|
|
487
459
|
parent: root,
|
|
460
|
+
package: pkg,
|
|
488
461
|
path: join(dep.root.localPath, nmFolder, dep.name),
|
|
489
462
|
realpath: target.path,
|
|
490
|
-
resolved: external
|
|
491
|
-
? `file:.store/${toKey}/node_modules/${dep.packageName}`
|
|
492
|
-
: dep.resolved,
|
|
463
|
+
resolved: external ? `file:.store/${toKey}/node_modules/${dep.packageName}` : dep.resolved,
|
|
493
464
|
root,
|
|
494
465
|
target,
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
466
|
+
})
|
|
467
|
+
// XXX top is from place-dep not lib/link.js
|
|
468
|
+
link.top = { path: dep.root.localPath }
|
|
498
469
|
const newEdge1 = { optional, from, to: link }
|
|
499
470
|
from.edgesOut.set(dep.name, newEdge1)
|
|
500
471
|
link.edgesIn.add(newEdge1)
|
package/lib/arborist/reify.js
CHANGED
|
@@ -36,6 +36,7 @@ const { callLimit: promiseCallLimit } = require('promise-call-limit')
|
|
|
36
36
|
const optionalSet = require('../optional-set.js')
|
|
37
37
|
const calcDepFlags = require('../calc-dep-flags.js')
|
|
38
38
|
const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js')
|
|
39
|
+
const { IsolatedNode, IsolatedLink } = require('../isolated-classes.js')
|
|
39
40
|
|
|
40
41
|
const Shrinkwrap = require('../shrinkwrap.js')
|
|
41
42
|
const { defaultLockfileVersion } = Shrinkwrap
|
|
@@ -841,49 +842,23 @@ module.exports = cls => class Reifier extends cls {
|
|
|
841
842
|
#buildLinkedActualForDiff (idealTree, actualTree) {
|
|
842
843
|
const combined = new Map()
|
|
843
844
|
|
|
844
|
-
for
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
845
|
+
// Create synthetic actual entries for ALL ideal children that exist on disk.
|
|
846
|
+
// The isolated ideal tree is flat (all entries as root children), but loadActual() produces a nested tree where workspace deps are under fsChildren and store entries are deep link targets.
|
|
847
|
+
// Synthetic entries ensure the diff compares matching resolved/integrity values (e.g. workspace links have resolved=undefined in the ideal tree but resolved="file:../packages/..." in the actual tree).
|
|
848
848
|
for (const child of idealTree.children.values()) {
|
|
849
|
-
if (
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
children: [],
|
|
861
|
-
edgesIn: new Set(),
|
|
862
|
-
edgesOut: new Map(),
|
|
863
|
-
binPaths: [],
|
|
864
|
-
fsChildren: [],
|
|
865
|
-
/* istanbul ignore next -- emulate Node */
|
|
866
|
-
getBundler () {
|
|
867
|
-
return null
|
|
868
|
-
},
|
|
869
|
-
hasShrinkwrap: false,
|
|
870
|
-
inDepBundle: false,
|
|
871
|
-
integrity: null,
|
|
872
|
-
isLink: Boolean(child.isLink),
|
|
873
|
-
isRoot: false,
|
|
874
|
-
isInStore: Boolean(child.isInStore),
|
|
875
|
-
path: child.path,
|
|
876
|
-
realpath: child.realpath,
|
|
877
|
-
resolved: child.resolved,
|
|
878
|
-
version: child.version,
|
|
879
|
-
package: child.package,
|
|
880
|
-
}
|
|
881
|
-
entry.target = entry
|
|
882
|
-
if (child.isLink && combined.has(child.realpath)) {
|
|
883
|
-
entry.target = combined.get(child.realpath)
|
|
884
|
-
}
|
|
885
|
-
combined.set(child.path, entry)
|
|
849
|
+
if (combined.has(child.path) || !existsSync(child.path)) {
|
|
850
|
+
continue
|
|
851
|
+
}
|
|
852
|
+
let entry
|
|
853
|
+
if (child.isLink) {
|
|
854
|
+
entry = new IsolatedLink(child)
|
|
855
|
+
} else {
|
|
856
|
+
entry = new IsolatedNode(child)
|
|
857
|
+
}
|
|
858
|
+
if (child.isLink && combined.has(child.realpath)) {
|
|
859
|
+
entry.target = combined.get(child.realpath)
|
|
886
860
|
}
|
|
861
|
+
combined.set(child.path, entry)
|
|
887
862
|
}
|
|
888
863
|
|
|
889
864
|
const origGet = actualTree.children.get.bind(actualTree.children)
|
|
@@ -891,32 +866,22 @@ module.exports = cls => class Reifier extends cls {
|
|
|
891
866
|
/* istanbul ignore next -- only reached during scoped workspace installs */
|
|
892
867
|
combined.get = (key) => combinedGet(key) || origGet(key)
|
|
893
868
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
realpath: actualTree.realpath,
|
|
901
|
-
edgesOut: actualTree.edgesOut,
|
|
902
|
-
inventory: actualTree.inventory,
|
|
903
|
-
package: actualTree.package,
|
|
904
|
-
resolved: actualTree.resolved,
|
|
905
|
-
version: actualTree.version,
|
|
906
|
-
integrity: actualTree.integrity,
|
|
907
|
-
binPaths: actualTree.binPaths,
|
|
908
|
-
hasShrinkwrap: false,
|
|
909
|
-
inDepBundle: false,
|
|
910
|
-
parent: null,
|
|
911
|
-
children: combined,
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
for (const child of combined.values()) {
|
|
915
|
-
if (!child.parent) {
|
|
916
|
-
child.parent = wrapper
|
|
917
|
-
child.root = wrapper
|
|
918
|
-
}
|
|
869
|
+
let wrapper
|
|
870
|
+
/* istanbul ignore next - untested! */
|
|
871
|
+
if (actualTree.isLink) {
|
|
872
|
+
wrapper = new IsolatedLink(actualTree)
|
|
873
|
+
} else {
|
|
874
|
+
wrapper = new IsolatedNode(actualTree)
|
|
919
875
|
}
|
|
876
|
+
wrapper.root = wrapper
|
|
877
|
+
wrapper.binPaths = actualTree.binPaths
|
|
878
|
+
wrapper.children = combined
|
|
879
|
+
wrapper.edgesOut = actualTree.edgesOut
|
|
880
|
+
// Use empty fsChildren so that allChildren() only picks up entries from the combined map.
|
|
881
|
+
// The actual fsChildren have real children with different resolved values (e.g. file:../../../node_modules/.store/... vs file:.store/...) that would overwrite our synthetic entries in allChildren().
|
|
882
|
+
wrapper.fsChildren = new Set()
|
|
883
|
+
wrapper.integrity = actualTree.integrity
|
|
884
|
+
wrapper.inventory = actualTree.inventory
|
|
920
885
|
|
|
921
886
|
return wrapper
|
|
922
887
|
}
|
package/lib/inventory.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// a class to manage an inventory and set of indexes of a set of objects based
|
|
2
|
-
// on specific fields.
|
|
1
|
+
// a class to manage an inventory and set of indexes of a set of objects based on specific fields.
|
|
3
2
|
const { hasOwnProperty } = Object.prototype
|
|
4
3
|
const debug = require('./debug.js')
|
|
5
4
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Alternate versions of different classes that we use for isolated mode
|
|
2
|
+
const CaseInsensitiveMap = require('./case-insensitive-map.js')
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
|
|
5
|
+
// fake lib/inventory.js
|
|
6
|
+
class IsolatedInventory extends Map {
|
|
7
|
+
query () {
|
|
8
|
+
return []
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// fake lib/node.js
|
|
13
|
+
class IsolatedNode {
|
|
14
|
+
binPaths = []
|
|
15
|
+
children = new CaseInsensitiveMap()
|
|
16
|
+
edgesIn = new Set()
|
|
17
|
+
edgesOut = new CaseInsensitiveMap()
|
|
18
|
+
fsChildren = new Set()
|
|
19
|
+
hasShrinkwrap = false
|
|
20
|
+
integrity = null
|
|
21
|
+
inventory = new IsolatedInventory()
|
|
22
|
+
isInStore = false
|
|
23
|
+
linksIn = new Set()
|
|
24
|
+
meta = { loadedFromDisk: false }
|
|
25
|
+
optional = false
|
|
26
|
+
parent = null
|
|
27
|
+
root = null
|
|
28
|
+
tops = new Set()
|
|
29
|
+
workspaces = new Map()
|
|
30
|
+
|
|
31
|
+
constructor (options) {
|
|
32
|
+
this.location = options.location
|
|
33
|
+
this.name = options.name
|
|
34
|
+
this.package = options.package
|
|
35
|
+
this.path = options.path
|
|
36
|
+
this.realpath = !this.isLink ? this.path : resolve(options.realpath)
|
|
37
|
+
|
|
38
|
+
if (options.parent) {
|
|
39
|
+
this.parent = options.parent
|
|
40
|
+
}
|
|
41
|
+
if (options.resolved) {
|
|
42
|
+
this.resolved = options.resolved
|
|
43
|
+
}
|
|
44
|
+
if (options.root) {
|
|
45
|
+
this.root = options.root
|
|
46
|
+
}
|
|
47
|
+
if (options.isInStore) {
|
|
48
|
+
this.isInStore = true
|
|
49
|
+
}
|
|
50
|
+
if (options.optional) {
|
|
51
|
+
this.optional = true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get isRoot () {
|
|
56
|
+
return this === this.root
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The idealGraph is where this is set to true
|
|
60
|
+
get isProjectRoot () {
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get inDepBundle () {
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get isLink () {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get isTop () {
|
|
73
|
+
return !this.parent
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* istanbul ignore next -- emulate lib/node.js */
|
|
77
|
+
get global () {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get globalTop () {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* istanbul ignore next -- emulate lib/node.js */
|
|
86
|
+
set target (t) {
|
|
87
|
+
// nop
|
|
88
|
+
// In the real lib/node.js this throws in debug mode
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get target () {
|
|
92
|
+
return this
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* istanbul ignore next -- emulate lib/node.js */
|
|
96
|
+
getBundler () {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* istanbul ignore next -- emulate lib/node.js */
|
|
101
|
+
get hasInstallScript () {
|
|
102
|
+
const { hasInstallScript, scripts } = this.package
|
|
103
|
+
const { install, preinstall, postinstall } = scripts || {}
|
|
104
|
+
return !!(hasInstallScript || install || preinstall || postinstall)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get version () {
|
|
108
|
+
return this.package.version
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// fake lib/link.js
|
|
113
|
+
class IsolatedLink extends IsolatedNode {
|
|
114
|
+
#target
|
|
115
|
+
isStoreLink = false
|
|
116
|
+
|
|
117
|
+
constructor (options) {
|
|
118
|
+
super(options)
|
|
119
|
+
this.#target = options.target
|
|
120
|
+
if (options.isStoreLink) {
|
|
121
|
+
this.isStoreLink = true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get isLink () {
|
|
126
|
+
return true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
set target (t) {
|
|
130
|
+
this.#target = t
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get target () {
|
|
134
|
+
return this.#target
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = { IsolatedNode, IsolatedLink }
|
package/lib/node.js
CHANGED
|
@@ -74,33 +74,32 @@ class Node {
|
|
|
74
74
|
constructor (options) {
|
|
75
75
|
// NB: path can be null if it's a link target
|
|
76
76
|
const {
|
|
77
|
-
root,
|
|
78
|
-
path,
|
|
79
|
-
realpath,
|
|
80
|
-
parent,
|
|
81
|
-
error,
|
|
82
|
-
meta,
|
|
83
|
-
fsParent,
|
|
84
|
-
resolved,
|
|
85
|
-
integrity,
|
|
86
|
-
// allow setting name explicitly when we haven't set a path yet
|
|
87
|
-
name,
|
|
88
77
|
children,
|
|
78
|
+
dev = true,
|
|
79
|
+
devOptional = true,
|
|
80
|
+
dummy = false,
|
|
81
|
+
error,
|
|
82
|
+
extraneous = true,
|
|
89
83
|
fsChildren,
|
|
84
|
+
fsParent,
|
|
85
|
+
global = false,
|
|
86
|
+
hasShrinkwrap,
|
|
90
87
|
installLinks = false,
|
|
88
|
+
integrity,
|
|
89
|
+
isInStore = false,
|
|
91
90
|
legacyPeerDeps = false,
|
|
92
91
|
linksIn,
|
|
93
|
-
isInStore = false,
|
|
94
|
-
hasShrinkwrap,
|
|
95
|
-
overrides,
|
|
96
92
|
loadOverrides = false,
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
meta,
|
|
94
|
+
name, // allow setting name explicitly when we haven't set a path yet
|
|
99
95
|
optional = true,
|
|
100
|
-
|
|
96
|
+
overrides,
|
|
97
|
+
parent,
|
|
98
|
+
path,
|
|
101
99
|
peer = true,
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
realpath,
|
|
101
|
+
resolved,
|
|
102
|
+
root,
|
|
104
103
|
sourceReference = null,
|
|
105
104
|
} = options
|
|
106
105
|
// this object gives querySelectorAll somewhere to stash context about a node
|
|
@@ -464,6 +463,27 @@ class Node {
|
|
|
464
463
|
return false
|
|
465
464
|
}
|
|
466
465
|
|
|
466
|
+
shouldOmit (omitSet) {
|
|
467
|
+
if (!omitSet.size) {
|
|
468
|
+
return false
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const { top } = this
|
|
472
|
+
|
|
473
|
+
// if the top is not the root or workspace then we do not want to omit it
|
|
474
|
+
if (!top.isProjectRoot && !top.isWorkspace) {
|
|
475
|
+
return false
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// omit node if the dep type matches any omit flags that were set
|
|
479
|
+
return (
|
|
480
|
+
this.peer && omitSet.has('peer') ||
|
|
481
|
+
this.dev && omitSet.has('dev') ||
|
|
482
|
+
this.optional && omitSet.has('optional') ||
|
|
483
|
+
this.devOptional && omitSet.has('optional') && omitSet.has('dev')
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
467
487
|
getBundler (path = []) {
|
|
468
488
|
// made a cycle, definitely not bundled!
|
|
469
489
|
if (path.includes(this)) {
|
|
@@ -785,9 +785,14 @@ const hasParent = (node, compareNodes) => {
|
|
|
785
785
|
compareNode = compareNode.target
|
|
786
786
|
}
|
|
787
787
|
|
|
788
|
-
//
|
|
788
|
+
// Follows logical parent for link ancestors (e.g. workspaces whose target lives outside node_modules).
|
|
789
|
+
// Only match if the node has a link whose parent is the compareNode. Without this check, nodes deep in the store (linked strategy) would incorrectly match as children of root via their fsParent chain.
|
|
789
790
|
if (node.isTop && (node.resolveParent === compareNode)) {
|
|
790
|
-
|
|
791
|
+
for (const link of node.linksIn) {
|
|
792
|
+
if (link.parent === compareNode) {
|
|
793
|
+
return true
|
|
794
|
+
}
|
|
795
|
+
}
|
|
791
796
|
}
|
|
792
797
|
// follows edges-in to check if they match a possible parent
|
|
793
798
|
for (const edge of node.edgesIn) {
|