@vltpkg/graph 1.0.0-rc.22 → 1.0.0-rc.24
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/dist/actual/load.d.ts +107 -0
- package/dist/actual/load.js +336 -0
- package/dist/browser.d.ts +14 -0
- package/dist/browser.js +16 -0
- package/dist/build.d.ts +28 -0
- package/dist/build.js +78 -0
- package/dist/dependencies.d.ts +65 -0
- package/dist/dependencies.js +111 -0
- package/dist/diff.d.ts +119 -0
- package/dist/diff.js +151 -0
- package/dist/edge.d.ts +46 -0
- package/dist/edge.js +77 -0
- package/dist/fixup-added-names.d.ts +18 -0
- package/dist/fixup-added-names.js +46 -0
- package/dist/graph.d.ts +153 -0
- package/dist/graph.js +444 -0
- package/dist/ideal/append-nodes.d.ts +31 -0
- package/dist/ideal/append-nodes.js +560 -0
- package/dist/ideal/build-ideal-from-starting-graph.d.ts +14 -0
- package/dist/ideal/build-ideal-from-starting-graph.js +69 -0
- package/dist/ideal/build.d.ts +40 -0
- package/dist/ideal/build.js +84 -0
- package/dist/ideal/get-importer-specs.d.ts +20 -0
- package/dist/ideal/get-importer-specs.js +180 -0
- package/dist/ideal/peers.d.ts +160 -0
- package/dist/ideal/peers.js +696 -0
- package/dist/ideal/refresh-ideal-graph.d.ts +43 -0
- package/dist/ideal/refresh-ideal-graph.js +62 -0
- package/dist/ideal/remove-satisfied-specs.d.ts +7 -0
- package/dist/ideal/remove-satisfied-specs.js +34 -0
- package/dist/ideal/sorting.d.ts +45 -0
- package/dist/ideal/sorting.js +70 -0
- package/dist/ideal/types.d.ts +107 -0
- package/dist/ideal/types.js +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +32 -0
- package/dist/install.d.ts +19 -0
- package/dist/install.js +208 -0
- package/dist/lockfile/load-edges.d.ts +11 -0
- package/dist/lockfile/load-edges.js +105 -0
- package/dist/lockfile/load-nodes.d.ts +4 -0
- package/dist/lockfile/load-nodes.js +101 -0
- package/dist/lockfile/load.d.ts +45 -0
- package/dist/lockfile/load.js +84 -0
- package/dist/lockfile/save.d.ts +30 -0
- package/dist/lockfile/save.js +174 -0
- package/dist/lockfile/types.d.ts +95 -0
- package/dist/lockfile/types.js +49 -0
- package/dist/modifiers.d.ts +188 -0
- package/dist/modifiers.js +329 -0
- package/dist/node.d.ts +234 -0
- package/dist/node.js +388 -0
- package/dist/non-empty-list.d.ts +2 -0
- package/dist/non-empty-list.js +2 -0
- package/dist/reify/add-edge.d.ts +9 -0
- package/dist/reify/add-edge.js +71 -0
- package/dist/reify/add-edges.d.ts +4 -0
- package/dist/reify/add-edges.js +12 -0
- package/dist/reify/add-nodes.d.ts +6 -0
- package/dist/reify/add-nodes.js +16 -0
- package/dist/reify/bin-chmod.d.ts +10 -0
- package/dist/reify/bin-chmod.js +38 -0
- package/dist/reify/build.d.ts +13 -0
- package/dist/reify/build.js +111 -0
- package/dist/reify/calculate-save-value.d.ts +2 -0
- package/dist/reify/calculate-save-value.js +50 -0
- package/dist/reify/check-needed-build.d.ts +34 -0
- package/dist/reify/check-needed-build.js +71 -0
- package/dist/reify/delete-edge.d.ts +4 -0
- package/dist/reify/delete-edge.js +27 -0
- package/dist/reify/delete-edges.d.ts +4 -0
- package/dist/reify/delete-edges.js +13 -0
- package/dist/reify/delete-nodes.d.ts +4 -0
- package/dist/reify/delete-nodes.js +15 -0
- package/dist/reify/extract-node.d.ts +23 -0
- package/dist/reify/extract-node.js +83 -0
- package/dist/reify/index.d.ts +34 -0
- package/dist/reify/index.js +161 -0
- package/dist/reify/internal-hoist.d.ts +8 -0
- package/dist/reify/internal-hoist.js +133 -0
- package/dist/reify/optional-fail.d.ts +15 -0
- package/dist/reify/optional-fail.js +15 -0
- package/dist/reify/rollback.d.ts +4 -0
- package/dist/reify/rollback.js +23 -0
- package/dist/reify/update-importers-package-json.d.ts +35 -0
- package/dist/reify/update-importers-package-json.js +122 -0
- package/dist/remove-optional-subgraph.d.ts +33 -0
- package/dist/remove-optional-subgraph.js +47 -0
- package/dist/resolve-save-type.d.ts +5 -0
- package/dist/resolve-save-type.js +4 -0
- package/dist/stringify-node.d.ts +2 -0
- package/dist/stringify-node.js +32 -0
- package/dist/transfer-data/load.d.ts +43 -0
- package/dist/transfer-data/load.js +175 -0
- package/dist/uninstall.d.ts +14 -0
- package/dist/uninstall.js +75 -0
- package/dist/update.d.ts +12 -0
- package/dist/update.js +73 -0
- package/dist/virtual-root.d.ts +15 -0
- package/dist/virtual-root.js +78 -0
- package/dist/visualization/human-readable-output.d.ts +26 -0
- package/dist/visualization/human-readable-output.js +163 -0
- package/dist/visualization/json-output.d.ts +41 -0
- package/dist/visualization/json-output.js +50 -0
- package/dist/visualization/mermaid-output.d.ts +17 -0
- package/dist/visualization/mermaid-output.js +170 -0
- package/dist/visualization/object-like-output.d.ts +2 -0
- package/dist/visualization/object-like-output.js +47 -0
- package/package.json +25 -25
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { PackageInfoClient } from '@vltpkg/package-info';
|
|
2
|
+
import type { LoadOptions as LoadActualOptions } from '../actual/load.ts';
|
|
3
|
+
import type { AddImportersDependenciesMap, RemoveImportersDependenciesMap } from '../dependencies.ts';
|
|
4
|
+
import type { Graph } from '../graph.ts';
|
|
5
|
+
import type { RollbackRemove } from '@vltpkg/rollback-remove';
|
|
6
|
+
export type BuildIdealOptions = LoadActualOptions & {
|
|
7
|
+
/**
|
|
8
|
+
* An actual graph
|
|
9
|
+
*/
|
|
10
|
+
actual?: Graph;
|
|
11
|
+
/**
|
|
12
|
+
* A `Map` in which keys are {@link DepID} linking to another `Map` in which
|
|
13
|
+
* keys are the dependency names and values are {@link Dependency}. This
|
|
14
|
+
* structure represents dependencies that need to be added to the importer
|
|
15
|
+
* represented by {@link DepID}.
|
|
16
|
+
*/
|
|
17
|
+
add?: AddImportersDependenciesMap;
|
|
18
|
+
/**
|
|
19
|
+
* A `Map` object representing nodes to be removed from the ideal graph.
|
|
20
|
+
* Each {@link DepID} key represents an importer node and the `Set` of
|
|
21
|
+
* dependency names to be removed from its dependency list.
|
|
22
|
+
*/
|
|
23
|
+
remove?: RemoveImportersDependenciesMap;
|
|
24
|
+
/**
|
|
25
|
+
* A {@link RollbackRemove} instance to handle extraction rollbacks
|
|
26
|
+
*/
|
|
27
|
+
remover: RollbackRemove;
|
|
28
|
+
/**
|
|
29
|
+
* A {@link PackageInfoClient} instance to read manifest info from.
|
|
30
|
+
*/
|
|
31
|
+
packageInfo: PackageInfoClient;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Builds an ideal {@link Graph} representing the dependencies that
|
|
35
|
+
* should be present in order to fulfill the requirements defined
|
|
36
|
+
* by the `package.json` and `vlt-lock.json` files using either the
|
|
37
|
+
* virtual or actual graph as a starting point. Also add / remove any
|
|
38
|
+
* dependencies listed in the `add` and `remove` properties.
|
|
39
|
+
*/
|
|
40
|
+
export declare const build: (options: BuildIdealOptions) => Promise<Graph>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { error } from '@vltpkg/error-cause';
|
|
2
|
+
import { graphStep } from '@vltpkg/output';
|
|
3
|
+
import { load as loadActual } from "../actual/load.js";
|
|
4
|
+
import { load as loadVirtual } from "../lockfile/load.js";
|
|
5
|
+
import { buildIdealFromStartingGraph } from "./build-ideal-from-starting-graph.js";
|
|
6
|
+
import { splitDepID } from '@vltpkg/dep-id';
|
|
7
|
+
const getMap = (m) => m ?? new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Validates that a file dependency node exists in the graph after
|
|
10
|
+
* a successful graph build. This helps providing an actionable error
|
|
11
|
+
* message in case the current working directory is a linked nested
|
|
12
|
+
* folder that hasn't yet been added as a dependency to an importer
|
|
13
|
+
* in the project.
|
|
14
|
+
*/
|
|
15
|
+
const validateFileDepNode = (graph, id) => {
|
|
16
|
+
const [type, path] = splitDepID(id);
|
|
17
|
+
if (type === 'file' && !graph.nodes.get(id)) {
|
|
18
|
+
throw error('The current working dir is not a dependency of this project', { path });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Builds an ideal {@link Graph} representing the dependencies that
|
|
23
|
+
* should be present in order to fulfill the requirements defined
|
|
24
|
+
* by the `package.json` and `vlt-lock.json` files using either the
|
|
25
|
+
* virtual or actual graph as a starting point. Also add / remove any
|
|
26
|
+
* dependencies listed in the `add` and `remove` properties.
|
|
27
|
+
*/
|
|
28
|
+
export const build = async (options) => {
|
|
29
|
+
const done = graphStep('build');
|
|
30
|
+
// Creates the shared instances that are going to be used
|
|
31
|
+
// in both the loader methods and the build graph
|
|
32
|
+
const { packageInfo, packageJson, scurry, monorepo } = options;
|
|
33
|
+
const mainManifest = options.mainManifest ?? packageJson.read(options.projectRoot);
|
|
34
|
+
let graph;
|
|
35
|
+
try {
|
|
36
|
+
graph = loadVirtual({
|
|
37
|
+
...options,
|
|
38
|
+
mainManifest,
|
|
39
|
+
monorepo,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
// Check if this is a lockfile version mismatch
|
|
44
|
+
const cause = err.cause;
|
|
45
|
+
if (cause?.code === 'ELOCKFILEVERSION') {
|
|
46
|
+
// If lockfile is from a different vlt version, hard fail
|
|
47
|
+
if (typeof cause.found === 'number' &&
|
|
48
|
+
typeof cause.wanted === 'number') {
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
// TODO: add warning to CLI layer via @vltpkg/output when available
|
|
52
|
+
}
|
|
53
|
+
graph = loadActual({
|
|
54
|
+
...options,
|
|
55
|
+
mainManifest,
|
|
56
|
+
monorepo,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const res = await buildIdealFromStartingGraph({
|
|
60
|
+
...options,
|
|
61
|
+
scurry,
|
|
62
|
+
add: getMap(options.add),
|
|
63
|
+
graph,
|
|
64
|
+
packageInfo,
|
|
65
|
+
remove: getMap(options.remove),
|
|
66
|
+
actual: options.actual,
|
|
67
|
+
});
|
|
68
|
+
done();
|
|
69
|
+
// when adding or removing a new dependency from a file dep,
|
|
70
|
+
// validate the receiver node was present in the graph
|
|
71
|
+
if (options.add) {
|
|
72
|
+
for (const [addKey, value] of options.add.entries()) {
|
|
73
|
+
if (value.size)
|
|
74
|
+
validateFileDepNode(res, addKey);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (options.remove) {
|
|
78
|
+
for (const [removeKey, value] of options.remove.entries()) {
|
|
79
|
+
if (value.size)
|
|
80
|
+
validateFileDepNode(res, removeKey);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return res;
|
|
84
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AddImportersDependenciesMap, RemoveImportersDependenciesMap } from '../dependencies.ts';
|
|
2
|
+
import type { BuildIdealAddOptions, BuildIdealFromGraphOptions, BuildIdealRemoveOptions, TransientAddMap, TransientRemoveMap } from './types.ts';
|
|
3
|
+
import type { SpecOptions } from '@vltpkg/spec';
|
|
4
|
+
import type { PackageJson } from '@vltpkg/package-json';
|
|
5
|
+
import type { PathScurry } from 'path-scurry';
|
|
6
|
+
export type GetImporterSpecsOptions = BuildIdealAddOptions & BuildIdealFromGraphOptions & BuildIdealRemoveOptions & SpecOptions & {
|
|
7
|
+
scurry: PathScurry;
|
|
8
|
+
packageJson: PackageJson;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Given a {@link Graph} and a list of {@link Dependency}, merges the
|
|
12
|
+
* dependencies info found in the graph importers and returns the add & remove
|
|
13
|
+
* results as a Map in which keys are {@link DepID} of each importer node.
|
|
14
|
+
*/
|
|
15
|
+
export declare const getImporterSpecs: (options: GetImporterSpecsOptions) => {
|
|
16
|
+
add: AddImportersDependenciesMap;
|
|
17
|
+
remove: RemoveImportersDependenciesMap;
|
|
18
|
+
transientAdd: TransientAddMap;
|
|
19
|
+
transientRemove: TransientRemoveMap;
|
|
20
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { longDependencyTypes } from '@vltpkg/types';
|
|
2
|
+
import { shorten, asDependency } from "../dependencies.js";
|
|
3
|
+
import { removeSatisfiedSpecs } from "./remove-satisfied-specs.js";
|
|
4
|
+
import { Spec } from '@vltpkg/spec';
|
|
5
|
+
const hasDepName = (importer, edge) => {
|
|
6
|
+
for (const depType of longDependencyTypes) {
|
|
7
|
+
const listedDeps = importer.manifest?.[depType];
|
|
8
|
+
if (listedDeps && Object.hasOwn(listedDeps, edge.name))
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
};
|
|
13
|
+
class AddImportersDependenciesMapImpl extends Map {
|
|
14
|
+
modifiedDependencies = false;
|
|
15
|
+
}
|
|
16
|
+
class RemoveImportersDependenciesMapImpl extends Map {
|
|
17
|
+
modifiedDependencies = false;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Given a {@link Graph} and a list of {@link Dependency}, merges the
|
|
21
|
+
* dependencies info found in the graph importers and returns the add & remove
|
|
22
|
+
* results as a Map in which keys are {@link DepID} of each importer node.
|
|
23
|
+
*/
|
|
24
|
+
export const getImporterSpecs = (options) => {
|
|
25
|
+
const { add, graph, remove } = options;
|
|
26
|
+
const addResult = new AddImportersDependenciesMapImpl();
|
|
27
|
+
const removeResult = new RemoveImportersDependenciesMapImpl();
|
|
28
|
+
// traverse the list of importers in the starting graph
|
|
29
|
+
for (const importer of graph.importers) {
|
|
30
|
+
// uses a Map keying to the spec.name in order to easily make sure there's
|
|
31
|
+
// only a single dependency entry for a given dependency for each importer
|
|
32
|
+
const addDeps = new Map();
|
|
33
|
+
const removeDeps = new Set();
|
|
34
|
+
// if an edge from the graph is not listed in the manifest,
|
|
35
|
+
// add that edge to the list of dependencies to be removed
|
|
36
|
+
for (const edge of importer.edgesOut.values()) {
|
|
37
|
+
if (!hasDepName(importer, edge) &&
|
|
38
|
+
!add.get(importer.id)?.has(edge.name)) {
|
|
39
|
+
removeDeps.add(edge.name);
|
|
40
|
+
removeResult.modifiedDependencies = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// if a dependency is listed in the manifest but not in the graph,
|
|
44
|
+
// add that dependency to the list of dependencies to be added
|
|
45
|
+
for (const depType of longDependencyTypes) {
|
|
46
|
+
const deps = Object.entries(importer.manifest?.[depType] ?? {});
|
|
47
|
+
for (const [depName, depSpec] of deps) {
|
|
48
|
+
const edge = importer.edgesOut.get(depName);
|
|
49
|
+
// skip if the edge exists and already uses the same spec
|
|
50
|
+
if (edge?.to && depSpec === edge.spec.bareSpec)
|
|
51
|
+
continue;
|
|
52
|
+
const dependency = asDependency({
|
|
53
|
+
spec: Spec.parse(depName, depSpec, options),
|
|
54
|
+
type: shorten(depType, depName, importer.manifest),
|
|
55
|
+
});
|
|
56
|
+
addDeps.set(depName, dependency);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
addResult.set(importer.id, addDeps);
|
|
60
|
+
removeResult.set(importer.id, removeDeps);
|
|
61
|
+
}
|
|
62
|
+
// Maps to store dependencies targeting non-importer nodes (e.g., nested folders)
|
|
63
|
+
// These will be injected when the target node is placed in the graph
|
|
64
|
+
const transientAdd = new Map();
|
|
65
|
+
const transientRemove = new Map();
|
|
66
|
+
// Traverse all nodes in the graph to find file type dependencies that are directories
|
|
67
|
+
// and populate transientAdd/transientRemove with their manifest dependencies
|
|
68
|
+
// Only process when scurry and packageJson are available
|
|
69
|
+
for (const node of graph.nodes.values()) {
|
|
70
|
+
// Skip importers as they're already handled above and also skip
|
|
71
|
+
// any non-file type dependencies
|
|
72
|
+
if (graph.importers.has(node) || !node.id.startsWith('file'))
|
|
73
|
+
continue;
|
|
74
|
+
// check if this is a file type dependency that is a directory
|
|
75
|
+
const nodePath = options.scurry.cwd.resolve(node.location);
|
|
76
|
+
const stat = nodePath.lstatSync();
|
|
77
|
+
if (stat?.isDirectory()) {
|
|
78
|
+
// load the manifest for this directory (throw if it does not exist)
|
|
79
|
+
const manifest = options.packageJson.read(nodePath.fullpath());
|
|
80
|
+
// should always set the manifest to the read manifest
|
|
81
|
+
node.manifest = manifest;
|
|
82
|
+
// create a map of dependencies from the manifest
|
|
83
|
+
const addDeps = new Map();
|
|
84
|
+
// check for edges not in manifest (should be removed)
|
|
85
|
+
const removeDeps = new Set();
|
|
86
|
+
for (const edge of node.edgesOut.values()) {
|
|
87
|
+
if (!hasDepName(node, edge) &&
|
|
88
|
+
!add.get(node.id)?.has(edge.name)) {
|
|
89
|
+
removeDeps.add(edge.name);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// iterate over manifest dependencies to add them if
|
|
93
|
+
// they're missing from the graph
|
|
94
|
+
for (const depType of longDependencyTypes) {
|
|
95
|
+
const deps = Object.entries(manifest[depType] ?? {});
|
|
96
|
+
for (const [depName, depSpec] of deps) {
|
|
97
|
+
const edge = node.edgesOut.get(depName);
|
|
98
|
+
// skip if the edge exists and already uses the same spec
|
|
99
|
+
if (edge?.to && depSpec === edge.spec.bareSpec)
|
|
100
|
+
continue;
|
|
101
|
+
// add the dependency to the addDeps map
|
|
102
|
+
const dependency = asDependency({
|
|
103
|
+
spec: Spec.parse(depName, depSpec, options),
|
|
104
|
+
type: shorten(depType, depName, manifest),
|
|
105
|
+
});
|
|
106
|
+
addDeps.set(depName, dependency);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// store in transientAdd if there are any dependencies
|
|
110
|
+
if (addDeps.size > 0) {
|
|
111
|
+
transientAdd.set(node.id, addDeps);
|
|
112
|
+
}
|
|
113
|
+
// store in transientRemove if there are any to remove
|
|
114
|
+
if (removeDeps.size > 0) {
|
|
115
|
+
transientRemove.set(node.id, removeDeps);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// merges any provided specs to add to the current found results
|
|
120
|
+
for (const [id, addDeps] of add.entries()) {
|
|
121
|
+
const deps = addResult.get(id);
|
|
122
|
+
if (!deps) {
|
|
123
|
+
// Not an importer - only store file-type deps for later injection
|
|
124
|
+
if (id.startsWith('file')) {
|
|
125
|
+
transientAdd.set(id, addDeps);
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
for (const [name, dep] of addDeps.entries()) {
|
|
130
|
+
deps.set(name, dep);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Merges results from user-provided `remove` option with any remove
|
|
134
|
+
// results found from comparing the manifest with the loaded graph
|
|
135
|
+
for (const [key, removeSet] of remove) {
|
|
136
|
+
const importerRemoveItem = removeResult.get(key);
|
|
137
|
+
if (importerRemoveItem) {
|
|
138
|
+
for (const depName of removeSet) {
|
|
139
|
+
importerRemoveItem.add(depName);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (key.startsWith('file')) {
|
|
143
|
+
// Not an importer - only store file-type deps in transientRemove
|
|
144
|
+
const existing = transientRemove.get(key);
|
|
145
|
+
if (existing) {
|
|
146
|
+
for (const depName of removeSet) {
|
|
147
|
+
existing.add(depName);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
transientRemove.set(key, new Set(removeSet));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Removes any references to an importer that no longer has specs
|
|
156
|
+
for (const [key, removeItem] of removeResult) {
|
|
157
|
+
if (removeItem.size === 0) {
|
|
158
|
+
removeResult.delete(key);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// removes already satisfied dependencies from the dependencies list
|
|
162
|
+
removeSatisfiedSpecs({
|
|
163
|
+
add: addResult,
|
|
164
|
+
graph,
|
|
165
|
+
});
|
|
166
|
+
// set the modifiedDependencies flag if any
|
|
167
|
+
// of the importers have modified dependencies
|
|
168
|
+
for (const addDeps of addResult.values()) {
|
|
169
|
+
if (addDeps.size > 0) {
|
|
170
|
+
addResult.modifiedDependencies = true;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
add: addResult,
|
|
176
|
+
remove: removeResult,
|
|
177
|
+
transientAdd,
|
|
178
|
+
transientRemove,
|
|
179
|
+
};
|
|
180
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Spec } from '@vltpkg/spec';
|
|
2
|
+
import type { PeerContext, PeerContextEntry, PeerContextEntryInput, ProcessPlacementResult } from './types.ts';
|
|
3
|
+
import type { SpecOptions } from '@vltpkg/spec';
|
|
4
|
+
import type { DependencySaveType, Manifest } from '@vltpkg/types';
|
|
5
|
+
import type { Monorepo } from '@vltpkg/workspaces';
|
|
6
|
+
import type { Dependency } from '../dependencies.ts';
|
|
7
|
+
import type { Graph } from '../graph.ts';
|
|
8
|
+
import type { Node } from '../node.ts';
|
|
9
|
+
/**
|
|
10
|
+
* Result of checking if an existing node's peer edges are compatible
|
|
11
|
+
* with a new parent's context. The `forkEntry` property is optional
|
|
12
|
+
* and will only be present if the node's peer edges are incompatible.
|
|
13
|
+
*/
|
|
14
|
+
type PeerEdgeCompatResult = {
|
|
15
|
+
compatible: boolean;
|
|
16
|
+
/** When incompatible, entry to add to forked context (target always present) */
|
|
17
|
+
forkEntry?: PeerContextEntryInput & {
|
|
18
|
+
target: Node;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Check if an existing node's peer edges would still resolve to the same
|
|
23
|
+
* targets from a new parent's context. Returns incompatible info if any
|
|
24
|
+
* peer would resolve differently, meaning the node should NOT be reused.
|
|
25
|
+
*
|
|
26
|
+
* This is crucial for avoiding incorrect node reuse that would break peer
|
|
27
|
+
* dependency contracts. Three sources of conflict are checked:
|
|
28
|
+
*
|
|
29
|
+
* 1. **Peer context entries**: The global peer context may have resolved a
|
|
30
|
+
* different version of a peer dependency than what the existing node expects.
|
|
31
|
+
*
|
|
32
|
+
* 2. **Already-placed siblings**: The parent node may already have an edge to
|
|
33
|
+
* a different version of the peer dependency.
|
|
34
|
+
*
|
|
35
|
+
* 3. **Not-yet-placed siblings**: The parent's manifest declares a dependency
|
|
36
|
+
* on the same package, and there's a graph node that would satisfy it but
|
|
37
|
+
* differs from what the existing node expects.
|
|
38
|
+
*/
|
|
39
|
+
export declare const checkPeerEdgesCompatible: (existingNode: Node, fromNode: Node, peerContext: PeerContext, graph: Graph) => PeerEdgeCompatResult;
|
|
40
|
+
/**
|
|
41
|
+
* Retrieve a unique hash value for a given peer context set.
|
|
42
|
+
*/
|
|
43
|
+
export declare const retrievePeerContextHash: (peerContext: PeerContext | undefined) => string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Checks if a given spec is compatible with the specs already
|
|
46
|
+
* assigned to a peer context entry.
|
|
47
|
+
*
|
|
48
|
+
* Returns true if INCOMPATIBLE, false if compatible.
|
|
49
|
+
*
|
|
50
|
+
* Compatibility rules:
|
|
51
|
+
* - **Registry specs**: Uses semver range intersection. `^18.0.0` and `^18.2.0`
|
|
52
|
+
* intersect (compatible), but `^18.0.0` and `^19.0.0` don't (incompatible).
|
|
53
|
+
* - **Non-registry specs** (git, file, etc.): Requires exact bareSpec match.
|
|
54
|
+
* `github:foo/bar#v1` only matches itself.
|
|
55
|
+
*
|
|
56
|
+
* This is used to determine when peer context forking is needed - if specs
|
|
57
|
+
* are incompatible, a new peer context must be created.
|
|
58
|
+
*/
|
|
59
|
+
export declare const incompatibleSpecs: (spec: Spec, entry: PeerContextEntry) => boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Sort peer context entry inputs for deterministic processing.
|
|
62
|
+
* Orders: non-peer dependencies first, then peer dependencies, alphabetically by name.
|
|
63
|
+
*/
|
|
64
|
+
export declare const getOrderedPeerContextEntries: (entries: PeerContextEntryInput[]) => PeerContextEntryInput[];
|
|
65
|
+
export declare const checkEntriesToPeerContext: (peerContext: PeerContext, entries: PeerContextEntryInput[]) => boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Add or update dependencies in a given peer context making sure to check
|
|
68
|
+
* for compatibility with existing dependencies already resolved by a given
|
|
69
|
+
* peer context set. Extra info such as a target or dependent nodes is
|
|
70
|
+
* optional.
|
|
71
|
+
*
|
|
72
|
+
* Returns true if forking is needed, false otherwise.
|
|
73
|
+
*/
|
|
74
|
+
export declare const addEntriesToPeerContext: (peerContext: PeerContext, entries: PeerContextEntryInput[], fromNode: Node, monorepo?: Monorepo) => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Create and returns a forked copy of a given peer context set.
|
|
77
|
+
*/
|
|
78
|
+
export declare const forkPeerContext: (graph: Graph, peerContext: PeerContext, entries: PeerContextEntryInput[]) => PeerContext;
|
|
79
|
+
/**
|
|
80
|
+
* Starts the peer dependency placement process
|
|
81
|
+
* for a given node being processed and placed.
|
|
82
|
+
*/
|
|
83
|
+
export declare const startPeerPlacement: (peerContext: PeerContext, manifest: Manifest, fromNode: Node, options: SpecOptions) => {
|
|
84
|
+
peerSetHash: string | undefined;
|
|
85
|
+
queuedEntries: PeerContextEntryInput[];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Ends the peer dependency placement process, returning the functions that
|
|
89
|
+
* are going to be used to update the peer context set, forking when needed
|
|
90
|
+
* and resolving peer dependencies if possible.
|
|
91
|
+
*
|
|
92
|
+
* Returns two deferred functions:
|
|
93
|
+
* - `putEntries()`: Adds entries to peer context; returns fork entries if conflict
|
|
94
|
+
* - `resolvePeerDeps()`: Resolves peer deps from context/siblings or adds to nextDeps
|
|
95
|
+
*
|
|
96
|
+
* These are deferred (not executed immediately) so that all siblings at a level
|
|
97
|
+
* can be processed before peer context updates, enabling context reuse optimization.
|
|
98
|
+
*/
|
|
99
|
+
export declare const endPeerPlacement: (peerContext: PeerContext, nextDeps: Dependency[], nextPeerDeps: Map<string, Dependency> & {
|
|
100
|
+
id?: number;
|
|
101
|
+
}, graph: Graph, spec: Spec, fromNode: Node, node: Node, type: DependencySaveType, queuedEntries: PeerContextEntryInput[]) => {
|
|
102
|
+
/**
|
|
103
|
+
* Add the new entries to the current peer context set.
|
|
104
|
+
*
|
|
105
|
+
* Two sets of entries are checked:
|
|
106
|
+
* - `prevEntries`: Parent's queued entries + self-reference
|
|
107
|
+
* - `nextEntries`: This node's deps + peer deps (with node as dependent)
|
|
108
|
+
*
|
|
109
|
+
* If either conflicts with the current context, returns ALL entries to be
|
|
110
|
+
* added to a forked context (prevEntries last for priority).
|
|
111
|
+
*
|
|
112
|
+
* Returns `undefined` if no fork needed (entries added directly to context).
|
|
113
|
+
*/
|
|
114
|
+
putEntries: () => (PeerContextEntryInput | {
|
|
115
|
+
dependent: Node;
|
|
116
|
+
spec: import("@vltpkg/spec/browser").Spec;
|
|
117
|
+
type: DependencySaveType;
|
|
118
|
+
} | {
|
|
119
|
+
spec: Spec;
|
|
120
|
+
target: Node;
|
|
121
|
+
type: DependencySaveType;
|
|
122
|
+
})[] | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* Try to resolve peer dependencies using already seen target
|
|
125
|
+
* values from the current peer context set.
|
|
126
|
+
*
|
|
127
|
+
* Resolution priority (highest to lowest):
|
|
128
|
+
* 1. Sibling deps from parent (workspace direct deps take priority)
|
|
129
|
+
* 2. Peer-edge closure of sibling targets (handles peer cycles)
|
|
130
|
+
* 3. Global peer context set entries
|
|
131
|
+
* 4. Add to nextDeps for normal resolution (or create dangling edge for optional)
|
|
132
|
+
*/
|
|
133
|
+
resolvePeerDeps: () => void;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Given an array of processed results for the current level dependencies
|
|
137
|
+
* being placed in the currently building ideal graph, traverse its direct
|
|
138
|
+
* dependencies and track peer dependencies in their appropriate peer context
|
|
139
|
+
* sets, forking as needed and resolving peer dependencies using suitable
|
|
140
|
+
* nodes already present in the graph if possible.
|
|
141
|
+
*
|
|
142
|
+
* This is the core peer context management algorithm, executed after each
|
|
143
|
+
* BFS level. It runs in three phases:
|
|
144
|
+
*
|
|
145
|
+
* **Phase 1: Collect fork requirements**
|
|
146
|
+
* Call `putEntries()` on each child dep to add entries to peer context.
|
|
147
|
+
* Collect which children need forked contexts (due to conflicts).
|
|
148
|
+
*
|
|
149
|
+
* **Phase 2: Fork or reuse contexts**
|
|
150
|
+
* For children needing forks, try to reuse a sibling's forked context if
|
|
151
|
+
* compatible. This optimization reduces the number of peer contexts created.
|
|
152
|
+
*
|
|
153
|
+
* **Phase 3: Resolve peer deps**
|
|
154
|
+
* With contexts finalized, call `resolvePeerDeps()` to create edges for
|
|
155
|
+
* peers that can be satisfied from context/siblings, or add them to nextDeps.
|
|
156
|
+
*
|
|
157
|
+
* All operations are sorted by `node.id` for deterministic, reproducible builds.
|
|
158
|
+
*/
|
|
159
|
+
export declare const postPlacementPeerCheck: (graph: Graph, sortedLevelResults: ProcessPlacementResult[]) => void;
|
|
160
|
+
export {};
|