@vltpkg/graph 1.0.0-rc.9 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -22
- package/dist/{esm/actual → actual}/load.d.ts +8 -1
- package/dist/{esm/actual → actual}/load.js +29 -6
- package/dist/{esm/browser.d.ts → browser.d.ts} +0 -1
- package/dist/{esm/browser.js → browser.js} +0 -1
- package/dist/{esm/build.d.ts → build.d.ts} +0 -1
- package/dist/{esm/build.js → build.js} +0 -1
- package/dist/{esm/dependencies.d.ts → dependencies.d.ts} +0 -1
- package/dist/{esm/dependencies.js → dependencies.js} +3 -2
- package/dist/{esm/diff.d.ts → diff.d.ts} +2 -1
- package/dist/{esm/diff.js → diff.js} +5 -2
- package/dist/{esm/edge.d.ts → edge.d.ts} +0 -1
- package/dist/{esm/edge.js → edge.js} +0 -1
- package/dist/{esm/fixup-added-names.d.ts → fixup-added-names.d.ts} +4 -2
- package/dist/{esm/fixup-added-names.js → fixup-added-names.js} +16 -1
- package/dist/{esm/graph.d.ts → graph.d.ts} +13 -1
- package/dist/{esm/graph.js → graph.js} +49 -12
- package/dist/{esm/ideal → ideal}/append-nodes.d.ts +14 -3
- package/dist/{esm/ideal → ideal}/append-nodes.js +252 -64
- package/dist/{esm/ideal → ideal}/build-ideal-from-starting-graph.d.ts +0 -1
- package/dist/{esm/ideal → ideal}/build-ideal-from-starting-graph.js +0 -1
- package/dist/{esm/ideal → ideal}/build.d.ts +0 -1
- package/dist/{esm/ideal → ideal}/build.js +15 -2
- package/dist/{esm/ideal → ideal}/get-importer-specs.d.ts +0 -1
- package/dist/{esm/ideal → ideal}/get-importer-specs.js +13 -2
- package/dist/ideal/peers.d.ts +160 -0
- package/dist/ideal/peers.js +696 -0
- package/dist/{esm/ideal → ideal}/refresh-ideal-graph.d.ts +0 -5
- package/dist/{esm/ideal → ideal}/refresh-ideal-graph.js +11 -26
- package/dist/{esm/ideal → ideal}/remove-satisfied-specs.d.ts +0 -1
- package/dist/{esm/ideal → ideal}/remove-satisfied-specs.js +8 -1
- package/dist/ideal/sorting.d.ts +45 -0
- package/dist/ideal/sorting.js +70 -0
- package/dist/{esm/ideal → ideal}/types.d.ts +1 -6
- package/dist/ideal/types.js +1 -0
- package/dist/{esm/index.d.ts → index.d.ts} +0 -1
- package/dist/{esm/index.js → index.js} +0 -1
- package/dist/{esm/install.d.ts → install.d.ts} +2 -1
- package/dist/{esm/install.js → install.js} +24 -4
- package/dist/{esm/lockfile → lockfile}/load-edges.d.ts +0 -1
- package/dist/{esm/lockfile → lockfile}/load-edges.js +16 -6
- package/dist/{esm/lockfile → lockfile}/load-nodes.d.ts +0 -1
- package/dist/{esm/lockfile → lockfile}/load-nodes.js +6 -3
- package/dist/{esm/lockfile → lockfile}/load.d.ts +0 -1
- package/dist/lockfile/load.js +171 -0
- package/dist/{esm/lockfile → lockfile}/save.d.ts +1 -2
- package/dist/{esm/lockfile → lockfile}/save.js +17 -11
- package/dist/{esm/lockfile → lockfile}/types.d.ts +7 -1
- package/dist/{esm/lockfile → lockfile}/types.js +6 -1
- package/dist/{esm/modifiers.d.ts → modifiers.d.ts} +0 -1
- package/dist/{esm/modifiers.js → modifiers.js} +0 -1
- package/dist/{esm/node.d.ts → node.d.ts} +2 -1
- package/dist/{esm/node.js → node.js} +5 -1
- package/dist/{esm/non-empty-list.d.ts → non-empty-list.d.ts} +0 -1
- package/dist/{esm/non-empty-list.js → non-empty-list.js} +0 -1
- package/dist/path-security-error.d.ts +5 -0
- package/dist/path-security-error.js +13 -0
- package/dist/{esm/reify → reify}/add-edge.d.ts +0 -1
- package/dist/{esm/reify → reify}/add-edge.js +10 -4
- package/dist/{esm/reify → reify}/add-edges.d.ts +0 -1
- package/dist/{esm/reify → reify}/add-edges.js +0 -1
- package/dist/{esm/reify → reify}/add-nodes.d.ts +0 -1
- package/dist/{esm/reify → reify}/add-nodes.js +0 -1
- package/dist/{esm/reify → reify}/bin-chmod.d.ts +0 -1
- package/dist/{esm/reify → reify}/bin-chmod.js +0 -1
- package/dist/{esm/reify → reify}/build.d.ts +0 -1
- package/dist/{esm/reify → reify}/build.js +12 -4
- package/dist/{esm/reify → reify}/calculate-save-value.d.ts +1 -2
- package/dist/{esm/reify → reify}/calculate-save-value.js +11 -5
- package/dist/{esm/reify → reify}/check-needed-build.d.ts +10 -1
- package/dist/reify/check-needed-build.js +71 -0
- package/dist/{esm/reify → reify}/delete-edge.d.ts +0 -1
- package/dist/{esm/reify → reify}/delete-edge.js +0 -1
- package/dist/{esm/reify → reify}/delete-edges.d.ts +0 -1
- package/dist/{esm/reify → reify}/delete-edges.js +0 -1
- package/dist/{esm/reify → reify}/delete-nodes.d.ts +0 -1
- package/dist/{esm/reify → reify}/delete-nodes.js +0 -1
- package/dist/{esm/reify → reify}/extract-node.d.ts +0 -1
- package/dist/{esm/reify → reify}/extract-node.js +10 -3
- package/dist/{esm/reify → reify}/index.d.ts +3 -1
- package/dist/{esm/reify → reify}/index.js +25 -5
- package/dist/{esm/reify → reify}/internal-hoist.d.ts +0 -1
- package/dist/{esm/reify → reify}/internal-hoist.js +0 -1
- package/dist/{esm/reify → reify}/optional-fail.d.ts +0 -1
- package/dist/{esm/reify → reify}/optional-fail.js +0 -1
- package/dist/{esm/reify → reify}/rollback.d.ts +0 -1
- package/dist/{esm/reify → reify}/rollback.js +0 -1
- package/dist/{esm/reify → reify}/update-importers-package-json.d.ts +11 -2
- package/dist/{esm/reify → reify}/update-importers-package-json.js +4 -5
- package/dist/{esm/remove-optional-subgraph.d.ts → remove-optional-subgraph.d.ts} +0 -1
- package/dist/{esm/remove-optional-subgraph.js → remove-optional-subgraph.js} +0 -1
- package/dist/{esm/resolve-save-type.d.ts → resolve-save-type.d.ts} +0 -1
- package/dist/{esm/resolve-save-type.js → resolve-save-type.js} +0 -1
- package/dist/{esm/stringify-node.d.ts → stringify-node.d.ts} +0 -1
- package/dist/{esm/stringify-node.js → stringify-node.js} +0 -1
- package/dist/{esm/transfer-data → transfer-data}/load.d.ts +0 -1
- package/dist/{esm/transfer-data → transfer-data}/load.js +8 -5
- package/dist/{esm/uninstall.d.ts → uninstall.d.ts} +0 -1
- package/dist/{esm/uninstall.js → uninstall.js} +17 -7
- package/dist/{esm/update.d.ts → update.d.ts} +0 -1
- package/dist/{esm/update.js → update.js} +1 -1
- package/dist/{esm/virtual-root.d.ts → virtual-root.d.ts} +0 -1
- package/dist/{esm/virtual-root.js → virtual-root.js} +0 -1
- package/dist/{esm/visualization → visualization}/human-readable-output.d.ts +0 -1
- package/dist/{esm/visualization → visualization}/human-readable-output.js +0 -1
- package/dist/{esm/visualization → visualization}/json-output.d.ts +0 -1
- package/dist/{esm/visualization → visualization}/json-output.js +0 -1
- package/dist/{esm/visualization → visualization}/mermaid-output.d.ts +2 -2
- package/dist/{esm/visualization → visualization}/mermaid-output.js +27 -21
- package/dist/{esm/visualization → visualization}/object-like-output.d.ts +0 -1
- package/dist/{esm/visualization → visualization}/object-like-output.js +0 -1
- package/package.json +57 -62
- package/dist/esm/actual/load.d.ts.map +0 -1
- package/dist/esm/actual/load.js.map +0 -1
- package/dist/esm/browser.d.ts.map +0 -1
- package/dist/esm/browser.js.map +0 -1
- package/dist/esm/build.d.ts.map +0 -1
- package/dist/esm/build.js.map +0 -1
- package/dist/esm/dependencies.d.ts.map +0 -1
- package/dist/esm/dependencies.js.map +0 -1
- package/dist/esm/diff.d.ts.map +0 -1
- package/dist/esm/diff.js.map +0 -1
- package/dist/esm/edge.d.ts.map +0 -1
- package/dist/esm/edge.js.map +0 -1
- package/dist/esm/fixup-added-names.d.ts.map +0 -1
- package/dist/esm/fixup-added-names.js.map +0 -1
- package/dist/esm/graph.d.ts.map +0 -1
- package/dist/esm/graph.js.map +0 -1
- package/dist/esm/ideal/append-nodes.d.ts.map +0 -1
- package/dist/esm/ideal/append-nodes.js.map +0 -1
- package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts.map +0 -1
- package/dist/esm/ideal/build-ideal-from-starting-graph.js.map +0 -1
- package/dist/esm/ideal/build.d.ts.map +0 -1
- package/dist/esm/ideal/build.js.map +0 -1
- package/dist/esm/ideal/get-importer-specs.d.ts.map +0 -1
- package/dist/esm/ideal/get-importer-specs.js.map +0 -1
- package/dist/esm/ideal/get-ordered-dependencies.d.ts +0 -10
- package/dist/esm/ideal/get-ordered-dependencies.d.ts.map +0 -1
- package/dist/esm/ideal/get-ordered-dependencies.js +0 -42
- package/dist/esm/ideal/get-ordered-dependencies.js.map +0 -1
- package/dist/esm/ideal/peers.d.ts +0 -71
- package/dist/esm/ideal/peers.d.ts.map +0 -1
- package/dist/esm/ideal/peers.js +0 -318
- package/dist/esm/ideal/peers.js.map +0 -1
- package/dist/esm/ideal/refresh-ideal-graph.d.ts.map +0 -1
- package/dist/esm/ideal/refresh-ideal-graph.js.map +0 -1
- package/dist/esm/ideal/remove-satisfied-specs.d.ts.map +0 -1
- package/dist/esm/ideal/remove-satisfied-specs.js.map +0 -1
- package/dist/esm/ideal/types.d.ts.map +0 -1
- package/dist/esm/ideal/types.js +0 -2
- package/dist/esm/ideal/types.js.map +0 -1
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/install.d.ts.map +0 -1
- package/dist/esm/install.js.map +0 -1
- package/dist/esm/lockfile/load-edges.d.ts.map +0 -1
- package/dist/esm/lockfile/load-edges.js.map +0 -1
- package/dist/esm/lockfile/load-nodes.d.ts.map +0 -1
- package/dist/esm/lockfile/load-nodes.js.map +0 -1
- package/dist/esm/lockfile/load.d.ts.map +0 -1
- package/dist/esm/lockfile/load.js +0 -64
- package/dist/esm/lockfile/load.js.map +0 -1
- package/dist/esm/lockfile/save.d.ts.map +0 -1
- package/dist/esm/lockfile/save.js.map +0 -1
- package/dist/esm/lockfile/types.d.ts.map +0 -1
- package/dist/esm/lockfile/types.js.map +0 -1
- package/dist/esm/modifiers.d.ts.map +0 -1
- package/dist/esm/modifiers.js.map +0 -1
- package/dist/esm/node.d.ts.map +0 -1
- package/dist/esm/node.js.map +0 -1
- package/dist/esm/non-empty-list.d.ts.map +0 -1
- package/dist/esm/non-empty-list.js.map +0 -1
- package/dist/esm/package.json +0 -3
- package/dist/esm/reify/add-edge.d.ts.map +0 -1
- package/dist/esm/reify/add-edge.js.map +0 -1
- package/dist/esm/reify/add-edges.d.ts.map +0 -1
- package/dist/esm/reify/add-edges.js.map +0 -1
- package/dist/esm/reify/add-nodes.d.ts.map +0 -1
- package/dist/esm/reify/add-nodes.js.map +0 -1
- package/dist/esm/reify/bin-chmod.d.ts.map +0 -1
- package/dist/esm/reify/bin-chmod.js.map +0 -1
- package/dist/esm/reify/build.d.ts.map +0 -1
- package/dist/esm/reify/build.js.map +0 -1
- package/dist/esm/reify/calculate-save-value.d.ts.map +0 -1
- package/dist/esm/reify/calculate-save-value.js.map +0 -1
- package/dist/esm/reify/check-needed-build.d.ts.map +0 -1
- package/dist/esm/reify/check-needed-build.js +0 -50
- package/dist/esm/reify/check-needed-build.js.map +0 -1
- package/dist/esm/reify/delete-edge.d.ts.map +0 -1
- package/dist/esm/reify/delete-edge.js.map +0 -1
- package/dist/esm/reify/delete-edges.d.ts.map +0 -1
- package/dist/esm/reify/delete-edges.js.map +0 -1
- package/dist/esm/reify/delete-nodes.d.ts.map +0 -1
- package/dist/esm/reify/delete-nodes.js.map +0 -1
- package/dist/esm/reify/extract-node.d.ts.map +0 -1
- package/dist/esm/reify/extract-node.js.map +0 -1
- package/dist/esm/reify/index.d.ts.map +0 -1
- package/dist/esm/reify/index.js.map +0 -1
- package/dist/esm/reify/internal-hoist.d.ts.map +0 -1
- package/dist/esm/reify/internal-hoist.js.map +0 -1
- package/dist/esm/reify/optional-fail.d.ts.map +0 -1
- package/dist/esm/reify/optional-fail.js.map +0 -1
- package/dist/esm/reify/rollback.d.ts.map +0 -1
- package/dist/esm/reify/rollback.js.map +0 -1
- package/dist/esm/reify/update-importers-package-json.d.ts.map +0 -1
- package/dist/esm/reify/update-importers-package-json.js.map +0 -1
- package/dist/esm/remove-optional-subgraph.d.ts.map +0 -1
- package/dist/esm/remove-optional-subgraph.js.map +0 -1
- package/dist/esm/resolve-save-type.d.ts.map +0 -1
- package/dist/esm/resolve-save-type.js.map +0 -1
- package/dist/esm/stringify-node.d.ts.map +0 -1
- package/dist/esm/stringify-node.js.map +0 -1
- package/dist/esm/transfer-data/load.d.ts.map +0 -1
- package/dist/esm/transfer-data/load.js.map +0 -1
- package/dist/esm/uninstall.d.ts.map +0 -1
- package/dist/esm/uninstall.js.map +0 -1
- package/dist/esm/update.d.ts.map +0 -1
- package/dist/esm/update.js.map +0 -1
- package/dist/esm/virtual-root.d.ts.map +0 -1
- package/dist/esm/virtual-root.js.map +0 -1
- package/dist/esm/visualization/human-readable-output.d.ts.map +0 -1
- package/dist/esm/visualization/human-readable-output.js.map +0 -1
- package/dist/esm/visualization/json-output.d.ts.map +0 -1
- package/dist/esm/visualization/json-output.js.map +0 -1
- package/dist/esm/visualization/mermaid-output.d.ts.map +0 -1
- package/dist/esm/visualization/mermaid-output.js.map +0 -1
- package/dist/esm/visualization/object-like-output.d.ts.map +0 -1
- package/dist/esm/visualization/object-like-output.js.map +0 -1
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { joinDepIDTuple, joinExtra } from '@vltpkg/dep-id';
|
|
2
2
|
import { error } from '@vltpkg/error-cause';
|
|
3
3
|
import { Spec } from '@vltpkg/spec';
|
|
4
|
+
import { satisfies } from '@vltpkg/satisfies';
|
|
4
5
|
import { longDependencyTypes, normalizeManifest } from '@vltpkg/types';
|
|
5
6
|
import { fixupAddedNames } from "../fixup-added-names.js";
|
|
6
7
|
import { shorten } from "../dependencies.js";
|
|
7
8
|
import { removeOptionalSubgraph } from "../remove-optional-subgraph.js";
|
|
9
|
+
import { isPathSecurityError } from "../path-security-error.js";
|
|
8
10
|
import { extractNode } from "../reify/extract-node.js";
|
|
9
|
-
import { endPeerPlacement, postPlacementPeerCheck, startPeerPlacement, } from "./peers.js";
|
|
11
|
+
import { checkPeerEdgesCompatible, endPeerPlacement, forkPeerContext, postPlacementPeerCheck, startPeerPlacement, } from "./peers.js";
|
|
12
|
+
import { compareByHasPeerDeps } from "./sorting.js";
|
|
10
13
|
/**
|
|
11
14
|
* Only install devDeps for git dependencies and importers
|
|
12
15
|
* Everything else always gets installed
|
|
@@ -39,18 +42,96 @@ const getFileTypeInfo = (spec, fromNode, scurry) => {
|
|
|
39
42
|
};
|
|
40
43
|
};
|
|
41
44
|
const isStringArray = (a) => Array.isArray(a) && !a.some(b => typeof b !== 'string');
|
|
45
|
+
/**
|
|
46
|
+
* Try to find a compatible resolution for a dependency, checking peer context.
|
|
47
|
+
* If the first resolution candidate is incompatible with the peer context,
|
|
48
|
+
* try other candidates.
|
|
49
|
+
*/
|
|
50
|
+
const findCompatibleResolution = (spec, fromNode, graph, peerContext, queryModifier, _peer) => {
|
|
51
|
+
// Hoist invariants once
|
|
52
|
+
const fromLoc = fromNode.location;
|
|
53
|
+
const projectRoot = graph.projectRoot;
|
|
54
|
+
const monorepo = graph.monorepo;
|
|
55
|
+
const final = spec.final;
|
|
56
|
+
// Memoize satisfies() results per-node within this resolution attempt
|
|
57
|
+
const satisfiesCache = new Map();
|
|
58
|
+
const satisfiesFinal = (n) => {
|
|
59
|
+
const key = n.id;
|
|
60
|
+
const cached = satisfiesCache.get(key);
|
|
61
|
+
/* c8 ignore next 3 - optimization: cache hit when same node checked multiple times */
|
|
62
|
+
if (cached !== undefined) {
|
|
63
|
+
return cached;
|
|
64
|
+
}
|
|
65
|
+
const result = satisfies(key, final, fromLoc, projectRoot, monorepo);
|
|
66
|
+
satisfiesCache.set(key, result);
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
// Prefer existing edge target if it satisfies the spec.
|
|
70
|
+
// This ensures lockfile resolutions are preserved when still valid,
|
|
71
|
+
// rather than potentially picking a different satisfying version.
|
|
72
|
+
const existingEdge = fromNode.edgesOut.get(spec.name);
|
|
73
|
+
let existingNode;
|
|
74
|
+
if (existingEdge?.to &&
|
|
75
|
+
!existingEdge.to.detached &&
|
|
76
|
+
satisfiesFinal(existingEdge.to)) {
|
|
77
|
+
existingNode = existingEdge.to;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
existingNode = graph.findResolution(spec, fromNode, queryModifier);
|
|
81
|
+
}
|
|
82
|
+
let peerCompatResult = existingNode ?
|
|
83
|
+
checkPeerEdgesCompatible(existingNode, fromNode, peerContext, graph)
|
|
84
|
+
: { compatible: true };
|
|
85
|
+
// CANDIDATE FALLBACK: If first candidate is peer-incompatible, try others
|
|
86
|
+
// Lazy-load candidates only when fallback needed
|
|
87
|
+
if (existingNode && !peerCompatResult.compatible) {
|
|
88
|
+
const candidates = graph.nodesByName.get(final.name);
|
|
89
|
+
if (candidates && candidates.size > 1) {
|
|
90
|
+
for (const candidate of candidates) {
|
|
91
|
+
if (candidate === existingNode)
|
|
92
|
+
continue;
|
|
93
|
+
if (candidate.detached)
|
|
94
|
+
continue;
|
|
95
|
+
if (!satisfiesFinal(candidate))
|
|
96
|
+
continue;
|
|
97
|
+
const compat = checkPeerEdgesCompatible(candidate, fromNode, peerContext, graph);
|
|
98
|
+
if (compat.compatible) {
|
|
99
|
+
existingNode = candidate;
|
|
100
|
+
peerCompatResult = compat;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { existingNode, peerCompatResult };
|
|
107
|
+
};
|
|
42
108
|
/**
|
|
43
109
|
* Fetch manifests for dependencies and create placement tasks.
|
|
110
|
+
*
|
|
111
|
+
* This is Phase 1 of the breadth-first graph building process. For each
|
|
112
|
+
* dependency at the current level:
|
|
113
|
+
* 1. Apply any active modifiers (spec swapping)
|
|
114
|
+
* 2. Try to find an existing node to reuse (with peer compatibility check)
|
|
115
|
+
* 3. If no reusable node, start a manifest fetch (in parallel)
|
|
116
|
+
* 4. Create placement tasks for Phase 2
|
|
117
|
+
*
|
|
118
|
+
* The result is sorted to process non-peer-dependent packages first,
|
|
119
|
+
* ensuring peer dependencies can resolve to already-placed siblings.
|
|
120
|
+
*
|
|
121
|
+
* **Read-only**: This function no longer mutates the graph. It returns
|
|
122
|
+
* tasks that will be applied serially in the BFS loop for deterministic ordering.
|
|
44
123
|
*/
|
|
45
124
|
const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry, peerContext, modifierRefs, depth = 0) => {
|
|
46
|
-
// Create fetch tasks for all dependencies at this level
|
|
47
125
|
const fetchTasks = [];
|
|
48
126
|
const placementTasks = [];
|
|
127
|
+
const reuseTasks = [];
|
|
128
|
+
const forkRequests = [];
|
|
49
129
|
for (const { spec: originalSpec, type } of deps) {
|
|
50
130
|
let spec = originalSpec;
|
|
51
131
|
const fileTypeInfo = getFileTypeInfo(spec, fromNode, scurry);
|
|
52
132
|
const activeModifier = modifierRefs?.get(spec.name);
|
|
53
|
-
//
|
|
133
|
+
// MODIFIER HANDLING: Swap spec if an edge modifier is fully matched
|
|
134
|
+
// Example: `vlt install --override "react:^19"` changes react's spec
|
|
54
135
|
const queryModifier = activeModifier?.modifier.query;
|
|
55
136
|
const completeModifier = activeModifier &&
|
|
56
137
|
activeModifier.interactiveBreadcrumb.current ===
|
|
@@ -59,30 +140,55 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
|
|
|
59
140
|
completeModifier &&
|
|
60
141
|
'spec' in activeModifier.modifier) {
|
|
61
142
|
spec = activeModifier.modifier.spec;
|
|
143
|
+
// bareSpec of '-' means "remove this dependency"
|
|
62
144
|
if (spec.bareSpec === '-') {
|
|
63
145
|
continue;
|
|
64
146
|
}
|
|
65
147
|
}
|
|
66
148
|
const peer = type === 'peer' || type === 'peerOptional';
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
149
|
+
// NODE REUSE LOGIC with peer compatibility
|
|
150
|
+
const { existingNode, peerCompatResult } = findCompatibleResolution(spec, fromNode, graph, peerContext, queryModifier, peer);
|
|
151
|
+
// Accumulate fork request if incompatible peer edges detected (defer actual fork)
|
|
152
|
+
const effectivePeerContext = peerContext;
|
|
153
|
+
/* c8 ignore start */
|
|
154
|
+
if (!peerCompatResult.compatible && peerCompatResult.forkEntry) {
|
|
155
|
+
forkRequests.push(peerCompatResult.forkEntry);
|
|
156
|
+
// All fork entries from this fromNode will be applied together in Phase B
|
|
157
|
+
}
|
|
158
|
+
/* c8 ignore stop */
|
|
70
159
|
// defines what nodes are eligible to be reused
|
|
71
160
|
const validExistingNode = existingNode &&
|
|
72
161
|
!existingNode.detached &&
|
|
73
|
-
// Regular deps can always reuse
|
|
162
|
+
// Regular deps can always reuse.
|
|
163
|
+
// Peer deps can reuse as well if their peer edges are compatible.
|
|
74
164
|
/* c8 ignore start */
|
|
75
|
-
(!peer ||
|
|
76
|
-
|
|
77
|
-
|
|
165
|
+
(!peer || peerCompatResult.compatible) &&
|
|
166
|
+
// Check if existing node's peer edges are compatible with new parent
|
|
167
|
+
peerCompatResult.compatible;
|
|
78
168
|
/* c8 ignore stop */
|
|
79
169
|
if (validExistingNode ||
|
|
80
|
-
// importers are handled at the ./
|
|
170
|
+
// importers are handled at the ./refresh-ideal-graph.ts top-level
|
|
81
171
|
// so we should just skip whenever we find one
|
|
82
172
|
existingNode?.importer) {
|
|
83
|
-
|
|
173
|
+
// Defer edge creation to Phase B for deterministic ordering.
|
|
174
|
+
// Previously added immediately, but this caused race conditions when
|
|
175
|
+
// parallel fetches completed in different orders.
|
|
176
|
+
reuseTasks.push({ type, spec, fromNode, toNode: existingNode });
|
|
84
177
|
continue;
|
|
85
178
|
}
|
|
179
|
+
// workspace: specs can only resolve to local workspace packages.
|
|
180
|
+
// If we didn't find a matching workspace node above, it means either
|
|
181
|
+
// the workspace doesn't exist or its version doesn't satisfy the range.
|
|
182
|
+
if (spec.final.type === 'workspace') {
|
|
183
|
+
const wsVersion = graph.nodesByName
|
|
184
|
+
.get(spec.name)
|
|
185
|
+
?.values()
|
|
186
|
+
.next().value?.version;
|
|
187
|
+
throw error(wsVersion ?
|
|
188
|
+
`workspace dependency ${spec} does not match the ` +
|
|
189
|
+
`local workspace version (${wsVersion})`
|
|
190
|
+
: `no workspace found matching ${spec}`, { spec });
|
|
191
|
+
}
|
|
86
192
|
// is the current edge pointint go an optional dependency?
|
|
87
193
|
const edgeOptional = type === 'optional' || type === 'peerOptional';
|
|
88
194
|
// Start manifest fetch immediately for parallel processing
|
|
@@ -90,7 +196,7 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
|
|
|
90
196
|
// the "detached" node state means that it has already been load as
|
|
91
197
|
// part of a graph (either lockfile or actual) and it has valid manifest
|
|
92
198
|
// data so we shortcut the package info manifest fetch here
|
|
93
|
-
existingNode?.detached ?
|
|
199
|
+
existingNode?.detached && existingNode.manifest ?
|
|
94
200
|
Promise.resolve(existingNode.manifest)
|
|
95
201
|
// this is the entry point to fetch calls to retrieve manifests
|
|
96
202
|
// from the build ideal graph point of view
|
|
@@ -98,7 +204,10 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
|
|
|
98
204
|
.manifest(spec, { from: scurry.resolve(fromNode.location) })
|
|
99
205
|
.then(manifest => manifest)
|
|
100
206
|
.catch((er) => {
|
|
101
|
-
// optional deps ignored if inaccessible
|
|
207
|
+
// optional deps ignored if inaccessible, but a dep that tried
|
|
208
|
+
// to escape the project is never silently dropped
|
|
209
|
+
if (isPathSecurityError(er))
|
|
210
|
+
throw er;
|
|
102
211
|
if (edgeOptional || fromNode.optional) {
|
|
103
212
|
return undefined;
|
|
104
213
|
}
|
|
@@ -114,7 +223,7 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
|
|
|
114
223
|
edgeOptional,
|
|
115
224
|
manifestPromise,
|
|
116
225
|
depth,
|
|
117
|
-
peerContext,
|
|
226
|
+
peerContext: effectivePeerContext,
|
|
118
227
|
};
|
|
119
228
|
fetchTasks.push(fetchTask);
|
|
120
229
|
}
|
|
@@ -130,34 +239,30 @@ const fetchManifestsForDeps = async (packageInfo, graph, fromNode, deps, scurry,
|
|
|
130
239
|
// so that peer dependencies can easily reuse already placed regular
|
|
131
240
|
// dependencies as part of peer context set resolution also makes sure to
|
|
132
241
|
// sort by the manifest name for deterministic order.
|
|
133
|
-
placementTasks.sort(
|
|
134
|
-
|
|
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
|
-
});
|
|
152
|
-
return placementTasks;
|
|
242
|
+
placementTasks.sort(compareByHasPeerDeps);
|
|
243
|
+
return { placementTasks, reuseTasks, forkRequests };
|
|
153
244
|
};
|
|
154
245
|
/**
|
|
155
|
-
* Process placement tasks and collect child dependencies
|
|
156
|
-
*
|
|
157
|
-
*
|
|
246
|
+
* Process placement tasks and collect child dependencies.
|
|
247
|
+
*
|
|
248
|
+
* This is Phase 2 of the breadth-first graph building process. For each
|
|
249
|
+
* resolved manifest:
|
|
250
|
+
* 1. Handle missing manifests (optional vs required deps)
|
|
251
|
+
* 2. Start peer placement process (collect sibling context)
|
|
252
|
+
* 3. Place the node in the graph with appropriate flags
|
|
253
|
+
* 4. Trigger early extraction if eligible (performance optimization)
|
|
254
|
+
* 5. Collect child dependencies for the next BFS level
|
|
255
|
+
* 6. End peer placement (setup context update functions)
|
|
256
|
+
*
|
|
257
|
+
* Early extraction: When `actual` graph is provided, nodes destined for the
|
|
258
|
+
* vlt store are extracted immediately (in parallel) instead of waiting for
|
|
259
|
+
* the full ideal graph to be built. This significantly improves install time.
|
|
158
260
|
*/
|
|
159
261
|
const processPlacementTasks = async (graph, options, placementTasks, add, modifiers, scurry, packageInfo, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove) => {
|
|
160
262
|
const childDepsToProcess = [];
|
|
263
|
+
// Note: placementTasks are already sorted by fetchManifestsForDeps
|
|
264
|
+
// using compareByHasPeerDeps to ensure non-peer deps are processed first.
|
|
265
|
+
// We don't sort again here to preserve that ordering.
|
|
161
266
|
for (const placementTask of placementTasks) {
|
|
162
267
|
const { fetchTask, manifest } = placementTask;
|
|
163
268
|
let { activeModifier, edgeOptional, fileTypeInfo, fromNode, peerContext, queryModifier, spec, type, } = fetchTask;
|
|
@@ -193,7 +298,9 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
|
|
|
193
298
|
const peerSetHash = peerPlacement.peerSetHash;
|
|
194
299
|
const queuedEntries = peerPlacement.queuedEntries;
|
|
195
300
|
// places a new node in the graph representing a newly seen dependency
|
|
196
|
-
const node = graph.placePackage(fromNode, type, spec,
|
|
301
|
+
const node = graph.placePackage(fromNode, type, spec,
|
|
302
|
+
// report broken manifest names against the dependency spec
|
|
303
|
+
normalizeManifest(manifest, String(spec)), fileTypeInfo?.id, joinExtra({ peerSetHash, modifier: queryModifier }));
|
|
197
304
|
/* c8 ignore start - not possible, already ensured manifest */
|
|
198
305
|
if (!node) {
|
|
199
306
|
throw error('failed to place package', {
|
|
@@ -237,7 +344,13 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
|
|
|
237
344
|
if (fileTypeInfo?.path && fileTypeInfo.isDirectory) {
|
|
238
345
|
node.location = fileTypeInfo.path;
|
|
239
346
|
}
|
|
240
|
-
|
|
347
|
+
// Do not clobber lockfile-provided resolved values.
|
|
348
|
+
// `setResolved()` cannot infer a tarball without a manifest,
|
|
349
|
+
// which can cause `resolved` to become undefined and the
|
|
350
|
+
// main lockfile to mutate across installs.
|
|
351
|
+
if (!node.resolved) {
|
|
352
|
+
node.setResolved();
|
|
353
|
+
}
|
|
241
354
|
// collect child dependencies for processing in the next level
|
|
242
355
|
const nextPeerDeps = new Map();
|
|
243
356
|
// compute deps normally
|
|
@@ -254,7 +367,9 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
|
|
|
254
367
|
for (const depTypeName of longDependencyTypes) {
|
|
255
368
|
const depRecord = manifest[depTypeName];
|
|
256
369
|
if (depRecord && shouldInstallDepType(node, depTypeName)) {
|
|
257
|
-
|
|
370
|
+
// Sort Object.entries for deterministic iteration
|
|
371
|
+
const sortedEntries = Object.entries(depRecord).sort(([a], [b]) => a.localeCompare(b, 'en'));
|
|
372
|
+
for (const [name, bareSpec] of sortedEntries) {
|
|
258
373
|
// might need to skip already placed peer deps here
|
|
259
374
|
if (bundled.has(name))
|
|
260
375
|
continue;
|
|
@@ -262,7 +377,9 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
|
|
|
262
377
|
type: shorten(depTypeName, name, manifest),
|
|
263
378
|
spec: Spec.parse(name, bareSpec, {
|
|
264
379
|
...options,
|
|
265
|
-
|
|
380
|
+
// git/file/remote parents have no spec.registry, so fall
|
|
381
|
+
// back to the configured one. see vltpkg/vltpkg#1580.
|
|
382
|
+
registry: spec.registry ?? options.registry,
|
|
266
383
|
}),
|
|
267
384
|
};
|
|
268
385
|
if (depTypeName === 'peerDependencies') {
|
|
@@ -324,22 +441,43 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
|
|
|
324
441
|
* Append new nodes in the given `graph` for dependencies specified at `add`
|
|
325
442
|
* and missing dependencies from the `deps` parameter.
|
|
326
443
|
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
444
|
+
* Uses **breadth-first traversal** (BFS) with **deterministic ordering** to
|
|
445
|
+
* ensure reproducible builds. The algorithm:
|
|
446
|
+
*
|
|
447
|
+
* 1. Process all deps at the current level in parallel
|
|
448
|
+
* 2. After each level, run `postPlacementPeerCheck` to handle peer contexts
|
|
449
|
+
* 3. Collect child deps for the next level
|
|
450
|
+
* 4. Repeat until no more deps to process
|
|
451
|
+
*
|
|
452
|
+
* **Peer Context Isolation**: Each workspace importer gets its own peer context
|
|
453
|
+
* to prevent cross-workspace leakage. Without this, `react@^18` from workspace A
|
|
454
|
+
* could incorrectly satisfy `react@^19` peer deps in workspace B.
|
|
455
|
+
*
|
|
456
|
+
* **Early Extraction**: When `actual` graph is provided, nodes are extracted
|
|
457
|
+
* to the vlt store during graph construction (not after), improving performance.
|
|
329
458
|
*/
|
|
330
459
|
export const appendNodes = async (packageInfo, graph, fromNode, deps, scurry, options, seen, add, modifiers, modifierRefs, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove) => {
|
|
460
|
+
// Cycle detection: skip if already processed
|
|
331
461
|
/* c8 ignore next */
|
|
332
462
|
if (seen.has(fromNode.id))
|
|
333
463
|
return;
|
|
334
464
|
seen.add(fromNode.id);
|
|
335
|
-
//
|
|
336
|
-
|
|
465
|
+
// PEER CONTEXT ISOLATION: Each workspace importer needs its own context
|
|
466
|
+
// to prevent peer targets from one workspace affecting another.
|
|
467
|
+
// The main importer (index 0) uses the initial context; others get fresh ones.
|
|
468
|
+
let initialPeerContext = graph.peerContexts[0];
|
|
337
469
|
/* c8 ignore start - impossible */
|
|
338
|
-
if (!initialPeerContext)
|
|
470
|
+
if (!initialPeerContext)
|
|
339
471
|
throw error('no initial peer context found in graph');
|
|
340
|
-
}
|
|
341
472
|
/* c8 ignore stop */
|
|
342
|
-
|
|
473
|
+
if (fromNode.importer && fromNode !== graph.mainImporter) {
|
|
474
|
+
// Create isolated peer context for this workspace importer
|
|
475
|
+
const nextPeerContext = new Map();
|
|
476
|
+
nextPeerContext.index = graph.nextPeerContextIndex();
|
|
477
|
+
graph.peerContexts[nextPeerContext.index] = nextPeerContext;
|
|
478
|
+
initialPeerContext = nextPeerContext;
|
|
479
|
+
}
|
|
480
|
+
// BFS queue: process deps level by level for deterministic builds
|
|
343
481
|
let currentLevelDeps = [
|
|
344
482
|
{
|
|
345
483
|
node: fromNode,
|
|
@@ -355,27 +493,78 @@ export const appendNodes = async (packageInfo, graph, fromNode, deps, scurry, op
|
|
|
355
493
|
/* c8 ignore stop */
|
|
356
494
|
},
|
|
357
495
|
];
|
|
496
|
+
// BFS MAIN LOOP: Process level by level until no more deps
|
|
358
497
|
while (currentLevelDeps.length > 0) {
|
|
359
498
|
const nextLevelDeps = [];
|
|
360
|
-
//
|
|
361
|
-
|
|
362
|
-
|
|
499
|
+
// ============================================================
|
|
500
|
+
// PHASE A: PARALLEL FETCH (READ-ONLY)
|
|
501
|
+
// ============================================================
|
|
502
|
+
// Fetch all manifests at this level in parallel without mutating the graph.
|
|
503
|
+
// This phase is read-only to avoid race conditions from network timing.
|
|
504
|
+
const fetchResults = await Promise.all(currentLevelDeps.map(async ({ node, deps: nodeDeps, modifierRefs: nodeModifierRefs, peerContext, depth, }) => {
|
|
505
|
+
// Cycle prevention: mark as seen when starting to process
|
|
363
506
|
seen.add(node.id);
|
|
364
|
-
// Fetch manifests
|
|
365
|
-
const
|
|
366
|
-
// Sort
|
|
507
|
+
// Fetch manifests and collect tasks (no graph mutations)
|
|
508
|
+
const result = await fetchManifestsForDeps(packageInfo, graph, node,
|
|
509
|
+
// Sort by name for deterministic ordering (reproducible builds)
|
|
367
510
|
nodeDeps.sort((a, b) => a.spec.name.localeCompare(b.spec.name, 'en')), scurry, peerContext, nodeModifierRefs, depth);
|
|
368
|
-
|
|
369
|
-
|
|
511
|
+
return {
|
|
512
|
+
entry: {
|
|
513
|
+
node,
|
|
514
|
+
deps: nodeDeps,
|
|
515
|
+
modifierRefs: nodeModifierRefs,
|
|
516
|
+
peerContext,
|
|
517
|
+
depth,
|
|
518
|
+
},
|
|
519
|
+
result,
|
|
520
|
+
};
|
|
370
521
|
}));
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
522
|
+
// ============================================================
|
|
523
|
+
// PHASE B: SERIAL MUTATIONS (DETERMINISTIC ORDER)
|
|
524
|
+
// ============================================================
|
|
525
|
+
// Sort results by stable identifiers to ensure deterministic ordering
|
|
526
|
+
// regardless of which manifest fetch completed first
|
|
527
|
+
const sortedResults = fetchResults.sort((a, b) => {
|
|
528
|
+
// Sort by node ID (DepID-based, stable) and depth
|
|
529
|
+
const keyA = `${a.entry.node.id}::${a.entry.depth}`;
|
|
530
|
+
const keyB = `${b.entry.node.id}::${b.entry.depth}`;
|
|
531
|
+
return keyA.localeCompare(keyB, 'en');
|
|
532
|
+
});
|
|
533
|
+
// Apply all mutations serially in deterministic order
|
|
534
|
+
const levelResults = [];
|
|
535
|
+
for (const { entry, result } of sortedResults) {
|
|
536
|
+
// Apply accumulated fork requests if any (from Phase A deferred forks)
|
|
537
|
+
if (result.forkRequests.length > 0) {
|
|
538
|
+
const forkedContext = forkPeerContext(graph, entry.peerContext, result.forkRequests);
|
|
539
|
+
entry.peerContext = forkedContext;
|
|
540
|
+
// Update peer context in all placement tasks to use forked context
|
|
541
|
+
for (const task of result.placementTasks) {
|
|
542
|
+
task.fetchTask.peerContext = forkedContext;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// Apply reuse edges in deterministic order (before placement)
|
|
546
|
+
// Sort reuse tasks by spec name for stability
|
|
547
|
+
const sortedReuseTasks = result.reuseTasks.sort((a, b) => a.spec.name.localeCompare(b.spec.name, 'en'));
|
|
548
|
+
for (const { type, spec, fromNode, toNode, } of sortedReuseTasks) {
|
|
549
|
+
graph.addEdge(type, spec, fromNode, toNode);
|
|
550
|
+
}
|
|
551
|
+
// Place nodes and collect child deps
|
|
552
|
+
const placed = await processPlacementTasks(graph, options, result.placementTasks, add, modifiers, scurry, packageInfo, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove);
|
|
553
|
+
levelResults.push(placed);
|
|
554
|
+
}
|
|
555
|
+
// ============================================================
|
|
556
|
+
// PHASE C: POST-PLACEMENT PEER CHECK
|
|
557
|
+
// ============================================================
|
|
558
|
+
// After all nodes at this level are placed, update peer contexts,
|
|
559
|
+
// fork as needed, and resolve peer deps that can be satisfied.
|
|
560
|
+
// This must happen AFTER placement so sibling nodes are available.
|
|
375
561
|
postPlacementPeerCheck(graph, levelResults);
|
|
376
|
-
//
|
|
562
|
+
// ============================================================
|
|
563
|
+
// STEP 3: COLLECT CHILD DEPS FOR NEXT LEVEL
|
|
564
|
+
// ============================================================
|
|
377
565
|
for (const childDepsToProcess of levelResults) {
|
|
378
566
|
for (const childDep of childDepsToProcess) {
|
|
567
|
+
// Skip already-seen nodes (cycle prevention)
|
|
379
568
|
if (!seen.has(childDep.node.id)) {
|
|
380
569
|
/* c8 ignore next */
|
|
381
570
|
const currentDepth = currentLevelDeps[0]?.depth ?? 0;
|
|
@@ -386,8 +575,7 @@ export const appendNodes = async (packageInfo, graph, fromNode, deps, scurry, op
|
|
|
386
575
|
}
|
|
387
576
|
}
|
|
388
577
|
}
|
|
389
|
-
//
|
|
578
|
+
// Advance to next BFS level
|
|
390
579
|
currentLevelDeps = nextLevelDeps;
|
|
391
580
|
}
|
|
392
581
|
};
|
|
393
|
-
//# sourceMappingURL=append-nodes.js.map
|
|
@@ -12,4 +12,3 @@ export type BuildIdealFromStartingGraphOptions = RefreshIdealGraphOptions & {
|
|
|
12
12
|
* listed in the `add` and `remove` properties.
|
|
13
13
|
*/
|
|
14
14
|
export declare const buildIdealFromStartingGraph: (options: BuildIdealFromStartingGraphOptions) => Promise<Graph>;
|
|
15
|
-
//# sourceMappingURL=build-ideal-from-starting-graph.d.ts.map
|
|
@@ -3,6 +3,7 @@ import { graphStep } from '@vltpkg/output';
|
|
|
3
3
|
import { load as loadActual } from "../actual/load.js";
|
|
4
4
|
import { load as loadVirtual } from "../lockfile/load.js";
|
|
5
5
|
import { buildIdealFromStartingGraph } from "./build-ideal-from-starting-graph.js";
|
|
6
|
+
import { isPathSecurityError } from "../path-security-error.js";
|
|
6
7
|
import { splitDepID } from '@vltpkg/dep-id';
|
|
7
8
|
const getMap = (m) => m ?? new Map();
|
|
8
9
|
/**
|
|
@@ -39,7 +40,20 @@ export const build = async (options) => {
|
|
|
39
40
|
monorepo,
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
|
-
catch {
|
|
43
|
+
catch (err) {
|
|
44
|
+
// a path-safety verdict must not be silently rebuilt away
|
|
45
|
+
if (isPathSecurityError(err))
|
|
46
|
+
throw err;
|
|
47
|
+
// Check if this is a lockfile version mismatch
|
|
48
|
+
const cause = err.cause;
|
|
49
|
+
if (cause?.code === 'ELOCKFILEVERSION') {
|
|
50
|
+
// If lockfile is from a different vlt version, hard fail
|
|
51
|
+
if (typeof cause.found === 'number' &&
|
|
52
|
+
typeof cause.wanted === 'number') {
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
// TODO: add warning to CLI layer via @vltpkg/output when available
|
|
56
|
+
}
|
|
43
57
|
graph = loadActual({
|
|
44
58
|
...options,
|
|
45
59
|
mainManifest,
|
|
@@ -72,4 +86,3 @@ export const build = async (options) => {
|
|
|
72
86
|
}
|
|
73
87
|
return res;
|
|
74
88
|
};
|
|
75
|
-
//# sourceMappingURL=build.js.map
|
|
@@ -49,8 +49,20 @@ export const getImporterSpecs = (options) => {
|
|
|
49
49
|
// skip if the edge exists and already uses the same spec
|
|
50
50
|
if (edge?.to && depSpec === edge.spec.bareSpec)
|
|
51
51
|
continue;
|
|
52
|
+
const spec = Spec.parse(depName, depSpec, options);
|
|
53
|
+
// if a workspace dep references a workspace that no longer exists
|
|
54
|
+
// (folder was removed), mark it for removal instead of trying to
|
|
55
|
+
// resolve it — the workspace can't be satisfied
|
|
56
|
+
if (spec.type === 'workspace' && !edge?.to) {
|
|
57
|
+
const wsExists = [...graph.importers].some(n => n.name === depName);
|
|
58
|
+
if (!wsExists) {
|
|
59
|
+
removeDeps.add(depName);
|
|
60
|
+
removeResult.modifiedDependencies = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
52
64
|
const dependency = asDependency({
|
|
53
|
-
spec
|
|
65
|
+
spec,
|
|
54
66
|
type: shorten(depType, depName, importer.manifest),
|
|
55
67
|
});
|
|
56
68
|
addDeps.set(depName, dependency);
|
|
@@ -178,4 +190,3 @@ export const getImporterSpecs = (options) => {
|
|
|
178
190
|
transientRemove,
|
|
179
191
|
};
|
|
180
192
|
};
|
|
181
|
-
//# sourceMappingURL=get-importer-specs.js.map
|