@vltpkg/graph 1.0.0-rc.1 → 1.0.0-rc.10

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.
Files changed (116) hide show
  1. package/dist/esm/actual/load.d.ts.map +1 -1
  2. package/dist/esm/actual/load.js +35 -14
  3. package/dist/esm/actual/load.js.map +1 -1
  4. package/dist/esm/browser.d.ts +1 -1
  5. package/dist/esm/diff.d.ts +2 -0
  6. package/dist/esm/diff.d.ts.map +1 -1
  7. package/dist/esm/edge.d.ts +1 -0
  8. package/dist/esm/edge.d.ts.map +1 -1
  9. package/dist/esm/edge.js +4 -0
  10. package/dist/esm/edge.js.map +1 -1
  11. package/dist/esm/fixup-added-names.d.ts +16 -0
  12. package/dist/esm/fixup-added-names.d.ts.map +1 -0
  13. package/dist/esm/fixup-added-names.js +31 -0
  14. package/dist/esm/fixup-added-names.js.map +1 -0
  15. package/dist/esm/graph.d.ts +18 -4
  16. package/dist/esm/graph.d.ts.map +1 -1
  17. package/dist/esm/graph.js +63 -40
  18. package/dist/esm/graph.js.map +1 -1
  19. package/dist/esm/ideal/append-nodes.d.ts +2 -1
  20. package/dist/esm/ideal/append-nodes.d.ts.map +1 -1
  21. package/dist/esm/ideal/append-nodes.js +183 -78
  22. package/dist/esm/ideal/append-nodes.js.map +1 -1
  23. package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts +4 -4
  24. package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts.map +1 -1
  25. package/dist/esm/ideal/build-ideal-from-starting-graph.js +34 -19
  26. package/dist/esm/ideal/build-ideal-from-starting-graph.js.map +1 -1
  27. package/dist/esm/ideal/build.d.ts.map +1 -1
  28. package/dist/esm/ideal/build.js +29 -2
  29. package/dist/esm/ideal/build.js.map +1 -1
  30. package/dist/esm/ideal/get-importer-specs.d.ts +9 -2
  31. package/dist/esm/ideal/get-importer-specs.d.ts.map +1 -1
  32. package/dist/esm/ideal/get-importer-specs.js +80 -5
  33. package/dist/esm/ideal/get-importer-specs.js.map +1 -1
  34. package/dist/esm/ideal/get-ordered-dependencies.d.ts +10 -0
  35. package/dist/esm/ideal/get-ordered-dependencies.d.ts.map +1 -0
  36. package/dist/esm/ideal/get-ordered-dependencies.js +42 -0
  37. package/dist/esm/ideal/get-ordered-dependencies.js.map +1 -0
  38. package/dist/esm/ideal/peers.d.ts +71 -0
  39. package/dist/esm/ideal/peers.d.ts.map +1 -0
  40. package/dist/esm/ideal/peers.js +318 -0
  41. package/dist/esm/ideal/peers.js.map +1 -0
  42. package/dist/esm/ideal/refresh-ideal-graph.d.ts +48 -0
  43. package/dist/esm/ideal/refresh-ideal-graph.d.ts.map +1 -0
  44. package/dist/esm/ideal/refresh-ideal-graph.js +79 -0
  45. package/dist/esm/ideal/refresh-ideal-graph.js.map +1 -0
  46. package/dist/esm/ideal/types.d.ts +78 -1
  47. package/dist/esm/ideal/types.d.ts.map +1 -1
  48. package/dist/esm/ideal/types.js.map +1 -1
  49. package/dist/esm/index.d.ts +2 -1
  50. package/dist/esm/index.d.ts.map +1 -1
  51. package/dist/esm/index.js +1 -0
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/install.d.ts +3 -2
  54. package/dist/esm/install.d.ts.map +1 -1
  55. package/dist/esm/install.js +30 -6
  56. package/dist/esm/install.js.map +1 -1
  57. package/dist/esm/lockfile/load-edges.d.ts.map +1 -1
  58. package/dist/esm/lockfile/load-edges.js +4 -3
  59. package/dist/esm/lockfile/load-edges.js.map +1 -1
  60. package/dist/esm/lockfile/load-nodes.d.ts +1 -1
  61. package/dist/esm/lockfile/load-nodes.d.ts.map +1 -1
  62. package/dist/esm/lockfile/load-nodes.js +16 -4
  63. package/dist/esm/lockfile/load-nodes.js.map +1 -1
  64. package/dist/esm/lockfile/load.d.ts +2 -2
  65. package/dist/esm/lockfile/load.d.ts.map +1 -1
  66. package/dist/esm/lockfile/load.js +6 -29
  67. package/dist/esm/lockfile/load.js.map +1 -1
  68. package/dist/esm/lockfile/save.d.ts +5 -1
  69. package/dist/esm/lockfile/save.d.ts.map +1 -1
  70. package/dist/esm/lockfile/save.js +23 -9
  71. package/dist/esm/lockfile/save.js.map +1 -1
  72. package/dist/esm/node.d.ts +14 -0
  73. package/dist/esm/node.d.ts.map +1 -1
  74. package/dist/esm/node.js +21 -0
  75. package/dist/esm/node.js.map +1 -1
  76. package/dist/esm/reify/add-edges.d.ts +1 -1
  77. package/dist/esm/reify/add-edges.d.ts.map +1 -1
  78. package/dist/esm/reify/add-edges.js +2 -1
  79. package/dist/esm/reify/add-edges.js.map +1 -1
  80. package/dist/esm/reify/extract-node.d.ts.map +1 -1
  81. package/dist/esm/reify/extract-node.js +10 -2
  82. package/dist/esm/reify/extract-node.js.map +1 -1
  83. package/dist/esm/reify/index.js +1 -1
  84. package/dist/esm/reify/index.js.map +1 -1
  85. package/dist/esm/reify/update-importers-package-json.d.ts +1 -1
  86. package/dist/esm/reify/update-importers-package-json.d.ts.map +1 -1
  87. package/dist/esm/reify/update-importers-package-json.js +19 -16
  88. package/dist/esm/reify/update-importers-package-json.js.map +1 -1
  89. package/dist/esm/stringify-node.d.ts.map +1 -1
  90. package/dist/esm/stringify-node.js +10 -1
  91. package/dist/esm/stringify-node.js.map +1 -1
  92. package/dist/esm/uninstall.d.ts.map +1 -1
  93. package/dist/esm/uninstall.js +10 -0
  94. package/dist/esm/uninstall.js.map +1 -1
  95. package/dist/esm/update.d.ts.map +1 -1
  96. package/dist/esm/update.js +10 -0
  97. package/dist/esm/update.js.map +1 -1
  98. package/dist/esm/visualization/human-readable-output.d.ts.map +1 -1
  99. package/dist/esm/visualization/human-readable-output.js +7 -2
  100. package/dist/esm/visualization/human-readable-output.js.map +1 -1
  101. package/dist/esm/visualization/json-output.d.ts +2 -1
  102. package/dist/esm/visualization/json-output.d.ts.map +1 -1
  103. package/dist/esm/visualization/json-output.js +2 -2
  104. package/dist/esm/visualization/json-output.js.map +1 -1
  105. package/dist/esm/visualization/mermaid-output.d.ts.map +1 -1
  106. package/dist/esm/visualization/mermaid-output.js +57 -16
  107. package/dist/esm/visualization/mermaid-output.js.map +1 -1
  108. package/package.json +31 -31
  109. package/dist/esm/ideal/add-nodes.d.ts +0 -34
  110. package/dist/esm/ideal/add-nodes.d.ts.map +0 -1
  111. package/dist/esm/ideal/add-nodes.js +0 -39
  112. package/dist/esm/ideal/add-nodes.js.map +0 -1
  113. package/dist/esm/ideal/remove-nodes.d.ts +0 -7
  114. package/dist/esm/ideal/remove-nodes.d.ts.map +0 -1
  115. package/dist/esm/ideal/remove-nodes.js +0 -19
  116. package/dist/esm/ideal/remove-nodes.js.map +0 -1
@@ -1,10 +1,12 @@
1
- import { joinDepIDTuple } from '@vltpkg/dep-id';
1
+ import { joinDepIDTuple, joinExtra } from '@vltpkg/dep-id';
2
2
  import { error } from '@vltpkg/error-cause';
3
3
  import { Spec } from '@vltpkg/spec';
4
4
  import { longDependencyTypes, normalizeManifest } from '@vltpkg/types';
5
- import { asDependency, shorten } from "../dependencies.js";
5
+ import { fixupAddedNames } from "../fixup-added-names.js";
6
+ import { shorten } from "../dependencies.js";
6
7
  import { removeOptionalSubgraph } from "../remove-optional-subgraph.js";
7
8
  import { extractNode } from "../reify/extract-node.js";
9
+ import { endPeerPlacement, postPlacementPeerCheck, startPeerPlacement, } from "./peers.js";
8
10
  /**
9
11
  * Only install devDeps for git dependencies and importers
10
12
  * Everything else always gets installed
@@ -40,9 +42,10 @@ const isStringArray = (a) => Array.isArray(a) && !a.some(b => typeof b !== 'stri
40
42
  /**
41
43
  * Fetch manifests for dependencies and create placement tasks.
42
44
  */
43
- const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry, modifierRefs, depth = 0) => {
45
+ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry, peerContext, modifierRefs, depth = 0) => {
44
46
  // Create fetch tasks for all dependencies at this level
45
47
  const fetchTasks = [];
48
+ const placementTasks = [];
46
49
  for (const { spec: originalSpec, type } of deps) {
47
50
  let spec = originalSpec;
48
51
  const fileTypeInfo = getFileTypeInfo(spec, fromNode, scurry);
@@ -60,31 +63,47 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
60
63
  continue;
61
64
  }
62
65
  }
