@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,43 @@
|
|
|
1
|
+
import type { PathScurry } from 'path-scurry';
|
|
2
|
+
import type { PackageInfoClient } from '@vltpkg/package-info';
|
|
3
|
+
import type { SpecOptions } from '@vltpkg/spec';
|
|
4
|
+
import type { RollbackRemove } from '@vltpkg/rollback-remove';
|
|
5
|
+
import type { BuildIdealAddOptions, BuildIdealFromGraphOptions, BuildIdealRemoveOptions, TransientAddMap, TransientRemoveMap } from './types.ts';
|
|
6
|
+
import type { GraphModifier } from '../modifiers.ts';
|
|
7
|
+
import type { Graph } from '../graph.ts';
|
|
8
|
+
export type RefreshIdealGraphOptions = BuildIdealAddOptions & BuildIdealRemoveOptions & BuildIdealFromGraphOptions & SpecOptions & {
|
|
9
|
+
/**
|
|
10
|
+
* The graph modifiers helper object.
|
|
11
|
+
*/
|
|
12
|
+
modifiers?: GraphModifier;
|
|
13
|
+
/**
|
|
14
|
+
* A {@link PathScurry} instance based on the `projectRoot` path
|
|
15
|
+
*/
|
|
16
|
+
scurry: PathScurry;
|
|
17
|
+
/**
|
|
18
|
+
* A {@link PackageInfoClient} instance to read manifest info from.
|
|
19
|
+
*/
|
|
20
|
+
packageInfo: PackageInfoClient;
|
|
21
|
+
/**
|
|
22
|
+
* The actual graph to compare against for early extraction
|
|
23
|
+
*/
|
|
24
|
+
actual?: Graph;
|
|
25
|
+
/**
|
|
26
|
+
* A {@link RollbackRemove} instance to handle extraction rollbacks
|
|
27
|
+
*/
|
|
28
|
+
remover: RollbackRemove;
|
|
29
|
+
/**
|
|
30
|
+
* Dependencies to be added to non-importer nodes when they are placed.
|
|
31
|
+
* Used for nested folder dependencies that are not importers.
|
|
32
|
+
*/
|
|
33
|
+
transientAdd?: TransientAddMap;
|
|
34
|
+
/**
|
|
35
|
+
* Dependencies to be removed from non-importer nodes when they are placed.
|
|
36
|
+
* Used for nested folder dependencies that are not importers.
|
|
37
|
+
*/
|
|
38
|
+
transientRemove?: TransientRemoveMap;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Rebuilds the provided ideal graph.
|
|
42
|
+
*/
|
|
43
|
+
export declare const refreshIdealGraph: ({ add, graph, modifiers, packageInfo, scurry, actual, remove, remover, transientAdd, transientRemove, ...specOptions }: RefreshIdealGraphOptions) => Promise<void>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { appendNodes } from "./append-nodes.js";
|
|
2
|
+
import { compareByHasPeerDeps, getNodeOrderedDependencies, } from "./sorting.js";
|
|
3
|
+
/**
|
|
4
|
+
* Returns an ordered list of importer nodes.
|
|
5
|
+
*/
|
|
6
|
+
const getOrderedImporters = (graph) => {
|
|
7
|
+
const orderedImporters = [...graph.importers].sort((a, b) => {
|
|
8
|
+
// mainImporter always comes first
|
|
9
|
+
/* c8 ignore next */
|
|
10
|
+
if (a === graph.mainImporter)
|
|
11
|
+
return -1;
|
|
12
|
+
if (b === graph.mainImporter)
|
|
13
|
+
return 1;
|
|
14
|
+
return compareByHasPeerDeps({ manifest: a.manifest }, { manifest: b.manifest });
|
|
15
|
+
});
|
|
16
|
+
return orderedImporters;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Rebuilds the provided ideal graph.
|
|
20
|
+
*/
|
|
21
|
+
export const refreshIdealGraph = async ({ add, graph, modifiers, packageInfo, scurry, actual, remove, remover, transientAdd, transientRemove, ...specOptions }) => {
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
const extractPromises = [];
|
|
24
|
+
const seenExtracted = new Set();
|
|
25
|
+
// gets an ordered list of importers to ensure deterministic processing
|
|
26
|
+
const orderedImporters = getOrderedImporters(graph);
|
|
27
|
+
const depsPerImporter = new Map();
|
|
28
|
+
for (const importer of orderedImporters) {
|
|
29
|
+
// gets an ordered list of dependencies for this importer
|
|
30
|
+
// while also taking into account additions and removals
|
|
31
|
+
const deps = getNodeOrderedDependencies(importer, { add, remove });
|
|
32
|
+
depsPerImporter.set(importer, deps);
|
|
33
|
+
}
|
|
34
|
+
// removes all edges to start recalculating the graph
|
|
35
|
+
if (add.modifiedDependencies || remove.modifiedDependencies) {
|
|
36
|
+
graph.resetEdges();
|
|
37
|
+
}
|
|
38
|
+
// iterates on the list of dependencies per importer updating
|
|
39
|
+
// the graph using metadata fetch from the registry manifest files
|
|
40
|
+
for (const importer of orderedImporters) {
|
|
41
|
+
modifiers?.tryImporter(importer);
|
|
42
|
+
// gets a ref to the map of dependencies being added to this importer
|
|
43
|
+
const addedDeps = add.get(importer.id);
|
|
44
|
+
const deps = depsPerImporter.get(importer);
|
|
45
|
+
/* c8 ignore next */
|
|
46
|
+
if (!deps)
|
|
47
|
+
continue;
|
|
48
|
+
// gets a ref to the list of modifier functions for this set of deps
|
|
49
|
+
const modifierRefs = modifiers?.tryDependencies(importer, deps);
|
|
50
|
+
// Add new nodes for packages defined in the dependencies list fetching
|
|
51
|
+
// metadata from the registry manifests and updating the graph
|
|
52
|
+
await appendNodes(packageInfo, graph, importer, deps, scurry, specOptions, seen, addedDeps, modifiers, modifierRefs, extractPromises, actual, seenExtracted, remover, transientAdd, transientRemove);
|
|
53
|
+
}
|
|
54
|
+
// set default node locations, if possible
|
|
55
|
+
for (const node of graph.nodes.values()) {
|
|
56
|
+
node.setDefaultLocation();
|
|
57
|
+
}
|
|
58
|
+
// Wait for all extraction promises to complete
|
|
59
|
+
if (extractPromises.length > 0) {
|
|
60
|
+
await Promise.all(extractPromises);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BuildIdealAddOptions, BuildIdealFromGraphOptions } from './types.ts';
|
|
2
|
+
export type RemoveSatisfiedSpecsOptions = BuildIdealAddOptions & BuildIdealFromGraphOptions;
|
|
3
|
+
/**
|
|
4
|
+
* Traverse the objects defined in `add` and removes any references to specs
|
|
5
|
+
* that are already satisfied by the contents of the actual `graph`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const removeSatisfiedSpecs: ({ add, graph, }: RemoveSatisfiedSpecsOptions) => void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { error } from '@vltpkg/error-cause';
|
|
2
|
+
import { satisfies } from '@vltpkg/satisfies';
|
|
3
|
+
/**
|
|
4
|
+
* Traverse the objects defined in `add` and removes any references to specs
|
|
5
|
+
* that are already satisfied by the contents of the actual `graph`.
|
|
6
|
+
*/
|
|
7
|
+
export const removeSatisfiedSpecs = ({ add, graph, }) => {
|
|
8
|
+
for (const [depID, dependencies] of add.entries()) {
|
|
9
|
+
const importer = graph.nodes.get(depID);
|
|
10
|
+
if (!importer) {
|
|
11
|
+
throw error('Referred importer node id could not be found', {
|
|
12
|
+
found: depID,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
for (const [name, dependency] of dependencies) {
|
|
16
|
+
const edge = importer.edgesOut.get(name);
|
|
17
|
+
if (!edge) {
|
|
18
|
+
// brand new edge being added
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
// If the current graph edge is already valid, then we remove that
|
|
22
|
+
// dependency item from the list of items to be added to the graph
|
|
23
|
+
if (satisfies(edge.to?.id, dependency.spec, edge.from.location, graph.projectRoot, graph.monorepo)) {
|
|
24
|
+
dependencies.delete(name);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Removes any references to an importer that no longer has specs
|
|
29
|
+
for (const [depID, dependencies] of add.entries()) {
|
|
30
|
+
if (dependencies.size === 0) {
|
|
31
|
+
add.delete(depID);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BuildIdealAddOptions, BuildIdealRemoveOptions } from './types.ts';
|
|
2
|
+
import type { Spec } from '@vltpkg/spec';
|
|
3
|
+
import type { Manifest, NormalizedManifest } from '@vltpkg/types';
|
|
4
|
+
import type { Node } from '../node.ts';
|
|
5
|
+
import type { Dependency } from '../dependencies.ts';
|
|
6
|
+
type SortableByHasPeerDeps = {
|
|
7
|
+
/** Package manifest containing dependency information */
|
|
8
|
+
manifest?: Manifest | NormalizedManifest;
|
|
9
|
+
/** Package name */
|
|
10
|
+
name?: string;
|
|
11
|
+
/** Package specifier */
|
|
12
|
+
spec?: Spec;
|
|
13
|
+
};
|
|
14
|
+
type SortableByType = {
|
|
15
|
+
/** Dependency type (e.g., 'prod', 'dev', 'peer', 'peerOptional') */
|
|
16
|
+
type: string;
|
|
17
|
+
/** Target node with package name */
|
|
18
|
+
target?: {
|
|
19
|
+
name: string;
|
|
20
|
+
};
|
|
21
|
+
/** Package specifier */
|
|
22
|
+
spec?: Spec;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Checks if a dependency type is a peer dependency.
|
|
26
|
+
*/
|
|
27
|
+
export declare const isPeerType: (type: string) => type is "peer" | "peerOptional";
|
|
28
|
+
/**
|
|
29
|
+
* Sorts a list of dependencies by whether they have peer dependencies.
|
|
30
|
+
*/
|
|
31
|
+
export declare const compareByHasPeerDeps: (a: SortableByHasPeerDeps, b: SortableByHasPeerDeps) => number;
|
|
32
|
+
/**
|
|
33
|
+
* Sorts a list of dependencies by type and name.
|
|
34
|
+
*/
|
|
35
|
+
export declare const compareByType: (a: SortableByType, b: SortableByType) => number;
|
|
36
|
+
/**
|
|
37
|
+
* Computes the ordered list of dependencies for an given node,
|
|
38
|
+
* taking into account additions and removals.
|
|
39
|
+
*/
|
|
40
|
+
export declare const getNodeOrderedDependencies: (fromNode: Node, options?: BuildIdealAddOptions & BuildIdealRemoveOptions) => Dependency[];
|
|
41
|
+
/**
|
|
42
|
+
* Sorts a list of dependencies by type.
|
|
43
|
+
*/
|
|
44
|
+
export declare const getOrderedDependencies: (deps: Dependency[]) => Dependency[];
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a dependency type is a peer dependency.
|
|
3
|
+
*/
|
|
4
|
+
export const isPeerType = (type) => type === 'peer' || type === 'peerOptional';
|
|
5
|
+
/**
|
|
6
|
+
* Sorts a list of dependencies by whether they have peer dependencies.
|
|
7
|
+
*/
|
|
8
|
+
export const compareByHasPeerDeps = (a, b) => {
|
|
9
|
+
const aHasPeer = (a.manifest?.peerDependencies &&
|
|
10
|
+
Object.keys(a.manifest.peerDependencies).length > 0) ?
|
|
11
|
+
1
|
|
12
|
+
: 0;
|
|
13
|
+
const bHasPeer = (b.manifest?.peerDependencies &&
|
|
14
|
+
Object.keys(b.manifest.peerDependencies).length > 0) ?
|
|
15
|
+
1
|
|
16
|
+
: 0;
|
|
17
|
+
if (aHasPeer !== bHasPeer)
|
|
18
|
+
return aHasPeer - bHasPeer;
|
|
19
|
+
const aName = a.manifest?.name || a.spec?.name || a.name || '';
|
|
20
|
+
const bName = b.manifest?.name ||
|
|
21
|
+
/* c8 ignore next - very hard to test */ b.spec?.name ||
|
|
22
|
+
b.name ||
|
|
23
|
+
'';
|
|
24
|
+
return aName.localeCompare(bName, 'en');
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Sorts a list of dependencies by type and name.
|
|
28
|
+
*/
|
|
29
|
+
export const compareByType = (a, b) => {
|
|
30
|
+
const aIsPeer = isPeerType(a.type) ? 1 : 0;
|
|
31
|
+
const bIsPeer = isPeerType(b.type) ? 1 : 0;
|
|
32
|
+
if (aIsPeer !== bIsPeer)
|
|
33
|
+
return aIsPeer - bIsPeer;
|
|
34
|
+
const aName = a.target?.name ?? a.spec?.name ?? '';
|
|
35
|
+
const bName = b.target?.name ?? b.spec?.name ?? '';
|
|
36
|
+
return aName.localeCompare(bName, 'en');
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Computes the ordered list of dependencies for an given node,
|
|
40
|
+
* taking into account additions and removals.
|
|
41
|
+
*/
|
|
42
|
+
export const getNodeOrderedDependencies = (fromNode, options) => {
|
|
43
|
+
// using a map here instead of an array helps us get simpler
|
|
44
|
+
// deduplication while iterating through all the items at hand:
|
|
45
|
+
// existing dependencies in the graph, dependencies to be added, etc.
|
|
46
|
+
const deps = new Map();
|
|
47
|
+
for (const [name, { spec, type }] of fromNode.edgesOut.entries()) {
|
|
48
|
+
deps.set(name, { spec, type });
|
|
49
|
+
}
|
|
50
|
+
// next iterate through the list of dependencies to be added
|
|
51
|
+
const addedDeps = options?.add.get(fromNode.id);
|
|
52
|
+
if (addedDeps) {
|
|
53
|
+
for (const [name, { spec, type }] of addedDeps.entries()) {
|
|
54
|
+
deps.set(name, { spec, type });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// finally iterate through the list of dependencies to be removed
|
|
58
|
+
const removedDeps = options?.remove.get(fromNode.id);
|
|
59
|
+
if (removedDeps) {
|
|
60
|
+
for (const name of removedDeps) {
|
|
61
|
+
deps.delete(name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// now turn the map into a sorted array
|
|
65
|
+
return getOrderedDependencies([...deps.values()]);
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Sorts a list of dependencies by type.
|
|
69
|
+
*/
|
|
70
|
+
export const getOrderedDependencies = (deps) => [...deps].sort(compareByType);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { PackageInfoClient } from '@vltpkg/package-info';
|
|
2
|
+
import type { Spec } from '@vltpkg/spec';
|
|
3
|
+
import type { DependencySaveType } from '@vltpkg/types';
|
|
4
|
+
import type { AddImportersDependenciesMap, Dependency, RemoveImportersDependenciesMap } from '../dependencies.ts';
|
|
5
|
+
import type { ModifierActiveEntry } from '../modifiers.ts';
|
|
6
|
+
import type { Graph } from '../graph.ts';
|
|
7
|
+
import type { Node } from '../node.ts';
|
|
8
|
+
/**
|
|
9
|
+
* A map of dependencies to be added to non-importer nodes.
|
|
10
|
+
* Keys are {@link DepID} of nodes that are not importers (e.g., nested folders).
|
|
11
|
+
* When these nodes are resolved and placed in the graph, their dependencies
|
|
12
|
+
* from this map are injected into the processing queue.
|
|
13
|
+
*/
|
|
14
|
+
export type TransientAddMap = Omit<AddImportersDependenciesMap, 'modifiedDependencies'>;
|
|
15
|
+
/**
|
|
16
|
+
* A map of dependency names to be removed from non-importer nodes.
|
|
17
|
+
* Keys are {@link DepID} of nodes that are not importers (e.g., nested folders).
|
|
18
|
+
* When these nodes are processed, their dependencies in this map are excluded.
|
|
19
|
+
*/
|
|
20
|
+
export type TransientRemoveMap = Omit<RemoveImportersDependenciesMap, 'modifiedDependencies'>;
|
|
21
|
+
export type BuildIdealAddOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* A {@link AddImportersDependenciesMap} in which keys are {@link DepID}
|
|
24
|
+
* linking to another `Map` in which keys are the dependency names and values
|
|
25
|
+
* are {@link Dependency}. This structure represents dependencies that need
|
|
26
|
+
* to be added to the importer represented by {@link DepID}.
|
|
27
|
+
*/
|
|
28
|
+
add: AddImportersDependenciesMap;
|
|
29
|
+
};
|
|
30
|
+
export type BuildIdealRemoveOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* A {@link RemoveImportersDependenciesMap} object representing nodes to be
|
|
33
|
+
* removed from the ideal graph. Each {@link DepID} key represents an
|
|
34
|
+
* importer node and the `Set` of dependency names to be removed from its
|
|
35
|
+
* dependency list.
|
|
36
|
+
*/
|
|
37
|
+
remove: RemoveImportersDependenciesMap;
|
|
38
|
+
};
|
|
39
|
+
export type BuildIdealFromGraphOptions = {
|
|
40
|
+
/**
|
|
41
|
+
* An initial {@link Graph} to start building from, adding nodes to any
|
|
42
|
+
* missing edges and appending any new specs defined in `addSpecs`.
|
|
43
|
+
*/
|
|
44
|
+
graph: Graph;
|
|
45
|
+
};
|
|
46
|
+
export type BuildIdealPackageInfoOptions = {
|
|
47
|
+
/**
|
|
48
|
+
* A {@link PackageInfoClient} instance to read manifest info from.
|
|
49
|
+
*/
|
|
50
|
+
packageInfo: PackageInfoClient;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Represents an ongoing append operation for a node and its dependencies.
|
|
54
|
+
*/
|
|
55
|
+
export type AppendNodeEntry = {
|
|
56
|
+
node: Node;
|
|
57
|
+
deps: Dependency[];
|
|
58
|
+
modifierRefs?: Map<string, ModifierActiveEntry>;
|
|
59
|
+
depth: number;
|
|
60
|
+
peerContext: PeerContext;
|
|
61
|
+
updateContext: {
|
|
62
|
+
putEntries: () => PeerContextEntryInput[] | undefined;
|
|
63
|
+
resolvePeerDeps: () => void;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The result of processing a given placed node in the graph.
|
|
68
|
+
*/
|
|
69
|
+
export type ProcessPlacementResultEntry = Omit<AppendNodeEntry, 'depth'>;
|
|
70
|
+
/**
|
|
71
|
+
* The result of processing placement for nodes to be added to the graph.
|
|
72
|
+
*/
|
|
73
|
+
export type ProcessPlacementResult = ProcessPlacementResultEntry[];
|
|
74
|
+
/**
|
|
75
|
+
* Entry in a peer context representing a resolved peer dependency.
|
|
76
|
+
*/
|
|
77
|
+
export type PeerContextEntry = {
|
|
78
|
+
/**
|
|
79
|
+
* True if this entry is currently being resolved and track by this
|
|
80
|
+
* peer context set, false in case this entry was inherit from a previous
|
|
81
|
+
* peer context set and should not be considered for resolution.
|
|
82
|
+
*/
|
|
83
|
+
active: boolean;
|
|
84
|
+
/** List of full Spec objects that are part of this peer context entry */
|
|
85
|
+
specs: Set<Spec>;
|
|
86
|
+
/** The target Node that satisfies all specs for this peer context entry */
|
|
87
|
+
target: Node | undefined;
|
|
88
|
+
/** The type of dependency this entry represents */
|
|
89
|
+
type: DependencySaveType;
|
|
90
|
+
/** Context dependent nodes that had dependencies resolved to this entry */
|
|
91
|
+
contextDependents: Set<Node>;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Input for adding an entry to peer contexts.
|
|
95
|
+
*/
|
|
96
|
+
export type PeerContextEntryInput = {
|
|
97
|
+
/** Node that depends on this resolved peer context set entry */
|
|
98
|
+
dependent?: Node;
|
|
99
|
+
/** Node this peer context entry resolves to */
|
|
100
|
+
target?: Node;
|
|
101
|
+
} & Dependency;
|
|
102
|
+
/**
|
|
103
|
+
* Represents resolved peer dependencies in a given append-nodes context.
|
|
104
|
+
*/
|
|
105
|
+
export type PeerContext = Map<string, PeerContextEntry> & {
|
|
106
|
+
index?: number;
|
|
107
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export * from './build.ts';
|
|
2
|
+
export * from './edge.ts';
|
|
3
|
+
export * from './graph.ts';
|
|
4
|
+
export * from './node.ts';
|
|
5
|
+
export * from './dependencies.ts';
|
|
6
|
+
export * from './fixup-added-names.ts';
|
|
7
|
+
export * from './lockfile/types.ts';
|
|
8
|
+
export * from './visualization/json-output.ts';
|
|
9
|
+
export * from './visualization/human-readable-output.ts';
|
|
10
|
+
export * from './visualization/mermaid-output.ts';
|
|
11
|
+
export * from './stringify-node.ts';
|
|
12
|
+
export * from './install.ts';
|
|
13
|
+
export * from './uninstall.ts';
|
|
14
|
+
export * from './update.ts';
|
|
15
|
+
export * from './diff.ts';
|
|
16
|
+
export * from './modifiers.ts';
|
|
17
|
+
export * from './virtual-root.ts';
|
|
18
|
+
import type { LoadOptions as ActualLoadOptions } from './actual/load.ts';
|
|
19
|
+
export declare const actual: {
|
|
20
|
+
load: (options: ActualLoadOptions) => import("./graph.ts").Graph;
|
|
21
|
+
};
|
|
22
|
+
import type { LoadOptions as LockfileLoadOptions } from './lockfile/load.ts';
|
|
23
|
+
export declare const lockfile: {
|
|
24
|
+
load: (options: LockfileLoadOptions) => import("./graph.ts").Graph;
|
|
25
|
+
loadEdges: (graph: import("@vltpkg/types").GraphLike, edges: import("./lockfile/types.ts").LockfileData["edges"], options: import("@vltpkg/spec").SpecOptions) => void;
|
|
26
|
+
loadNodes: (graph: import("@vltpkg/types").GraphLike, nodes: import("./lockfile/types.ts").LockfileData["nodes"], options: import("@vltpkg/spec").SpecOptions, actual?: import("@vltpkg/types").GraphLike, throwOnMissingManifest?: boolean) => void;
|
|
27
|
+
save: (options: Omit<import("./lockfile/save.ts").SaveOptions, "saveManifests">) => void;
|
|
28
|
+
};
|
|
29
|
+
export type { ActualLoadOptions, LockfileLoadOptions };
|
|
30
|
+
export type { SaveOptions } from './lockfile/save.ts';
|
|
31
|
+
import type { BuildIdealOptions } from './ideal/build.ts';
|
|
32
|
+
export type { BuildIdealOptions };
|
|
33
|
+
export declare const ideal: {
|
|
34
|
+
build: (options: BuildIdealOptions) => Promise<import("./graph.ts").Graph>;
|
|
35
|
+
};
|
|
36
|
+
export { reify } from './reify/index.ts';
|
|
37
|
+
export type { ReifyOptions } from './reify/index.ts';
|
|
38
|
+
export type { BuildResult } from './reify/build.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export * from "./build.js";
|
|
2
|
+
export * from "./edge.js";
|
|
3
|
+
export * from "./graph.js";
|
|
4
|
+
export * from "./node.js";
|
|
5
|
+
export * from "./dependencies.js";
|
|
6
|
+
export * from "./fixup-added-names.js";
|
|
7
|
+
export * from "./lockfile/types.js";
|
|
8
|
+
export * from "./visualization/json-output.js";
|
|
9
|
+
export * from "./visualization/human-readable-output.js";
|
|
10
|
+
export * from "./visualization/mermaid-output.js";
|
|
11
|
+
export * from "./stringify-node.js";
|
|
12
|
+
export * from "./install.js";
|
|
13
|
+
export * from "./uninstall.js";
|
|
14
|
+
export * from "./update.js";
|
|
15
|
+
export * from "./diff.js";
|
|
16
|
+
export * from "./modifiers.js";
|
|
17
|
+
export * from "./virtual-root.js";
|
|
18
|
+
import { load as actualLoad } from "./actual/load.js";
|
|
19
|
+
export const actual = { load: actualLoad };
|
|
20
|
+
import { load as lockfileLoad } from "./lockfile/load.js";
|
|
21
|
+
import { loadEdges } from "./lockfile/load-edges.js";
|
|
22
|
+
import { loadNodes } from "./lockfile/load-nodes.js";
|
|
23
|
+
import { save } from "./lockfile/save.js";
|
|
24
|
+
export const lockfile = {
|
|
25
|
+
load: lockfileLoad,
|
|
26
|
+
loadEdges,
|
|
27
|
+
loadNodes,
|
|
28
|
+
save,
|
|
29
|
+
};
|
|
30
|
+
import { build } from "./ideal/build.js";
|
|
31
|
+
export const ideal = { build };
|
|
32
|
+
export { reify } from "./reify/index.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PackageInfoClient } from '@vltpkg/package-info';
|
|
2
|
+
import type { LoadOptions } from './actual/load.ts';
|
|
3
|
+
import type { AddImportersDependenciesMap } from './dependencies.ts';
|
|
4
|
+
import type { DepID } from '@vltpkg/dep-id';
|
|
5
|
+
import type { Graph } from './index.ts';
|
|
6
|
+
export type InstallOptions = LoadOptions & {
|
|
7
|
+
packageInfo: PackageInfoClient;
|
|
8
|
+
cleanInstall?: boolean;
|
|
9
|
+
allowScripts: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const install: (options: InstallOptions, add?: AddImportersDependenciesMap) => Promise<{
|
|
12
|
+
graph: Graph;
|
|
13
|
+
diff: undefined;
|
|
14
|
+
buildQueue?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
buildQueue: DepID[] | undefined;
|
|
17
|
+
graph: Graph;
|
|
18
|
+
diff: import("./diff.ts").Diff;
|
|
19
|
+
}>;
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { load as actualLoad } from "./actual/load.js";
|
|
2
|
+
import { build as idealBuild } from "./ideal/build.js";
|
|
3
|
+
import { reify } from "./reify/index.js";
|
|
4
|
+
import { GraphModifier } from "./modifiers.js";
|
|
5
|
+
import { init } from '@vltpkg/init';
|
|
6
|
+
import { error } from '@vltpkg/error-cause';
|
|
7
|
+
import { asError } from '@vltpkg/types';
|
|
8
|
+
import { getDependencies } from "./dependencies.js";
|
|
9
|
+
import { RollbackRemove } from '@vltpkg/rollback-remove';
|
|
10
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
import { load as loadVirtual } from "./lockfile/load.js";
|
|
13
|
+
import { getImporterSpecs } from "./ideal/get-importer-specs.js";
|
|
14
|
+
import { lockfile } from "./index.js";
|
|
15
|
+
import { updatePackageJson } from "./reify/update-importers-package-json.js";
|
|
16
|
+
import { Monorepo } from '@vltpkg/workspaces';
|
|
17
|
+
export const install = async (options, add) => {
|
|
18
|
+
// Validate incompatible options
|
|
19
|
+
if (options.lockfileOnly && options.cleanInstall) {
|
|
20
|
+
throw error('Cannot use --lockfile-only with --clean-install (ci command). Clean install requires filesystem operations.');
|
|
21
|
+
}
|
|
22
|
+
if (options.expectLockfile || options.frozenLockfile) {
|
|
23
|
+
const lockfilePath = resolve(options.projectRoot, 'vlt-lock.json');
|
|
24
|
+
if (!existsSync(lockfilePath)) {
|
|
25
|
+
throw error('vlt-lock.json file is required when using --expect-lockfile, --frozen-lockfile, or ci command', {
|
|
26
|
+
path: lockfilePath,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
let mainManifest = undefined;
|
|
31
|
+
try {
|
|
32
|
+
mainManifest = options.packageJson.read(options.projectRoot);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (asError(err).message === 'Could not read package.json file') {
|
|
36
|
+
await init({ cwd: options.projectRoot });
|
|
37
|
+
mainManifest = options.packageJson.read(options.projectRoot, {
|
|
38
|
+
reload: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Load an unfiltered monorepo to ensure all workspace importers are
|
|
46
|
+
// included in the graph. This is necessary because the options.monorepo
|
|
47
|
+
// may be filtered by -w/--workspace flags, which would cause nodes/edges
|
|
48
|
+
// from other workspaces to be lost during graph construction.
|
|
49
|
+
const fullMonorepo = Monorepo.maybeLoad(options.projectRoot, {
|
|
50
|
+
packageJson: options.packageJson,
|
|
51
|
+
scurry: options.scurry,
|
|
52
|
+
});
|
|
53
|
+
if (options.frozenLockfile) {
|
|
54
|
+
// validates no add/remove operations are requested
|
|
55
|
+
if (add?.modifiedDependencies) {
|
|
56
|
+
const dependencies = [];
|
|
57
|
+
for (const [, deps] of add) {
|
|
58
|
+
for (const [name] of deps) {
|
|
59
|
+
dependencies.push(name);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw error('Cannot add dependencies when using --frozen-lockfile', { found: dependencies.join(', ') });
|
|
63
|
+
}
|
|
64
|
+
const lockfileGraph = loadVirtual({
|
|
65
|
+
...options,
|
|
66
|
+
mainManifest,
|
|
67
|
+
monorepo: fullMonorepo,
|
|
68
|
+
});
|
|
69
|
+
const emptyAdd = Object.assign(new Map(), { modifiedDependencies: false });
|
|
70
|
+
const emptyRemove = Object.assign(new Map(), {
|
|
71
|
+
modifiedDependencies: false,
|
|
72
|
+
});
|
|
73
|
+
const importerSpecs = getImporterSpecs({
|
|
74
|
+
graph: lockfileGraph,
|
|
75
|
+
add: emptyAdd,
|
|
76
|
+
remove: emptyRemove,
|
|
77
|
+
...options,
|
|
78
|
+
});
|
|
79
|
+
// Check for spec changes by comparing package.json specs with lockfile edges
|
|
80
|
+
const specChanges = [];
|
|
81
|
+
for (const importer of lockfileGraph.importers) {
|
|
82
|
+
const deps = getDependencies(importer, options);
|
|
83
|
+
for (const [depName, dep] of deps) {
|
|
84
|
+
const edge = importer.edgesOut.get(depName);
|
|
85
|
+
if (edge?.spec) {
|
|
86
|
+
if (edge.spec.toString() !== dep.spec.toString()) {
|
|
87
|
+
const node = lockfileGraph.nodes.get(importer.id);
|
|
88
|
+
/* c8 ignore next */
|
|
89
|
+
const location = node?.location || importer.id;
|
|
90
|
+
specChanges.push(` ${location}: ${depName} spec changed from "${edge.spec}" to "${dep.spec}"`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (importerSpecs.add.modifiedDependencies ||
|
|
96
|
+
importerSpecs.remove.modifiedDependencies ||
|
|
97
|
+
specChanges.length > 0) {
|
|
98
|
+
const details = [];
|
|
99
|
+
if (specChanges.length > 0) {
|
|
100
|
+
details.push(...specChanges);
|
|
101
|
+
}
|
|
102
|
+
for (const [importerId, deps] of importerSpecs.add) {
|
|
103
|
+
if (deps.size > 0) {
|
|
104
|
+
const node = lockfileGraph.nodes.get(importerId);
|
|
105
|
+
const location = node?.location || importerId;
|
|
106
|
+
const depNames = Array.from(deps.keys());
|
|
107
|
+
const depLabelAdd = deps.size === 1 ? 'dependency' : 'dependencies';
|
|
108
|
+
details.push(` ${location}: ${deps.size} ${depLabelAdd} to add (${depNames.join(', ')})`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (const [importerId, deps] of importerSpecs.remove) {
|
|
112
|
+
if (deps.size > 0) {
|
|
113
|
+
const node = lockfileGraph.nodes.get(importerId);
|
|
114
|
+
const location = node?.location || importerId;
|
|
115
|
+
const depNames = Array.from(deps);
|
|
116
|
+
const depLabelRemove = deps.size === 1 ?
|
|
117
|
+
'dependency'
|
|
118
|
+
: /* c8 ignore next */ 'dependencies';
|
|
119
|
+
details.push(` ${location}: ${deps.size} ${depLabelRemove} to remove (${depNames.join(', ')})`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const lockfilePath = resolve(options.projectRoot, 'vlt-lock.json');
|
|
123
|
+
throw error('Lockfile is out of sync with package.json. Run "vlt install" to update.\n' +
|
|
124
|
+
details.join('\n'), {
|
|
125
|
+
path: lockfilePath,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const remover = new RollbackRemove();
|
|
130
|
+
if (options.cleanInstall) {
|
|
131
|
+
const nodeModulesPath = resolve(options.projectRoot, 'node_modules');
|
|
132
|
+
if (existsSync(nodeModulesPath)) {
|
|
133
|
+
await remover.rm(nodeModulesPath);
|
|
134
|
+
remover.confirm();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const remove = Object.assign(new Map(), {
|
|
139
|
+
modifiedDependencies: false,
|
|
140
|
+
});
|
|
141
|
+
const modifiers = GraphModifier.maybeLoad(options);
|
|
142
|
+
let act = actualLoad({
|
|
143
|
+
...options,
|
|
144
|
+
mainManifest,
|
|
145
|
+
loadManifests: true,
|
|
146
|
+
modifiers: undefined, // modifiers should not be used here
|
|
147
|
+
monorepo: fullMonorepo,
|
|
148
|
+
});
|
|
149
|
+
// if the actual graph has no dependencies, it's simpler to ignore it
|
|
150
|
+
// this allows us to check for its availability later on for properly
|
|
151
|
+
// handling situations like resetting edges for refreshing the ideal graph
|
|
152
|
+
if (act.importers.size === act.nodes.size) {
|
|
153
|
+
act = undefined;
|
|
154
|
+
}
|
|
155
|
+
const graph = await idealBuild({
|
|
156
|
+
...options,
|
|
157
|
+
actual: act,
|
|
158
|
+
add,
|
|
159
|
+
mainManifest,
|
|
160
|
+
loadManifests: true,
|
|
161
|
+
modifiers,
|
|
162
|
+
remove,
|
|
163
|
+
remover,
|
|
164
|
+
monorepo: fullMonorepo,
|
|
165
|
+
});
|
|
166
|
+
// If lockfileOnly is enabled, skip reify and only save the lockfile
|
|
167
|
+
if (options.lockfileOnly) {
|
|
168
|
+
// Save only the main lockfile, skip all filesystem operations
|
|
169
|
+
lockfile.save({ graph, modifiers });
|
|
170
|
+
const saveImportersPackageJson =
|
|
171
|
+
/* c8 ignore next */
|
|
172
|
+
add?.modifiedDependencies || remove.modifiedDependencies ?
|
|
173
|
+
updatePackageJson({
|
|
174
|
+
...options,
|
|
175
|
+
add,
|
|
176
|
+
graph,
|
|
177
|
+
remove,
|
|
178
|
+
})
|
|
179
|
+
: undefined;
|
|
180
|
+
saveImportersPackageJson?.();
|
|
181
|
+
return { graph, diff: undefined };
|
|
182
|
+
}
|
|
183
|
+
const { diff, buildQueue } = await reify({
|
|
184
|
+
...options,
|
|
185
|
+
add,
|
|
186
|
+
actual: act,
|
|
187
|
+
graph,
|
|
188
|
+
loadManifests: true,
|
|
189
|
+
modifiers,
|
|
190
|
+
remove,
|
|
191
|
+
remover,
|
|
192
|
+
});
|
|
193
|
+
return { buildQueue, graph, diff };
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
/* c8 ignore next */
|
|
197
|
+
await remover.rollback().catch(() => { });
|
|
198
|
+
// Remove hidden lockfile on failure
|
|
199
|
+
try {
|
|
200
|
+
const hiddenLockfile = resolve(options.projectRoot, 'node_modules/.vlt-lock.json');
|
|
201
|
+
if (existsSync(hiddenLockfile)) {
|
|
202
|
+
rmSync(hiddenLockfile, { force: true });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch { }
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
};
|