66
+ const peer = type === 'peer' || type === 'peerOptional';
67
+ // skip reusing nodes for peer deps since their reusability
68
+ // is handled ahead-of-time during its parent's placement
63
69
  const existingNode = graph.findResolution(spec, fromNode, queryModifier);
64
- if (existingNode) {
65
- // For peerOptional dependencies, create a dangling edge instead of linking to existing node
66
- if (type === 'peerOptional') {
67
- /* c8 ignore next 3 */
68
- // This case happens when peerOptional dep already exists in graph
69
- graph.addEdge(type, spec, fromNode);
70
- }
71
- else {
72
- graph.addEdge(type, spec, fromNode, existingNode);
73
- }
70
+ // defines what nodes are eligible to be reused
71
+ const validExistingNode = existingNode &&
72
+ !existingNode.detached &&
73
+ // Regular deps can always reuse
74
+ /* c8 ignore start */
75
+ (!peer ||
76
+ // otherwise reusing peer deps only in case of a peerSetHash matche
77
+ existingNode.peerSetHash === fromNode.peerSetHash);
78
+ /* c8 ignore stop */
79
+ if (validExistingNode ||
80
+ // importers are handled at the ./add-nodes.ts top-level
81
+ // so we should just skip whenever we find one
82
+ existingNode?.importer) {
83
+ graph.addEdge(type, spec, fromNode, existingNode);
74
84
  continue;
75
85
  }
86
+ // is the current edge pointint go an optional dependency?
76
87
  const edgeOptional = type === 'optional' || type === 'peerOptional';
77
88
  // Start manifest fetch immediately for parallel processing
78
- const manifestPromise = packageInfo
79
- .manifest(spec, { from: scurry.resolve(fromNode.location) })
80
- .then(manifest => manifest)
81
- .catch((er) => {
82
- // optional deps ignored if inaccessible
83
- if (edgeOptional || fromNode.optional) {
84
- return undefined;
85
- }
86
- throw er;
87
- });
89
+ const manifestPromise =
90
+ // the "detached" node state means that it has already been load as
91
+ // part of a graph (either lockfile or actual) and it has valid manifest
92
+ // data so we shortcut the package info manifest fetch here
93
+ existingNode?.detached ?
94
+ Promise.resolve(existingNode.manifest)
95
+ // this is the entry point to fetch calls to retrieve manifests
96
+ // from the build ideal graph point of view
97
+ : packageInfo
98
+ .manifest(spec, { from: scurry.resolve(fromNode.location) })
99
+ .then(manifest => manifest)
100
+ .catch((er) => {
101
+ // optional deps ignored if inaccessible
102
+ if (edgeOptional || fromNode.optional) {
103
+ return undefined;
104
+ }
105
+ throw er;
106
+ });
88
107
  const fetchTask = {
89
108
  spec,
90
109
  type,
@@ -95,11 +114,11 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
95
114
  edgeOptional,
96
115
  manifestPromise,
97
116
  depth,
117
+ peerContext,
98
118
  };
99
119
  fetchTasks.push(fetchTask);
100
120
  }
101
- // Create placement tasks
102
- const placementTasks = [];
121
+ // Create placement tasks from fetch tasks
103
122
  for (const fetchTask of fetchTasks) {
104
123
  const manifest = await fetchTask.manifestPromise;
105
124
  placementTasks.push({
@@ -107,38 +126,46 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
107
126
  manifest,
108
127
  });
109
128
  }
129
+ // sort placement tasks: non-peer dependencies first, then peer dependencies
130
+ // so that peer dependencies can easily reuse already placed regular
131
+ // dependencies as part of peer context set resolution also makes sure to
132
+ // sort by the manifest name for deterministic order.
133
+ placementTasks.sort((a, b) => {
134
+ const aIsPeer = (a.manifest?.peerDependencies &&
135
+ Object.keys(a.manifest.peerDependencies).length > 0) ?
136
+ 1
137
+ : 0;
138
+ const bIsPeer = (b.manifest?.peerDependencies &&
139
+ Object.keys(b.manifest.peerDependencies).length > 0) ?
140
+ 1
141
+ : 0;
142
+ // regular dependencies first, peer dependencies last
143
+ if (aIsPeer !== bIsPeer) {
144
+ return aIsPeer - bIsPeer;
145
+ }
146
+ // if both are in the same group,
147
+ // sort alphabetically by manifest name (fallback to spec.name)
148
+ const aName = a.manifest?.name /* c8 ignore next */ || a.fetchTask.spec.name;
149
+ const bName = b.manifest?.name || b.fetchTask.spec.name;
150
+ return aName.localeCompare(bName, 'en');
151
+ });
110
152
  return placementTasks;
111
153
  };
112
154
  /**
113
- * Process placement tasks and collect child dependencies
155
+ * Process placement tasks and collect child dependencies, this is the
156
+ * second step of the appendNodes operation after manifest fetching in
157
+ * which the final graph data structure is actually built.
114
158
  */
115
- const processPlacementTasks = async (add, graph, options, placementTasks, modifiers, scurry, packageInfo, extractPromises, actual, seenExtracted, remover) => {
159
+ const processPlacementTasks = async (graph, options, placementTasks, add, modifiers, scurry, packageInfo, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove) => {
116
160
  const childDepsToProcess = [];
117
161
  for (const placementTask of placementTasks) {
118
162
  const { fetchTask, manifest } = placementTask;
119
- let { spec } = fetchTask;
120
- const type = fetchTask.type;
121
- const fromNode = fetchTask.fromNode;
122
- const fileTypeInfo = fetchTask.fileTypeInfo;
123
- const activeModifier = fetchTask.activeModifier;
124
- const queryModifier = fetchTask.queryModifier;
125
- const edgeOptional = fetchTask.edgeOptional;
126
- // Handle nameless dependencies
127
- if (manifest?.name && spec.name === '(unknown)') {
128
- const s = add.get(String(spec));
129
- if (s) {
130
- // removes the previous, placeholder entry key
131
- add.delete(String(spec));
132
- // replaces spec with a version with the correct name
133
- spec = Spec.parse(manifest.name, spec.bareSpec, options);
134
- // updates the add map with the fixed up spec
135
- const n = asDependency({
136
- ...s,
137
- spec,
138
- });
139
- add.set(manifest.name, n);
140
- }
141
- }
163
+ let { activeModifier, edgeOptional, fileTypeInfo, fromNode, peerContext, queryModifier, spec, type, } = fetchTask;
164
+ // fix the name in the `add` map when needed. This allows the upcoming
165
+ // reify step to properly update the package.json file dependencies
166
+ // using the correct names retrieved from the manifest data
167
+ const additiveMap = fromNode.importer ? add : transientAdd?.get(fromNode.id);
168
+ spec = fixupAddedNames(additiveMap, manifest, options, spec);
142
169
  // handles missing manifest resolution
143
170
  if (!manifest) {
144
171
  if (!edgeOptional && fromNode.isOptional()) {
@@ -159,14 +186,14 @@ const processPlacementTasks = async (add, graph, options, placementTasks, modifi
159
186
  });
160
187
  }
161
188
  }
162
- // Skip placing peerOptional dependencies, just create a dangling edge
163
- if (type === 'peerOptional') {
164
- /* c8 ignore next 3 */
165
- graph.addEdge(type, spec, fromNode);
166
- continue;
167
- }
189
+ // start peer deps placement process, populating the peer context with
190
+ // dependency data; adding the parent node deps and this manifest's
191
+ // peer deps references to the current peer context set
192
+ const peerPlacement = startPeerPlacement(peerContext, manifest, fromNode, options);
193
+ const peerSetHash = peerPlacement.peerSetHash;
194
+ const queuedEntries = peerPlacement.queuedEntries;
168
195
  // places a new node in the graph representing a newly seen dependency
169
- const node = graph.placePackage(fromNode, type, spec, normalizeManifest(manifest), fileTypeInfo?.id, queryModifier);
196
+ const node = graph.placePackage(fromNode, type, spec, normalizeManifest(manifest), fileTypeInfo?.id, joinExtra({ peerSetHash, modifier: queryModifier }));
170
197
  /* c8 ignore start - not possible, already ensured manifest */
171
198
  if (!node) {
172
199
  throw error('failed to place package', {
@@ -179,13 +206,20 @@ const processPlacementTasks = async (add, graph, options, placementTasks, modifi
179
206
  if (activeModifier) {
180
207
  modifiers?.updateActiveEntry(node, activeModifier);
181
208
  }
182
- // Extract the node if it doesn't exist in the actual graph and we have the necessary parameters
183
- if (remover &&
209
+ const eligibleForExtraction = type !== 'peer' &&
210
+ type !== 'peerOptional' &&
211
+ remover &&
184
212
  extractPromises &&
185
213
  actual &&
186
214
  scurry &&
187
215
  packageInfo &&
188
- node.inVltStore()) {
216
+ node.inVltStore() &&
217
+ !node.isOptional() &&
218
+ // this fixes an issue with installing `file:pathname` specs
219
+ /* c8 ignore next */ !fileTypeInfo?.isDirectory &&
220
+ !node.importer;
221
+ // extract the node if it meets the criteria for early extraction
222
+ if (eligibleForExtraction) {
189
223
  /* c8 ignore start */
190
224
  if (seenExtracted?.has(node.id)) {
191
225
  continue;
@@ -194,7 +228,7 @@ const processPlacementTasks = async (add, graph, options, placementTasks, modifi
194
228
  seenExtracted?.add(node.id);
195
229
  const actualNode = actual.nodes.get(node.id);
196
230
  if (!actualNode?.equals(node)) {
197
- // Extract the node without awaiting - push the promise to the array
231
+ // extract the node without awaiting - push the promise to the array
198
232
  const extractPromise = extractNode(node, scurry, remover, options, packageInfo);
199
233
  extractPromises.push(extractPromise);
200
234
  }
@@ -204,40 +238,87 @@ const processPlacementTasks = async (add, graph, options, placementTasks, modifi
204
238
  node.location = fileTypeInfo.path;
205
239
  }
206
240
  node.setResolved();
207
- // Collect child dependencies for processing in the next level
241
+ // collect child dependencies for processing in the next level
242
+ const nextPeerDeps = new Map();
243
+ // compute deps normally
208
244
  const bundleDeps = manifest.bundleDependencies;
209
245
  const bundled = new Set((node.id.startsWith('git') ||
210
246
  node.importer ||
211
247
  !isStringArray(bundleDeps)) ?
212
248
  []
213
249
  : bundleDeps);
214
- // recursively process all child dependencies in the manifest
250
+ // setup next level to process all child dependencies in the manifest
215
251
  const nextDeps = [];
252
+ // traverse actual dependency declarations in the manifest
253
+ // creating dependency entries for them
216
254
  for (const depTypeName of longDependencyTypes) {
217
255
  const depRecord = manifest[depTypeName];
218
256
  if (depRecord && shouldInstallDepType(node, depTypeName)) {
219
257
  for (const [name, bareSpec] of Object.entries(depRecord)) {
258
+ // might need to skip already placed peer deps here
220
259
  if (bundled.has(name))
221
260
  continue;
222
- nextDeps.push({
261
+ const dep = {
223
262
  type: shorten(depTypeName, name, manifest),
224
263
  spec: Spec.parse(name, bareSpec, {
225
264
  ...options,
226
265
  registry: spec.registry,
227
266
  }),
228
- });
267
+ };
268
+ if (depTypeName === 'peerDependencies') {
269
+ nextPeerDeps.set(name, dep);
270
+ }
271
+ else {
272
+ nextDeps.push(dep);
273
+ }
229
274
  }
230
275
  }
231
276
  }
232
- if (nextDeps.length > 0) {
233
- childDepsToProcess.push({
234
- node,
235
- deps: nextDeps,
236
- modifierRefs: modifiers?.tryDependencies(node, nextDeps),
237
- });
277
+ // Inject transient dependencies for non-importer nodes (nested folders)
278
+ // These are deps that were added from a nested folder context using
279
+ // relative file: specs that should resolve relative to that folder
280
+ const transientDeps = transientAdd?.get(node.id);
281
+ if (transientDeps) {
282
+ for (const [, dep] of transientDeps) {
283
+ if (dep.type === 'peer' || dep.type === 'peerOptional') {
284
+ nextPeerDeps.set(dep.spec.name, dep);
285
+ continue;
286
+ }
287
+ // remove the dependency from nextDeps if it already exists
288
+ const index = nextDeps.findIndex(d => d.spec.name === dep.spec.name);
289
+ if (index !== -1) {
290
+ nextDeps.splice(index, 1);
291
+ }
292
+ nextDeps.push(dep);
293
+ }
294
+ }
295
+ // Remove transient removals when needed
296
+ const transientRemovals = transientRemove?.get(node.id);
297
+ if (transientRemovals) {
298
+ for (const depName of transientRemovals) {
299
+ const index = nextDeps.findIndex(dep => dep.spec.name === depName);
300
+ if (index !== -1) {
301
+ nextDeps.splice(index, 1);
302
+ continue;
303
+ }
304
+ if (nextPeerDeps.has(depName)) {
305
+ nextPeerDeps.delete(depName);
306
+ }
307
+ }
238
308
  }
309
+ // finish peer placement for this node, resolving satisfied peers
310
+ // to seen nodes from the peer context and adding unsatisfied peers
311
+ // to `nextDeps` so they get processed along regular dependencies
312
+ const updateContext = endPeerPlacement(peerContext, nextDeps, nextPeerDeps, graph, spec, fromNode, node, type, queuedEntries);
313
+ childDepsToProcess.push({
314
+ node,
315
+ deps: nextDeps,
316
+ modifierRefs: modifiers?.tryDependencies(node, nextDeps),
317
+ peerContext,
318
+ updateContext,
319
+ });
239
320
  }
240
- return { childDepsToProcess };
321
+ return childDepsToProcess;
241
322
  };
242
323
  /**
243
324
  * Append new nodes in the given `graph` for dependencies specified at `add`
@@ -246,30 +327,54 @@ const processPlacementTasks = async (add, graph, options, placementTasks, modifi
246
327
  * It also applies any modifiers that applies to a given node as it processes
247
328
  * and builds the graph.
248
329
  */
249
- export const appendNodes = async (add, packageInfo, graph, fromNode, deps, scurry, options, seen, modifiers, modifierRefs, extractPromises, actual, seenExtracted, remover) => {
330
+ export const appendNodes = async (packageInfo, graph, fromNode, deps, scurry, options, seen, add, modifiers, modifierRefs, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove) => {
250
331
  /* c8 ignore next */
251
332
  if (seen.has(fromNode.id))
252
333
  return;
253
334
  seen.add(fromNode.id);
335
+ // Get the initial peer context from the graph
336
+ const [initialPeerContext] = graph.peerContexts;
337
+ /* c8 ignore start - impossible */
338
+ if (!initialPeerContext) {
339
+ throw error('no initial peer context found in graph');
340
+ }
341
+ /* c8 ignore stop */
254
342
  // Use a queue for breadth-first processing
255
343
  let currentLevelDeps = [
256
- { node: fromNode, deps, modifierRefs, depth: 0 },
344
+ {
345
+ node: fromNode,
346
+ deps,
347
+ modifierRefs,
348
+ depth: 0,
349
+ peerContext: initialPeerContext,
350
+ /* c8 ignore start */
351
+ updateContext: {
352
+ putEntries: () => undefined,
353
+ resolvePeerDeps: () => { },
354
+ },
355
+ /* c8 ignore stop */
356
+ },
257
357
  ];
258
358
  while (currentLevelDeps.length > 0) {
259
359
  const nextLevelDeps = [];
260
360
  // Process all nodes at the current level in parallel
261
- const levelResults = await Promise.all(currentLevelDeps.map(async ({ node, deps: nodeDeps, modifierRefs: nodeModifierRefs, depth, }) => {
361
+ const levelResults = await Promise.all(currentLevelDeps.map(async ({ node, deps: nodeDeps, modifierRefs: nodeModifierRefs, peerContext, depth, }) => {
262
362
  // Mark node as seen when we start processing its dependencies
263
363
  seen.add(node.id);
264
364
  // Fetch manifests for this node's dependencies
265
365
  const placementTasks = await fetchManifestsForDeps(packageInfo, graph, node,
266
366
  // Sort dependencies by spec.name for deterministic ordering
267
- nodeDeps.sort((a, b) => a.spec.name.localeCompare(b.spec.name, 'en')), scurry, nodeModifierRefs, depth);
367
+ nodeDeps.sort((a, b) => a.spec.name.localeCompare(b.spec.name, 'en')), scurry, peerContext, nodeModifierRefs, depth);
268
368
  // Process the placement tasks and get child dependencies
269
- return await processPlacementTasks(add, graph, options, placementTasks, modifiers, scurry, packageInfo, extractPromises, actual, seenExtracted, remover);
369
+ return await processPlacementTasks(graph, options, placementTasks, add, modifiers, scurry, packageInfo, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove);
270
370
  }));
371
+ // Traverse the queued up children dependencies, adding and tracking
372
+ // dependencies on the peer context set, forking the context as needed
373
+ // and resolving any peer dependency that is able to be resolved using
374
+ // the current peer context set
375
+ postPlacementPeerCheck(graph, levelResults);
271
376
  // Collect all child dependencies for the next level
272
- for (const { childDepsToProcess } of levelResults) {
377
+ for (const childDepsToProcess of levelResults) {
273
378
  for (const childDep of childDepsToProcess) {
274
379
  if (!seen.has(childDep.node.id)) {
275
380
  /* c8 ignore next */
@@ -1 +1 @@
1
- {"version":3,"file":"append-nodes.js","sourceRoot":"","sources":["../../../src/ideal/append-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAEnC,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAOtE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAI1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAMvE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAStD;;;GAGG;AACH,MAAM,oBAAoB,GAAG,CAC3B,IAAU,EACV,OAA2B,EAC3B,EAAE,CACF,OAAO,KAAK,iBAAiB;IAC7B,IAAI,CAAC,QAAQ;IACb,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;AAE3B;;GAEG;AACH,MAAM,eAAe,GAAG,CACtB,IAAU,EACV,QAAc,EACd,MAAkB,EACQ,EAAE;IAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAA;IACpB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;QAAE,OAAM;IAE7B,4CAA4C;IAC5C,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACZ,MAAM,KAAK,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACpD,CAAC;IACD,oBAAoB;IAEpB,uEAAuE;IACvE,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,EAAE,CAAA;IACnC,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;IAEzC,OAAO;QACL,IAAI;QACJ,EAAE;QACF,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE;KACjD,CAAA;AACH,CAAC,CAAA;AAED,MAAM,aAAa,GAAG,CAAC,CAAU,EAAiB,EAAE,CAClD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAA;AAsCzD;;GAEG;AACH,MAAM,qBAAqB,GAAG,KAAK,EACjC,WAA8B,EAC9B,KAAY,EACZ,QAAc,EACd,IAAkB,EAClB,MAAkB,EAClB,YAA+C,EAC/C,KAAK,GAAG,CAAC,EACqB,EAAE;IAChC,wDAAwD;IACxD,MAAM,UAAU,GAAwB,EAAE,CAAA;IAE1C,KAAK,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;QAChD,IAAI,IAAI,GAAG,YAAY,CAAA;QACvB,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC5D,MAAM,cAAc,GAAG,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEnD,iEAAiE;QACjE,MAAM,aAAa,GAAG,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAA;QACpD,MAAM,gBAAgB,GACpB,cAAc;YACd,cAAc,CAAC,qBAAqB,CAAC,OAAO;gBAC1C,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAA;QAC3C,IACE,aAAa;YACb,gBAAgB;YAChB,MAAM,IAAI,cAAc,CAAC,QAAQ,EACjC,CAAC;YACD,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAA;YACnC,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;gBAC1B,SAAQ;YACV,CAAC;QACH,CAAC;QAED,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,CACvC,IAAI,EACJ,QAAQ,EACR,aAAa,CACd,CAAA;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,4FAA4F;YAC5F,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;gBAC5B,sBAAsB;gBACtB,kEAAkE;gBAClE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACrC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;YACnD,CAAC;YACD,SAAQ;QACV,CAAC;QAED,MAAM,YAAY,GAChB,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,cAAc,CAAA;QAEhD,2DAA2D;QAC3D,MAAM,eAAe,GAAG,WAAW;aAChC,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;aAC3D,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAgC,CAAC;aAClD,KAAK,CAAC,CAAC,EAAW,EAAE,EAAE;YACrB,wCAAwC;YACxC,IAAI,YAAY,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACtC,OAAO,SAAS,CAAA;YAClB,CAAC;YACD,MAAM,EAAE,CAAA;QACV,CAAC,CAAC,CAAA;QAEJ,MAAM,SAAS,GAAsB;YACnC,IAAI;YACJ,IAAI;YACJ,QAAQ;YACR,YAAY;YACZ,cAAc;YACd,aAAa;YACb,YAAY;YACZ,eAAe;YACf,KAAK;SACN,CAAA;QAED,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAC5B,CAAC;IAED,yBAAyB;IACzB,MAAM,cAAc,GAAwB,EAAE,CAAA;IAC9C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,eAAe,CAAA;QAEhD,cAAc,CAAC,IAAI,CAAC;YAClB,SAAS;YACT,QAAQ;SACT,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,cAAc,CAAA;AACvB,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,qBAAqB,GAAG,KAAK,EACjC,GAA4B,EAC5B,KAAY,EACZ,OAAoB,EACpB,cAAmC,EACnC,SAAyB,EACzB,MAAmB,EACnB,WAA+B,EAC/B,eAA0C,EAC1C,MAAc,EACd,aAA0B,EAC1B,OAAwB,EAGvB,EAAE;IACH,MAAM,kBAAkB,GAAqC,EAAE,CAAA;IAE/D,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE,CAAC;QAC3C,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAA;QAC7C,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,CAAA;QACxB,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAA;QAC3B,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAA;QACnC,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAA;QAC3C,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,CAAA;QAC/C,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAA;QAC7C,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAA;QAE3C,+BAA+B;QAC/B,IAAI,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAChD,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/B,IAAI,CAAC,EAAE,CAAC;gBACN,8CAA8C;gBAC9C,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;gBACxB,qDAAqD;gBACrD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;gBACxD,6CAA6C;gBAC7C,MAAM,CAAC,GAAG,YAAY,CAAC;oBACrB,GAAG,CAAC;oBACJ,IAAI;iBACL,CAAC,CAAA;gBACF,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,YAAY,IAAI,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC;gBAC3C,8DAA8D;gBAC9D,kCAAkC;gBAClC,sBAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;gBACvC,SAAQ;YACV,CAAC;iBAAM,IAAI,YAAY,EAAE,CAAC;gBACxB,wDAAwD;gBACxD,iEAAiE;gBACjE,SAAQ;YACV,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,CAAC,8BAA8B,EAAE;oBAC1C,IAAI;oBACJ,IAAI,EAAE,QAAQ,CAAC,QAAQ;iBACxB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,sEAAsE;QACtE,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;YAC5B,sBAAsB;YACtB,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACnC,SAAQ;QACV,CAAC;QAED,sEAAsE;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAC7B,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,iBAAiB,CAAC,QAAQ,CAAC,EAC3B,YAAY,EAAE,EAAE,EAChB,aAAa,CACd,CAAA;QAED,8DAA8D;QAC9D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,KAAK,CAAC,yBAAyB,EAAE;gBACrC,IAAI,EAAE,QAAQ,CAAC,QAAQ;gBACvB,IAAI;aACL,CAAC,CAAA;QACJ,CAAC;QACD,oBAAoB;QAEpB,mCAAmC;QACnC,IAAI,cAAc,EAAE,CAAC;YACnB,SAAS,EAAE,iBAAiB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAA;QACpD,CAAC;QAED,gGAAgG;QAChG,IACE,OAAO;YACP,eAAe;YACf,MAAM;YACN,MAAM;YACN,WAAW;YACX,IAAI,CAAC,UAAU,EAAE,EACjB,CAAC;YACD,qBAAqB;YACrB,IAAI,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBAChC,SAAQ;YACV,CAAC;YACD,oBAAoB;YACpB,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC5C,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,oEAAoE;gBACpE,MAAM,cAAc,GAAG,WAAW,CAChC,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,EACP,WAAW,CACZ,CAAA;gBACD,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;YACtC,CAAC;QACH,CAAC;QAED,iCAAiC;QACjC,IAAI,YAAY,EAAE,IAAI,IAAI,YAAY,CAAC,WAAW,EAAE,CAAC;YACnD,IAAI,CAAC,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAA;QACnC,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAA;QAElB,8DAA8D;QAC9D,MAAM,UAAU,GAAG,QAAQ,CAAC,kBAAkB,CAAA;QAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,CACE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC;YACzB,IAAI,CAAC,QAAQ;YACb,CAAC,aAAa,CAAC,UAAU,CAAC,CAC3B,CAAC,CAAC;YACD,EAAE;YACJ,CAAC,CAAC,UAAU,CACb,CAAA;QAED,6DAA6D;QAC7D,MAAM,QAAQ,GAAiB,EAAE,CAAA;QAEjC,KAAK,MAAM,WAAW,IAAI,mBAAmB,EAAE,CAAC;YAC9C,MAAM,SAAS,GACb,QAAQ,CAAC,WAAW,CAAC,CAAA;YAEvB,IAAI,SAAS,IAAI,oBAAoB,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;gBACzD,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;oBACzD,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE,SAAQ;oBAC/B,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC;wBAC1C,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE;4BAC/B,GAAG,OAAO;4BACV,QAAQ,EAAE,IAAI,CAAC,QAAQ;yBACxB,CAAC;qBACH,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,kBAAkB,CAAC,IAAI,CAAC;gBACtB,IAAI;gBACJ,IAAI,EAAE,QAAQ;gBACd,YAAY,EAAE,SAAS,EAAE,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC;aACzD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAC/B,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC9B,GAA4B,EAC5B,WAA8B,EAC9B,KAAY,EACZ,QAAc,EACd,IAAkB,EAClB,MAAkB,EAClB,OAAoB,EACpB,IAAgB,EAChB,SAAyB,EACzB,YAA+C,EAC/C,eAA0C,EAC1C,MAAc,EACd,aAA0B,EAC1B,OAAwB,EACxB,EAAE;IACF,oBAAoB;IACpB,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAM;IACjC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAErB,2CAA2C;IAC3C,IAAI,gBAAgB,GAAsB;QACxC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE;KACjD,CAAA;IAED,OAAO,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,aAAa,GAAsB,EAAE,CAAA;QAE3C,qDAAqD;QACrD,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,GAAG,CACpC,gBAAgB,CAAC,GAAG,CAClB,KAAK,EAAE,EACL,IAAI,EACJ,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,gBAAgB,EAC9B,KAAK,GACW,EAAE,EAAE;YACpB,8DAA8D;YAC9D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAEjB,+CAA+C;YAC/C,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAChD,WAAW,EACX,KAAK,EACL,IAAI;YACJ,4DAA4D;YAC5D,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACrB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAC7C,EACD,MAAM,EACN,gBAAgB,EAChB,KAAK,CACN,CAAA;YAED,yDAAyD;YACzD,OAAO,MAAM,qBAAqB,CAChC,GAAG,EACH,KAAK,EACL,OAAO,EACP,cAAc,EACd,SAAS,EACT,MAAM,EACN,WAAW,EACX,eAAe,EACf,MAAM,EACN,aAAa,EACb,OAAO,CACR,CAAA;QACH,CAAC,CACF,CACF,CAAA;QAED,oDAAoD;QACpD,KAAK,MAAM,EAAE,kBAAkB,EAAE,IAAI,YAAY,EAAE,CAAC;YAClD,KAAK,MAAM,QAAQ,IAAI,kBAAkB,EAAE,CAAC;gBAC1C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBAChC,oBAAoB;oBACpB,MAAM,YAAY,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;oBACpD,aAAa,CAAC,IAAI,CAAC;wBACjB,GAAG,QAAQ;wBACX,KAAK,EAAE,YAAY,GAAG,CAAC;qBACxB,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,yBAAyB;QACzB,gBAAgB,GAAG,aAAa,CAAA;IAClC,CAAC;AACH,CAAC,CAAA","sourcesContent":["import { joinDepIDTuple } from '@vltpkg/dep-id'\nimport type { DepID } from '@vltpkg/dep-id'\nimport { error } from '@vltpkg/error-cause'\nimport type { PackageInfoClient } from '@vltpkg/package-info'\nimport { Spec } from '@vltpkg/spec'\nimport type { SpecOptions } from '@vltpkg/spec'\nimport { longDependencyTypes, normalizeManifest } from '@vltpkg/types'\nimport type {\n DependencyTypeLong,\n DependencySaveType,\n Manifest,\n} from '@vltpkg/types'\nimport type { PathScurry } from 'path-scurry'\nimport { asDependency, shorten } from '../dependencies.ts'\nimport type { Dependency } from '../dependencies.ts'\nimport type { Graph } from '../graph.ts'\nimport type { Node } from '../node.ts'\nimport { removeOptionalSubgraph } from '../remove-optional-subgraph.ts'\nimport type {\n GraphModifier,\n ModifierActiveEntry,\n} from '../modifiers.ts'\nimport type { ExtractResult } from '../reify/extract-node.ts'\nimport { extractNode } from '../reify/extract-node.ts'\nimport type { RollbackRemove } from '@vltpkg/rollback-remove'\n\ntype FileTypeInfo = {\n id: DepID\n path: string\n isDirectory: boolean\n}\n\n/**\n * Only install devDeps for git dependencies and importers\n * Everything else always gets installed\n */\nconst shouldInstallDepType = (\n node: Node,\n depType: DependencyTypeLong,\n) =>\n depType !== 'devDependencies' ||\n node.importer ||\n node.id.startsWith('git')\n\n/**\n * Retrieve the {@link DepID} and location for a `file:` type {@link Node}.\n */\nconst getFileTypeInfo = (\n spec: Spec,\n fromNode: Node,\n scurry: PathScurry,\n): FileTypeInfo | undefined => {\n const f = spec.final\n if (f.type !== 'file') return\n\n /* c8 ignore start - should be impossible */\n if (!f.file) {\n throw error('no path on file specifier', { spec })\n }\n /* c8 ignore stop */\n\n // Given that both linked folders and local tarballs (both defined with\n // usage of the `file:` spec prefix) location needs to be relative to their\n // parents, build the expected path and use it for both location and id\n const target = scurry.cwd.resolve(fromNode.location).resolve(f.file)\n const path = target.relativePosix()\n const id = joinDepIDTuple(['file', path])\n\n return {\n path,\n id,\n isDirectory: !!target.lstatSync()?.isDirectory(),\n }\n}\n\nconst isStringArray = (a: unknown): a is string[] =>\n Array.isArray(a) && !a.some(b => typeof b !== 'string')\n\n/**\n * Represents a manifest fetch operation with all the context needed.\n */\ntype ManifestFetchTask = {\n spec: Spec\n type: DependencySaveType\n fromNode: Node\n fileTypeInfo?: FileTypeInfo\n activeModifier?: ModifierActiveEntry\n queryModifier?: string\n edgeOptional: boolean\n manifestPromise: Promise<Manifest | undefined>\n depth: number\n}\n\n/**\n * Represents a node placement operation that depends on a resolved manifest.\n */\ntype NodePlacementTask = {\n fetchTask: ManifestFetchTask\n manifest: Manifest | undefined\n node?: Node\n childDeps?: Dependency[]\n childModifierRefs?: Map<string, ModifierActiveEntry>\n}\n\n/**\n * Represents an ongoing append operation for a node and its dependencies.\n */\ntype AppendNodeEntry = {\n node: Node\n deps: Dependency[]\n modifierRefs?: Map<string, ModifierActiveEntry>\n depth: number\n}\n\n/**\n * Fetch manifests for dependencies and create placement tasks.\n */\nconst fetchManifestsForDeps = async (\n packageInfo: PackageInfoClient,\n graph: Graph,\n fromNode: Node,\n deps: Dependency[],\n scurry: PathScurry,\n modifierRefs?: Map<string, ModifierActiveEntry>,\n depth = 0,\n): Promise<NodePlacementTask[]> => {\n // Create fetch tasks for all dependencies at this level\n const fetchTasks: ManifestFetchTask[] = []\n\n for (const { spec: originalSpec, type } of deps) {\n let spec = originalSpec\n const fileTypeInfo = getFileTypeInfo(spec, fromNode, scurry)\n const activeModifier = modifierRefs?.get(spec.name)\n\n // here is the place we swap specs if a edge modifier was defined\n const queryModifier = activeModifier?.modifier.query\n const completeModifier =\n activeModifier &&\n activeModifier.interactiveBreadcrumb.current ===\n activeModifier.modifier.breadcrumb.last\n if (\n queryModifier &&\n completeModifier &&\n 'spec' in activeModifier.modifier\n ) {\n spec = activeModifier.modifier.spec\n if (spec.bareSpec === '-') {\n continue\n }\n }\n\n const existingNode = graph.findResolution(\n spec,\n fromNode,\n queryModifier,\n )\n if (existingNode) {\n // For peerOptional dependencies, create a dangling edge instead of linking to existing node\n if (type === 'peerOptional') {\n /* c8 ignore next 3 */\n // This case happens when peerOptional dep already exists in graph\n graph.addEdge(type, spec, fromNode)\n } else {\n graph.addEdge(type, spec, fromNode, existingNode)\n }\n continue\n }\n\n const edgeOptional =\n type === 'optional' || type === 'peerOptional'\n\n // Start manifest fetch immediately for parallel processing\n const manifestPromise = packageInfo\n .manifest(spec, { from: scurry.resolve(fromNode.location) })\n .then(manifest => manifest as Manifest | undefined)\n .catch((er: unknown) => {\n // optional deps ignored if inaccessible\n if (edgeOptional || fromNode.optional) {\n return undefined\n }\n throw er\n })\n\n const fetchTask: ManifestFetchTask = {\n spec,\n type,\n fromNode,\n fileTypeInfo,\n activeModifier,\n queryModifier,\n edgeOptional,\n manifestPromise,\n depth,\n }\n\n fetchTasks.push(fetchTask)\n }\n\n // Create placement tasks\n const placementTasks: NodePlacementTask[] = []\n for (const fetchTask of fetchTasks) {\n const manifest = await fetchTask.manifestPromise\n\n placementTasks.push({\n fetchTask,\n manifest,\n })\n }\n\n return placementTasks\n}\n\n/**\n * Process placement tasks and collect child dependencies\n */\nconst processPlacementTasks = async (\n add: Map<string, Dependency>,\n graph: Graph,\n options: SpecOptions,\n placementTasks: NodePlacementTask[],\n modifiers?: GraphModifier,\n scurry?: PathScurry,\n packageInfo?: PackageInfoClient,\n extractPromises?: Promise<ExtractResult>[],\n actual?: Graph,\n seenExtracted?: Set<DepID>,\n remover?: RollbackRemove,\n): Promise<{\n childDepsToProcess: Omit<AppendNodeEntry, 'depth'>[]\n}> => {\n const childDepsToProcess: Omit<AppendNodeEntry, 'depth'>[] = []\n\n for (const placementTask of placementTasks) {\n const { fetchTask, manifest } = placementTask\n let { spec } = fetchTask\n const type = fetchTask.type\n const fromNode = fetchTask.fromNode\n const fileTypeInfo = fetchTask.fileTypeInfo\n const activeModifier = fetchTask.activeModifier\n const queryModifier = fetchTask.queryModifier\n const edgeOptional = fetchTask.edgeOptional\n\n // Handle nameless dependencies\n if (manifest?.name && spec.name === '(unknown)') {\n const s = add.get(String(spec))\n if (s) {\n // removes the previous, placeholder entry key\n add.delete(String(spec))\n // replaces spec with a version with the correct name\n spec = Spec.parse(manifest.name, spec.bareSpec, options)\n // updates the add map with the fixed up spec\n const n = asDependency({\n ...s,\n spec,\n })\n add.set(manifest.name, n)\n }\n }\n\n // handles missing manifest resolution\n if (!manifest) {\n if (!edgeOptional && fromNode.isOptional()) {\n // failed resolution of a non-optional dep of an optional node\n // have to clean up the dependents\n removeOptionalSubgraph(graph, fromNode)\n continue\n } else if (edgeOptional) {\n // failed resolution of an optional dep, just ignore it,\n // nothing to prune because we never added it in the first place.\n continue\n } else {\n throw error('failed to resolve dependency', {\n spec,\n from: fromNode.location,\n })\n }\n }\n\n // Skip placing peerOptional dependencies, just create a dangling edge\n if (type === 'peerOptional') {\n /* c8 ignore next 3 */\n graph.addEdge(type, spec, fromNode)\n continue\n }\n\n // places a new node in the graph representing a newly seen dependency\n const node = graph.placePackage(\n fromNode,\n type,\n spec,\n normalizeManifest(manifest),\n fileTypeInfo?.id,\n queryModifier,\n )\n\n /* c8 ignore start - not possible, already ensured manifest */\n if (!node) {\n throw error('failed to place package', {\n from: fromNode.location,\n spec,\n })\n }\n /* c8 ignore stop */\n\n // update the node modifier tracker\n if (activeModifier) {\n modifiers?.updateActiveEntry(node, activeModifier)\n }\n\n // Extract the node if it doesn't exist in the actual graph and we have the necessary parameters\n if (\n remover &&\n extractPromises &&\n actual &&\n scurry &&\n packageInfo &&\n node.inVltStore()\n ) {\n /* c8 ignore start */\n if (seenExtracted?.has(node.id)) {\n continue\n }\n /* c8 ignore stop */\n seenExtracted?.add(node.id)\n const actualNode = actual.nodes.get(node.id)\n if (!actualNode?.equals(node)) {\n // Extract the node without awaiting - push the promise to the array\n const extractPromise = extractNode(\n node,\n scurry,\n remover,\n options,\n packageInfo,\n )\n extractPromises.push(extractPromise)\n }\n }\n\n // updates graph node information\n if (fileTypeInfo?.path && fileTypeInfo.isDirectory) {\n node.location = fileTypeInfo.path\n }\n node.setResolved()\n\n // Collect child dependencies for processing in the next level\n const bundleDeps = manifest.bundleDependencies\n const bundled = new Set<string>(\n (\n node.id.startsWith('git') ||\n node.importer ||\n !isStringArray(bundleDeps)\n ) ?\n []\n : bundleDeps,\n )\n\n // recursively process all child dependencies in the manifest\n const nextDeps: Dependency[] = []\n\n for (const depTypeName of longDependencyTypes) {\n const depRecord: Record<string, string> | undefined =\n manifest[depTypeName]\n\n if (depRecord && shouldInstallDepType(node, depTypeName)) {\n for (const [name, bareSpec] of Object.entries(depRecord)) {\n if (bundled.has(name)) continue\n nextDeps.push({\n type: shorten(depTypeName, name, manifest),\n spec: Spec.parse(name, bareSpec, {\n ...options,\n registry: spec.registry,\n }),\n })\n }\n }\n }\n\n if (nextDeps.length > 0) {\n childDepsToProcess.push({\n node,\n deps: nextDeps,\n modifierRefs: modifiers?.tryDependencies(node, nextDeps),\n })\n }\n }\n\n return { childDepsToProcess }\n}\n\n/**\n * Append new nodes in the given `graph` for dependencies specified at `add`\n * and missing dependencies from the `deps` parameter.\n *\n * It also applies any modifiers that applies to a given node as it processes\n * and builds the graph.\n */\nexport const appendNodes = async (\n add: Map<string, Dependency>,\n packageInfo: PackageInfoClient,\n graph: Graph,\n fromNode: Node,\n deps: Dependency[],\n scurry: PathScurry,\n options: SpecOptions,\n seen: Set<DepID>,\n modifiers?: GraphModifier,\n modifierRefs?: Map<string, ModifierActiveEntry>,\n extractPromises?: Promise<ExtractResult>[],\n actual?: Graph,\n seenExtracted?: Set<DepID>,\n remover?: RollbackRemove,\n) => {\n /* c8 ignore next */\n if (seen.has(fromNode.id)) return\n seen.add(fromNode.id)\n\n // Use a queue for breadth-first processing\n let currentLevelDeps: AppendNodeEntry[] = [\n { node: fromNode, deps, modifierRefs, depth: 0 },\n ]\n\n while (currentLevelDeps.length > 0) {\n const nextLevelDeps: AppendNodeEntry[] = []\n\n // Process all nodes at the current level in parallel\n const levelResults = await Promise.all(\n currentLevelDeps.map(\n async ({\n node,\n deps: nodeDeps,\n modifierRefs: nodeModifierRefs,\n depth,\n }: AppendNodeEntry) => {\n // Mark node as seen when we start processing its dependencies\n seen.add(node.id)\n\n // Fetch manifests for this node's dependencies\n const placementTasks = await fetchManifestsForDeps(\n packageInfo,\n graph,\n node,\n // Sort dependencies by spec.name for deterministic ordering\n nodeDeps.sort((a, b) =>\n a.spec.name.localeCompare(b.spec.name, 'en'),\n ),\n scurry,\n nodeModifierRefs,\n depth,\n )\n\n // Process the placement tasks and get child dependencies\n return await processPlacementTasks(\n add,\n graph,\n options,\n placementTasks,\n modifiers,\n scurry,\n packageInfo,\n extractPromises,\n actual,\n seenExtracted,\n remover,\n )\n },\n ),\n )\n\n // Collect all child dependencies for the next level\n for (const { childDepsToProcess } of levelResults) {\n for (const childDep of childDepsToProcess) {\n if (!seen.has(childDep.node.id)) {\n /* c8 ignore next */\n const currentDepth = currentLevelDeps[0]?.depth ?? 0\n nextLevelDeps.push({\n ...childDep,\n depth: currentDepth + 1,\n })\n }\n }\n }\n\n // Move to the next level\n currentLevelDeps = nextLevelDeps\n }\n}\n"]}
1
+ {"version":3,"file":"append-nodes.js","sourceRoot":"","sources":["../../../src/ideal/append-nodes.ts"],"names":[],"mappings":"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","sourcesContent":["import { joinDepIDTuple, joinExtra } from '@vltpkg/dep-id'\nimport type { DepID } from '@vltpkg/dep-id'\nimport { error } from '@vltpkg/error-cause'\nimport type { PackageInfoClient } from '@vltpkg/package-info'\nimport { Spec } from '@vltpkg/spec'\nimport type { SpecOptions } from '@vltpkg/spec'\nimport { longDependencyTypes, normalizeManifest } from '@vltpkg/types'\nimport type {\n DependencyTypeLong,\n DependencySaveType,\n Manifest,\n} from '@vltpkg/types'\nimport type { PathScurry } from 'path-scurry'\nimport { fixupAddedNames } from '../fixup-added-names.ts'\nimport { shorten } from '../dependencies.ts'\nimport type { Dependency } from '../dependencies.ts'\nimport type { Graph } from '../graph.ts'\nimport type { Node } from '../node.ts'\nimport { removeOptionalSubgraph } from '../remove-optional-subgraph.ts'\nimport type {\n GraphModifier,\n ModifierActiveEntry,\n} from '../modifiers.ts'\nimport type { ExtractResult } from '../reify/extract-node.ts'\nimport { extractNode } from '../reify/extract-node.ts'\nimport type { RollbackRemove } from '@vltpkg/rollback-remove'\nimport {\n endPeerPlacement,\n postPlacementPeerCheck,\n startPeerPlacement,\n} from './peers.ts'\nimport type {\n PeerContext,\n AppendNodeEntry,\n ProcessPlacementResult,\n TransientAddMap,\n TransientRemoveMap,\n} from './types.ts'\n\ntype FileTypeInfo = {\n id: DepID\n path: string\n isDirectory: boolean\n}\n\n/**\n * Only install devDeps for git dependencies and importers\n * Everything else always gets installed\n */\nconst shouldInstallDepType = (\n node: Node,\n depType: DependencyTypeLong,\n) =>\n depType !== 'devDependencies' ||\n node.importer ||\n node.id.startsWith('git')\n\n/**\n * Retrieve the {@link DepID} and location for a `file:` type {@link Node}.\n */\nconst getFileTypeInfo = (\n spec: Spec,\n fromNode: Node,\n scurry: PathScurry,\n): FileTypeInfo | undefined => {\n const f = spec.final\n if (f.type !== 'file') return\n\n /* c8 ignore start - should be impossible */\n if (!f.file) {\n throw error('no path on file specifier', { spec })\n }\n /* c8 ignore stop */\n\n // Given that both linked folders and local tarballs (both defined with\n // usage of the `file:` spec prefix) location needs to be relative to their\n // parents, build the expected path and use it for both location and id\n const target = scurry.cwd.resolve(fromNode.location).resolve(f.file)\n const path = target.relativePosix()\n const id = joinDepIDTuple(['file', path])\n\n return {\n path,\n id,\n isDirectory: !!target.lstatSync()?.isDirectory(),\n }\n}\n\nconst isStringArray = (a: unknown): a is string[] =>\n Array.isArray(a) && !a.some(b => typeof b !== 'string')\n\n/**\n * Represents a manifest fetch operation with all the context needed.\n */\ntype ManifestFetchTask = {\n spec: Spec\n type: DependencySaveType\n fromNode: Node\n fileTypeInfo?: FileTypeInfo\n activeModifier?: ModifierActiveEntry\n queryModifier?: string\n edgeOptional: boolean\n manifestPromise: Promise<Manifest | undefined>\n depth: number\n peerContext: PeerContext\n}\n\n/**\n * Represents a node placement operation that depends on a resolved manifest.\n */\ntype NodePlacementTask = {\n fetchTask: ManifestFetchTask\n manifest: Manifest | undefined\n node?: Node\n childDeps?: Dependency[]\n childModifierRefs?: Map<string, ModifierActiveEntry>\n childPeerContext?: PeerContext\n}\n\n/**\n * Fetch manifests for dependencies and create placement tasks.\n */\nconst fetchManifestsForDeps = async (\n packageInfo: PackageInfoClient,\n graph: Graph,\n fromNode: Node,\n deps: Dependency[],\n scurry: PathScurry,\n peerContext: PeerContext,\n modifierRefs?: Map<string, ModifierActiveEntry>,\n depth = 0,\n): Promise<NodePlacementTask[]> => {\n // Create fetch tasks for all dependencies at this level\n const fetchTasks: ManifestFetchTask[] = []\n const placementTasks: NodePlacementTask[] = []\n\n for (const { spec: originalSpec, type } of deps) {\n let spec = originalSpec\n const fileTypeInfo = getFileTypeInfo(spec, fromNode, scurry)\n const activeModifier = modifierRefs?.get(spec.name)\n\n // here is the place we swap specs if a edge modifier was defined\n const queryModifier = activeModifier?.modifier.query\n const completeModifier =\n activeModifier &&\n activeModifier.interactiveBreadcrumb.current ===\n activeModifier.modifier.breadcrumb.last\n if (\n queryModifier &&\n completeModifier &&\n 'spec' in activeModifier.modifier\n ) {\n spec = activeModifier.modifier.spec\n if (spec.bareSpec === '-') {\n continue\n }\n }\n\n const peer = type === 'peer' || type === 'peerOptional'\n\n // skip reusing nodes for peer deps since their reusability\n // is handled ahead-of-time during its parent's placement\n const existingNode = graph.findResolution(\n spec,\n fromNode,\n queryModifier,\n )\n // defines what nodes are eligible to be reused\n const validExistingNode =\n existingNode &&\n !existingNode.detached &&\n // Regular deps can always reuse\n /* c8 ignore start */\n (!peer ||\n // otherwise reusing peer deps only in case of a peerSetHash matche\n existingNode.peerSetHash === fromNode.peerSetHash)\n /* c8 ignore stop */\n\n if (\n validExistingNode ||\n // importers are handled at the ./add-nodes.ts top-level\n // so we should just skip whenever we find one\n existingNode?.importer\n ) {\n graph.addEdge(type, spec, fromNode, existingNode)\n continue\n }\n\n // is the current edge pointint go an optional dependency?\n const edgeOptional =\n type === 'optional' || type === 'peerOptional'\n\n // Start manifest fetch immediately for parallel processing\n const manifestPromise =\n // the \"detached\" node state means that it has already been load as\n // part of a graph (either lockfile or actual) and it has valid manifest\n // data so we shortcut the package info manifest fetch here\n existingNode?.detached ?\n Promise.resolve(existingNode.manifest as Manifest | undefined)\n // this is the entry point to fetch calls to retrieve manifests\n // from the build ideal graph point of view\n : packageInfo\n .manifest(spec, { from: scurry.resolve(fromNode.location) })\n .then(manifest => manifest as Manifest | undefined)\n .catch((er: unknown) => {\n // optional deps ignored if inaccessible\n if (edgeOptional || fromNode.optional) {\n return undefined\n }\n throw er\n })\n\n const fetchTask: ManifestFetchTask = {\n spec,\n type,\n fromNode,\n fileTypeInfo,\n activeModifier,\n queryModifier,\n edgeOptional,\n manifestPromise,\n depth,\n peerContext,\n }\n\n fetchTasks.push(fetchTask)\n }\n\n // Create placement tasks from fetch tasks\n for (const fetchTask of fetchTasks) {\n const manifest = await fetchTask.manifestPromise\n\n placementTasks.push({\n fetchTask,\n manifest,\n })\n }\n\n // sort placement tasks: non-peer dependencies first, then peer dependencies\n // so that peer dependencies can easily reuse already placed regular\n // dependencies as part of peer context set resolution also makes sure to\n // sort by the manifest name for deterministic order.\n placementTasks.sort((a, b) => {\n const aIsPeer =\n (\n a.manifest?.peerDependencies &&\n Object.keys(a.manifest.peerDependencies).length > 0\n ) ?\n 1\n : 0\n const bIsPeer =\n (\n b.manifest?.peerDependencies &&\n Object.keys(b.manifest.peerDependencies).length > 0\n ) ?\n 1\n : 0\n\n // regular dependencies first, peer dependencies last\n if (aIsPeer !== bIsPeer) {\n return aIsPeer - bIsPeer\n }\n\n // if both are in the same group,\n // sort alphabetically by manifest name (fallback to spec.name)\n const aName =\n a.manifest?.name /* c8 ignore next */ || a.fetchTask.spec.name\n const bName = b.manifest?.name || b.fetchTask.spec.name\n return aName.localeCompare(bName, 'en')\n })\n\n return placementTasks\n}\n\n/**\n * Process placement tasks and collect child dependencies, this is the\n * second step of the appendNodes operation after manifest fetching in\n * which the final graph data structure is actually built.\n */\nconst processPlacementTasks = async (\n graph: Graph,\n options: SpecOptions,\n placementTasks: NodePlacementTask[],\n add?: Map<string, Dependency>,\n modifiers?: GraphModifier,\n scurry?: PathScurry,\n packageInfo?: PackageInfoClient,\n extractPromises?: Promise<ExtractResult>[],\n actual?: Graph,\n seenExtracted?: Set<DepID>,\n remover?: RollbackRemove,\n transientAdd?: TransientAddMap,\n transientRemove?: TransientRemoveMap,\n): Promise<ProcessPlacementResult> => {\n const childDepsToProcess: ProcessPlacementResult = []\n\n for (const placementTask of placementTasks) {\n const { fetchTask, manifest } = placementTask\n let {\n activeModifier,\n edgeOptional,\n fileTypeInfo,\n fromNode,\n peerContext,\n queryModifier,\n spec,\n type,\n } = fetchTask\n\n // fix the name in the `add` map when needed. This allows the upcoming\n // reify step to properly update the package.json file dependencies\n // using the correct names retrieved from the manifest data\n const additiveMap =\n fromNode.importer ? add : transientAdd?.get(fromNode.id)\n spec = fixupAddedNames(additiveMap, manifest, options, spec)\n\n // handles missing manifest resolution\n if (!manifest) {\n if (!edgeOptional && fromNode.isOptional()) {\n // failed resolution of a non-optional dep of an optional node\n // have to clean up the dependents\n removeOptionalSubgraph(graph, fromNode)\n continue\n } else if (edgeOptional) {\n // failed resolution of an optional dep, just ignore it,\n // nothing to prune because we never added it in the first place.\n continue\n } else {\n throw error('failed to resolve dependency', {\n spec,\n from: fromNode.location,\n })\n }\n }\n\n // start peer deps placement process, populating the peer context with\n // dependency data; adding the parent node deps and this manifest's\n // peer deps references to the current peer context set\n const peerPlacement = startPeerPlacement(\n peerContext,\n manifest,\n fromNode,\n options,\n )\n const peerSetHash = peerPlacement.peerSetHash\n const queuedEntries = peerPlacement.queuedEntries\n\n // places a new node in the graph representing a newly seen dependency\n const node = graph.placePackage(\n fromNode,\n type,\n spec,\n normalizeManifest(manifest),\n fileTypeInfo?.id,\n joinExtra({ peerSetHash, modifier: queryModifier }),\n )\n\n /* c8 ignore start - not possible, already ensured manifest */\n if (!node) {\n throw error('failed to place package', {\n from: fromNode.location,\n spec,\n })\n }\n /* c8 ignore stop */\n\n // update the node modifier tracker\n if (activeModifier) {\n modifiers?.updateActiveEntry(node, activeModifier)\n }\n\n const eligibleForExtraction =\n type !== 'peer' &&\n type !== 'peerOptional' &&\n remover &&\n extractPromises &&\n actual &&\n scurry &&\n packageInfo &&\n node.inVltStore() &&\n !node.isOptional() &&\n // this fixes an issue with installing `file:pathname` specs\n /* c8 ignore next */ !fileTypeInfo?.isDirectory &&\n !node.importer\n\n // extract the node if it meets the criteria for early extraction\n if (eligibleForExtraction) {\n /* c8 ignore start */\n if (seenExtracted?.has(node.id)) {\n continue\n }\n /* c8 ignore stop */\n seenExtracted?.add(node.id)\n const actualNode = actual.nodes.get(node.id)\n if (!actualNode?.equals(node)) {\n // extract the node without awaiting - push the promise to the array\n const extractPromise = extractNode(\n node,\n scurry,\n remover,\n options,\n packageInfo,\n )\n extractPromises.push(extractPromise)\n }\n }\n\n // updates graph node information\n if (fileTypeInfo?.path && fileTypeInfo.isDirectory) {\n node.location = fileTypeInfo.path\n }\n node.setResolved()\n\n // collect child dependencies for processing in the next level\n const nextPeerDeps = new Map<string, Dependency>()\n\n // compute deps normally\n const bundleDeps = manifest.bundleDependencies\n const bundled = new Set<string>(\n (\n node.id.startsWith('git') ||\n node.importer ||\n !isStringArray(bundleDeps)\n ) ?\n []\n : bundleDeps,\n )\n\n // setup next level to process all child dependencies in the manifest\n const nextDeps: Dependency[] = []\n\n // traverse actual dependency declarations in the manifest\n // creating dependency entries for them\n for (const depTypeName of longDependencyTypes) {\n const depRecord: Record<string, string> | undefined =\n manifest[depTypeName]\n\n if (depRecord && shouldInstallDepType(node, depTypeName)) {\n for (const [name, bareSpec] of Object.entries(depRecord)) {\n // might need to skip already placed peer deps here\n if (bundled.has(name)) continue\n const dep = {\n type: shorten(depTypeName, name, manifest),\n spec: Spec.parse(name, bareSpec, {\n ...options,\n registry: spec.registry,\n }),\n }\n if (depTypeName === 'peerDependencies') {\n nextPeerDeps.set(name, dep)\n } else {\n nextDeps.push(dep)\n }\n }\n }\n }\n\n // Inject transient dependencies for non-importer nodes (nested folders)\n // These are deps that were added from a nested folder context using\n // relative file: specs that should resolve relative to that folder\n const transientDeps = transientAdd?.get(node.id)\n if (transientDeps) {\n for (const [, dep] of transientDeps) {\n if (dep.type === 'peer' || dep.type === 'peerOptional') {\n nextPeerDeps.set(dep.spec.name, dep)\n continue\n }\n\n // remove the dependency from nextDeps if it already exists\n const index = nextDeps.findIndex(\n d => d.spec.name === dep.spec.name,\n )\n if (index !== -1) {\n nextDeps.splice(index, 1)\n }\n\n nextDeps.push(dep)\n }\n }\n\n // Remove transient removals when needed\n const transientRemovals = transientRemove?.get(node.id)\n if (transientRemovals) {\n for (const depName of transientRemovals) {\n const index = nextDeps.findIndex(\n dep => dep.spec.name === depName,\n )\n if (index !== -1) {\n nextDeps.splice(index, 1)\n continue\n }\n\n if (nextPeerDeps.has(depName)) {\n nextPeerDeps.delete(depName)\n }\n }\n }\n\n // finish peer placement for this node, resolving satisfied peers\n // to seen nodes from the peer context and adding unsatisfied peers\n // to `nextDeps` so they get processed along regular dependencies\n const updateContext = endPeerPlacement(\n peerContext,\n nextDeps,\n nextPeerDeps,\n graph,\n spec,\n fromNode,\n node,\n type,\n queuedEntries,\n )\n\n childDepsToProcess.push({\n node,\n deps: nextDeps,\n modifierRefs: modifiers?.tryDependencies(node, nextDeps),\n peerContext,\n updateContext,\n })\n }\n\n return childDepsToProcess\n}\n\n/**\n * Append new nodes in the given `graph` for dependencies specified at `add`\n * and missing dependencies from the `deps` parameter.\n *\n * It also applies any modifiers that applies to a given node as it processes\n * and builds the graph.\n */\nexport const appendNodes = async (\n packageInfo: PackageInfoClient,\n graph: Graph,\n fromNode: Node,\n deps: Dependency[],\n scurry: PathScurry,\n options: SpecOptions,\n seen: Set<DepID>,\n add?: Map<string, Dependency>,\n modifiers?: GraphModifier,\n modifierRefs?: Map<string, ModifierActiveEntry>,\n extractPromises?: Promise<ExtractResult>[],\n actual?: Graph,\n seenExtracted?: Set<DepID>,\n remover?: RollbackRemove,\n transientAdd?: TransientAddMap,\n transientRemove?: TransientRemoveMap,\n) => {\n /* c8 ignore next */\n if (seen.has(fromNode.id)) return\n seen.add(fromNode.id)\n\n // Get the initial peer context from the graph\n const [initialPeerContext] = graph.peerContexts\n /* c8 ignore start - impossible */\n if (!initialPeerContext) {\n throw error('no initial peer context found in graph')\n }\n /* c8 ignore stop */\n\n // Use a queue for breadth-first processing\n let currentLevelDeps: AppendNodeEntry[] = [\n {\n node: fromNode,\n deps,\n modifierRefs,\n depth: 0,\n peerContext: initialPeerContext,\n /* c8 ignore start */\n updateContext: {\n putEntries: () => undefined,\n resolvePeerDeps: () => {},\n },\n /* c8 ignore stop */\n },\n ]\n\n while (currentLevelDeps.length > 0) {\n const nextLevelDeps: AppendNodeEntry[] = []\n\n // Process all nodes at the current level in parallel\n const levelResults = await Promise.all(\n currentLevelDeps.map(\n async ({\n node,\n deps: nodeDeps,\n modifierRefs: nodeModifierRefs,\n peerContext,\n depth,\n }: AppendNodeEntry) => {\n // Mark node as seen when we start processing its dependencies\n seen.add(node.id)\n\n // Fetch manifests for this node's dependencies\n const placementTasks = await fetchManifestsForDeps(\n packageInfo,\n graph,\n node,\n // Sort dependencies by spec.name for deterministic ordering\n nodeDeps.sort((a, b) =>\n a.spec.name.localeCompare(b.spec.name, 'en'),\n ),\n scurry,\n peerContext,\n nodeModifierRefs,\n depth,\n )\n\n // Process the placement tasks and get child dependencies\n return await processPlacementTasks(\n graph,\n options,\n placementTasks,\n add,\n modifiers,\n scurry,\n packageInfo,\n extractPromises,\n actual,\n seenExtracted,\n remover,\n transientAdd,\n transientRemove,\n )\n },\n ),\n )\n\n // Traverse the queued up children dependencies, adding and tracking\n // dependencies on the peer context set, forking the context as needed\n // and resolving any peer dependency that is able to be resolved using\n // the current peer context set\n postPlacementPeerCheck(graph, levelResults)\n\n // Collect all child dependencies for the next level\n for (const childDepsToProcess of levelResults) {\n for (const childDep of childDepsToProcess) {\n if (!seen.has(childDep.node.id)) {\n /* c8 ignore next */\n const currentDepth = currentLevelDeps[0]?.depth ?? 0\n nextLevelDeps.push({\n ...childDep,\n depth: currentDepth + 1,\n })\n }\n }\n }\n\n // Move to the next level\n currentLevelDeps = nextLevelDeps\n }\n}\n"]}
@@ -1,8 +1,8 @@
1
+ import type { PackageJson } from '@vltpkg/package-json';
2
+ import type { RefreshIdealGraphOptions } from './refresh-ideal-graph.ts';
1
3
  import type { Graph } from '../graph.ts';
2
- import type { AddNodesOptions } from './add-nodes.ts';
3
- import type { RemoveNodesOptions } from './remove-nodes.ts';
4
- export type BuildIdealFromStartingGraphOptions = AddNodesOptions & RemoveNodesOptions & {
5
- projectRoot: string;
4
+ export type BuildIdealFromStartingGraphOptions = RefreshIdealGraphOptions & {
5
+ packageJson: PackageJson;
6
6
  };
7
7
  /**
8
8
  * Builds an ideal {@link Graph} representing the dependencies that
@@ -1 +1 @@
1
- {"version":3,"file":"build-ideal-from-starting-graph.d.ts","sourceRoot":"","sources":["../../../src/ideal/build-ideal-from-starting-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D,MAAM,MAAM,kCAAkC,GAAG,eAAe,GAC9D,kBAAkB,GAAG;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,YAC7B,kCAAkC,KAC1C,OAAO,CAAC,KAAK,CAoDf,CAAA"}
1
+ {"version":3,"file":"build-ideal-from-starting-graph.d.ts","sourceRoot":"","sources":["../../../src/ideal/build-ideal-from-starting-graph.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AACxE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,MAAM,kCAAkC,GAC5C,wBAAwB,GAAG;IACzB,WAAW,EAAE,WAAW,CAAA;CACzB,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,YAC7B,kCAAkC,KAC1C,OAAO,CAAC,KAAK,CAmEf,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { getImporterSpecs } from "./get-importer-specs.js";
2
- import { addNodes } from "./add-nodes.js";
3
- import { removeNodes } from "./remove-nodes.js";
2
+ import { refreshIdealGraph } from "./refresh-ideal-graph.js";
3
+ import { resolveSaveType } from "../resolve-save-type.js";
4
4
  /**
5
5
  * Builds an ideal {@link Graph} representing the dependencies that
6
6
  * should be present in order to fulfill the requirements defined
@@ -21,34 +21,49 @@ export const buildIdealFromStartingGraph = async (options) => {
21
21
  options.remove.modifiedDependencies =
22
22
  options.remove.modifiedDependencies ||
23
23
  importerSpecs.remove.modifiedDependencies;
24
- // merge values found on importer specs with
24
+ // merge values found on node specs with
25
25
  // user-provided values from `options.add`
26
- for (const [importerId, deps] of importerSpecs.add) {
27
- if (!options.add.has(importerId)) {
28
- options.add.set(importerId, deps);
26
+ for (const [nodeId, deps] of importerSpecs.add) {
27
+ const node = options.graph.nodes.get(nodeId);
28
+ /* c8 ignore next - impossible */
29
+ if (!node)
30
+ continue;
31
+ if (!options.add.has(nodeId)) {
32
+ options.add.set(nodeId, deps);
29
33
  continue;
30
34
  }
31
- // merge any deps found when reading the importers manifest
35
+ // merge any deps found when reading the nodes manifest
32
36
  // with the ones provided by the user in the `add` options,
33
37
  // user-provided deps should take precedence
34
38
  for (const [depName, dep] of deps) {
35
- if (!options.add.get(importerId)?.has(depName)) {
36
- options.add.get(importerId)?.set(depName, dep);
39
+ if (!options.add.get(nodeId)?.has(depName)) {
40
+ options.add.get(nodeId)?.set(depName, dep);
37
41
  }
42
+ // update the save type for deps when using an implicit type
43
+ dep.type = resolveSaveType(node, depName, dep.type);
38
44
  }
39
45
  }
40
- // hydrate the resolution cache
41
- if (options.add.modifiedDependencies) {
42
- options.graph.resetResolution();
43
- // add nodes, fetching remote manifests for each dependency to be added
44
- await addNodes(options);
45
- // move things into their default locations, if possible
46
- for (const node of options.graph.nodes.values()) {
47
- node.setDefaultLocation();
46
+ // merge values found on node specs with
47
+ // user-provided values from `options.remove`
48
+ for (const [nodeId, deps] of importerSpecs.remove) {
49
+ if (!options.remove.has(nodeId)) {
50
+ options.remove.set(nodeId, deps);
51
+ continue;
52
+ }
53
+ // merge any deps found when reading the nodes manifest
54
+ // with the ones provided by the user in the `remove` options
55
+ for (const depName of deps) {
56
+ options.remove.get(nodeId)?.add(depName);
48
57
  }
49
58
  }
50
- // removes any dependencies that are listed in the `remove` option
51
- removeNodes({ ...options, remove: importerSpecs.remove });
59
+ // refreshs the current graph adding the nodes marked for addition
60
+ // and removing the ones marked for removal, while also recalculating
61
+ // peer dependencies and default locations
62
+ await refreshIdealGraph({
63
+ ...options,
64
+ transientAdd: importerSpecs.transientAdd,
65
+ transientRemove: importerSpecs.transientRemove,
66
+ });
52
67
  options.graph.gc();
53
68
  return options.graph;
54
69
  };