@vltpkg/graph 1.0.0-rc.23 → 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.
Files changed (109) hide show
  1. package/dist/actual/load.d.ts +107 -0
  2. package/dist/actual/load.js +336 -0
  3. package/dist/browser.d.ts +14 -0
  4. package/dist/browser.js +16 -0
  5. package/dist/build.d.ts +28 -0
  6. package/dist/build.js +78 -0
  7. package/dist/dependencies.d.ts +65 -0
  8. package/dist/dependencies.js +111 -0
  9. package/dist/diff.d.ts +119 -0
  10. package/dist/diff.js +151 -0
  11. package/dist/edge.d.ts +46 -0
  12. package/dist/edge.js +77 -0
  13. package/dist/fixup-added-names.d.ts +18 -0
  14. package/dist/fixup-added-names.js +46 -0
  15. package/dist/graph.d.ts +153 -0
  16. package/dist/graph.js +444 -0
  17. package/dist/ideal/append-nodes.d.ts +31 -0
  18. package/dist/ideal/append-nodes.js +560 -0
  19. package/dist/ideal/build-ideal-from-starting-graph.d.ts +14 -0
  20. package/dist/ideal/build-ideal-from-starting-graph.js +69 -0
  21. package/dist/ideal/build.d.ts +40 -0
  22. package/dist/ideal/build.js +84 -0
  23. package/dist/ideal/get-importer-specs.d.ts +20 -0
  24. package/dist/ideal/get-importer-specs.js +180 -0
  25. package/dist/ideal/peers.d.ts +160 -0
  26. package/dist/ideal/peers.js +696 -0
  27. package/dist/ideal/refresh-ideal-graph.d.ts +43 -0
  28. package/dist/ideal/refresh-ideal-graph.js +62 -0
  29. package/dist/ideal/remove-satisfied-specs.d.ts +7 -0
  30. package/dist/ideal/remove-satisfied-specs.js +34 -0
  31. package/dist/ideal/sorting.d.ts +45 -0
  32. package/dist/ideal/sorting.js +70 -0
  33. package/dist/ideal/types.d.ts +107 -0
  34. package/dist/ideal/types.js +1 -0
  35. package/dist/index.d.ts +38 -0
  36. package/dist/index.js +32 -0
  37. package/dist/install.d.ts +19 -0
  38. package/dist/install.js +208 -0
  39. package/dist/lockfile/load-edges.d.ts +11 -0
  40. package/dist/lockfile/load-edges.js +105 -0
  41. package/dist/lockfile/load-nodes.d.ts +4 -0
  42. package/dist/lockfile/load-nodes.js +101 -0
  43. package/dist/lockfile/load.d.ts +45 -0
  44. package/dist/lockfile/load.js +84 -0
  45. package/dist/lockfile/save.d.ts +30 -0
  46. package/dist/lockfile/save.js +174 -0
  47. package/dist/lockfile/types.d.ts +95 -0
  48. package/dist/lockfile/types.js +49 -0
  49. package/dist/modifiers.d.ts +188 -0
  50. package/dist/modifiers.js +329 -0
  51. package/dist/node.d.ts +234 -0
  52. package/dist/node.js +388 -0
  53. package/dist/non-empty-list.d.ts +2 -0
  54. package/dist/non-empty-list.js +2 -0
  55. package/dist/reify/add-edge.d.ts +9 -0
  56. package/dist/reify/add-edge.js +71 -0
  57. package/dist/reify/add-edges.d.ts +4 -0
  58. package/dist/reify/add-edges.js +12 -0
  59. package/dist/reify/add-nodes.d.ts +6 -0
  60. package/dist/reify/add-nodes.js +16 -0
  61. package/dist/reify/bin-chmod.d.ts +10 -0
  62. package/dist/reify/bin-chmod.js +38 -0
  63. package/dist/reify/build.d.ts +13 -0
  64. package/dist/reify/build.js +111 -0
  65. package/dist/reify/calculate-save-value.d.ts +2 -0
  66. package/dist/reify/calculate-save-value.js +50 -0
  67. package/dist/reify/check-needed-build.d.ts +34 -0
  68. package/dist/reify/check-needed-build.js +71 -0
  69. package/dist/reify/delete-edge.d.ts +4 -0
  70. package/dist/reify/delete-edge.js +27 -0
  71. package/dist/reify/delete-edges.d.ts +4 -0
  72. package/dist/reify/delete-edges.js +13 -0
  73. package/dist/reify/delete-nodes.d.ts +4 -0
  74. package/dist/reify/delete-nodes.js +15 -0
  75. package/dist/reify/extract-node.d.ts +23 -0
  76. package/dist/reify/extract-node.js +83 -0
  77. package/dist/reify/index.d.ts +34 -0
  78. package/dist/reify/index.js +161 -0
  79. package/dist/reify/internal-hoist.d.ts +8 -0
  80. package/dist/reify/internal-hoist.js +133 -0
  81. package/dist/reify/optional-fail.d.ts +15 -0
  82. package/dist/reify/optional-fail.js +15 -0
  83. package/dist/reify/rollback.d.ts +4 -0
  84. package/dist/reify/rollback.js +23 -0
  85. package/dist/reify/update-importers-package-json.d.ts +35 -0
  86. package/dist/reify/update-importers-package-json.js +122 -0
  87. package/dist/remove-optional-subgraph.d.ts +33 -0
  88. package/dist/remove-optional-subgraph.js +47 -0
  89. package/dist/resolve-save-type.d.ts +5 -0
  90. package/dist/resolve-save-type.js +4 -0
  91. package/dist/stringify-node.d.ts +2 -0
  92. package/dist/stringify-node.js +32 -0
  93. package/dist/transfer-data/load.d.ts +43 -0
  94. package/dist/transfer-data/load.js +175 -0
  95. package/dist/uninstall.d.ts +14 -0
  96. package/dist/uninstall.js +75 -0
  97. package/dist/update.d.ts +12 -0
  98. package/dist/update.js +73 -0
  99. package/dist/virtual-root.d.ts +15 -0
  100. package/dist/virtual-root.js +78 -0
  101. package/dist/visualization/human-readable-output.d.ts +26 -0
  102. package/dist/visualization/human-readable-output.js +163 -0
  103. package/dist/visualization/json-output.d.ts +41 -0
  104. package/dist/visualization/json-output.js +50 -0
  105. package/dist/visualization/mermaid-output.d.ts +17 -0
  106. package/dist/visualization/mermaid-output.js +170 -0
  107. package/dist/visualization/object-like-output.d.ts +2 -0
  108. package/dist/visualization/object-like-output.js +47 -0
  109. package/package.json +22 -22
@@ -0,0 +1,107 @@
1
+ import { Spec } from '@vltpkg/spec';
2
+ import { Graph } from '../graph.ts';
3
+ import type { DepID } from '@vltpkg/dep-id';
4
+ import type { PackageJson } from '@vltpkg/package-json';
5
+ import type { SpecOptions } from '@vltpkg/spec';
6
+ import type { NormalizedManifest } from '@vltpkg/types';
7
+ import type { Monorepo } from '@vltpkg/workspaces';
8
+ import type { Path, PathScurry } from 'path-scurry';
9
+ import type { GraphModifier } from '../modifiers.ts';
10
+ export type LoadOptions = SpecOptions & {
11
+ /**
12
+ * The project root dirname.
13
+ */
14
+ projectRoot: string;
15
+ /**
16
+ * The project root manifest.
17
+ */
18
+ mainManifest?: NormalizedManifest;
19
+ /**
20
+ * The graph modifiers helper object.
21
+ */
22
+ modifiers?: GraphModifier;
23
+ /**
24
+ * A {@link Monorepo} object, for managing workspaces
25
+ */
26
+ monorepo?: Monorepo;
27
+ /**
28
+ * A {@link PackageJson} object, for sharing manifest caches
29
+ */
30
+ packageJson: PackageJson;
31
+ /**
32
+ * A {@link PathScurry} object, for use in globs
33
+ */
34
+ scurry: PathScurry;
35
+ /**
36
+ * If set to `false`, `actual.load` will not load any `package.json`
37
+ * files while traversing the file system.
38
+ *
39
+ * The resulting {@link Graph} from loading with `loadManifests=false`
40
+ * has no information on dependency types or the specs defined and
41
+ * no information on missing and extraneous dependencies.
42
+ */
43
+ loadManifests?: boolean;
44
+ /**
45
+ * If set to `true`, then do not shortcut the process by reading the
46
+ * hidden lockfile at `node_modules/.vlt-lock.json`
47
+ */
48
+ skipHiddenLockfile?: boolean;
49
+ /**
50
+ * Load only importers into the graph if the modifiers have changed.
51
+ */
52
+ skipLoadingNodesOnModifiersChange?: boolean;
53
+ /**
54
+ * If set to `true`, fail if lockfile is missing or out of date.
55
+ * Used by ci command to enforce lockfile integrity.
56
+ */
57
+ expectLockfile?: boolean;
58
+ /**
59
+ * If set to `true`, fail if lockfile is missing or out of sync with package.json.
60
+ * Prevents any lockfile modifications and is stricter than expectLockfile.
61
+ */
62
+ frozenLockfile?: boolean;
63
+ /**
64
+ * If set to `true`, only update the lockfile without performing any node_modules
65
+ * operations. Skips package extraction, filesystem operations, and hidden lockfile saves.
66
+ */
67
+ lockfileOnly?: boolean;
68
+ };
69
+ export type ReadEntry = {
70
+ alias: string;
71
+ name: string;
72
+ realpath: Path;
73
+ };
74
+ /**
75
+ * The configuration object type as it is saved in the `.vlt/vlt.json`
76
+ */
77
+ export type StoreConfigObject = {
78
+ modifiers: Record<string, string> | undefined;
79
+ };
80
+ /**
81
+ * Checks if a given object is a {@link StoreConfigObject}.
82
+ */
83
+ export declare const isStoreConfigObject: (obj: unknown) => obj is StoreConfigObject;
84
+ /**
85
+ * Returns a {@link StoreConfigObject} from a given object.
86
+ * Throws a TypeError if the object can't be converted.
87
+ */
88
+ export declare const asStoreConfigObject: (obj: unknown) => StoreConfigObject;
89
+ /**
90
+ * Returns a {@link DepID} for a given spec and path, if the spec is
91
+ * path-based or a registry spec, otherwise returns `undefined`.
92
+ */
93
+ export declare const getPathBasedId: (spec: Spec, path: Path) => DepID | undefined;
94
+ /**
95
+ * Verify that all importer node_modules directories exist on disk.
96
+ * The hidden lockfile may report deps as installed even when a
97
+ * workspace's node_modules has been manually deleted. Throws if
98
+ * any importer node_modules directory is missing, causing the
99
+ * caller to fall through to filesystem-based graph loading.
100
+ */
101
+ export declare const verifyImporterNodeModules: (graph: Graph, projectRoot: string) => void;
102
+ /**
103
+ * Read the file system looking for `node_modules` folders and
104
+ * returns a new {@link Graph} that represents the relationship
105
+ * between the dependencies found.
106
+ */
107
+ export declare const load: (options: LoadOptions) => Graph;
@@ -0,0 +1,336 @@
1
+ import { asDepID, hydrate, joinDepIDTuple, joinExtra, splitDepID, splitExtra, } from '@vltpkg/dep-id';
2
+ import { Spec } from '@vltpkg/spec';
3
+ import { graphStep } from '@vltpkg/output';
4
+ import { isObject } from '@vltpkg/types';
5
+ import { shorten, getRawDependencies, getDependencies, } from "../dependencies.js";
6
+ import { Graph } from "../graph.js";
7
+ import { loadHidden } from "../lockfile/load.js";
8
+ import { saveHidden } from "../lockfile/save.js";
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { resolve } from 'node:path';
11
+ /**
12
+ * Checks if a given object is a {@link StoreConfigObject}.
13
+ */
14
+ export const isStoreConfigObject = (obj) => isObject(obj) &&
15
+ Object.prototype.hasOwnProperty.call(obj, 'modifiers') &&
16
+ isObject(obj.modifiers);
17
+ /**
18
+ * Returns a {@link StoreConfigObject} from a given object.
19
+ * Throws a TypeError if the object can't be converted.
20
+ */
21
+ export const asStoreConfigObject = (obj) => {
22
+ if (!isStoreConfigObject(obj)) {
23
+ throw new TypeError(`Expected a store config object, got ${obj}`);
24
+ }
25
+ return obj;
26
+ };
27
+ // path-based refer to the types of dependencies that are directly linked to
28
+ // their real location in the file system and thus will not have an entry
29
+ // in the `node_modules/.vlt` store
30
+ const pathBasedType = new Set(['file', 'workspace']);
31
+ const isPathBasedType = (type) => pathBasedType.has(type);
32
+ /**
33
+ * Returns a {@link DepID} for a given spec and path, if the spec is
34
+ * path-based or a registry spec, otherwise returns `undefined`.
35
+ */
36
+ export const getPathBasedId = (spec, path) => isPathBasedType(spec.type) ?
37
+ joinDepIDTuple([spec.type, path.relativePosix()])
38
+ : findDepID(path);
39
+ /**
40
+ * Retrieve the {@link DepID} for a given package from its location.
41
+ */
42
+ const findDepID = ({ parent, name }) => parent?.name === '.vlt' ? asDepID(name)
43
+ : parent?.isCWD === false ? findDepID(parent)
44
+ : undefined;
45
+ /**
46
+ * Retrieves the closest `node_modules` parent {@link Path} found.
47
+ */
48
+ const findNodeModules = ({ parent, name, isCWD, }) => parent?.name === 'node_modules' ? parent
49
+ : parent && name !== '.vlt' && !isCWD ? findNodeModules(parent)
50
+ : undefined;
51
+ /**
52
+ * Retrieves the scoped-normalized package name from its {@link Path}.
53
+ */
54
+ const findName = ({ parent, name }) => parent?.name.startsWith('@') ? `${parent.name}/${name}` : name;
55
+ /**
56
+ * Helper function that gets a modified {@link Spec} when finding a modifier
57
+ * that applies to a given dependency. Otherwise returns the original spec
58
+ * value and no queryModifier.
59
+ */
60
+ const maybeApplyModifierToSpec = (spec, depName, modifierRefs) => {
61
+ const activeModifier = modifierRefs?.get(depName);
62
+ const queryModifier = activeModifier?.modifier.query;
63
+ const completeModifier = activeModifier &&
64
+ activeModifier.interactiveBreadcrumb.current ===
65
+ activeModifier.modifier.breadcrumb.last;
66
+ if (queryModifier &&
67
+ completeModifier &&
68
+ 'spec' in activeModifier.modifier) {
69
+ const modifiedSpec = activeModifier.modifier.spec;
70
+ modifiedSpec.overridden = true;
71
+ return { spec: modifiedSpec, queryModifier };
72
+ }
73
+ return { spec, queryModifier };
74
+ };
75
+ /**
76
+ * Reads the current directory defined at `currDir` and looks for folder
77
+ * names and their realpath resolution, normalizing scoped package names
78
+ * and removing any invalid symlinks from the list of items that should
79
+ * be parsed through in order to build the graph.
80
+ */
81
+ const readDir = (scurry, currDir, fromNodeName) => {
82
+ const res = new Set();
83
+ for (const entry of scurry.readdirSync(currDir)) {
84
+ // ignore any hidden files / folders
85
+ if (entry.name.startsWith('.'))
86
+ continue;
87
+ // scope folder found, it will need to be read and iterated over
88
+ // in order to find any scoped packages inside
89
+ if (entry.name.startsWith('@')) {
90
+ const scopedItems = readDir(scurry, entry, fromNodeName);
91
+ for (const scopedItem of scopedItems) {
92
+ res.add(scopedItem);
93
+ }
94
+ continue;
95
+ }
96
+ // skip anything that isn't a symlink, it's not an edge
97
+ if (!entry.isSymbolicLink())
98
+ continue;
99
+ // we'll need to learn what is the real path for this entry in order
100
+ // to retrieve the `location` and `id` properties for the node, if a
101
+ // realpath is not found just move on to the next element
102
+ const realpath = entry.realpathSync();
103
+ if (!realpath) {
104
+ continue;
105
+ }
106
+ // infer both the alias and proper package names, including scopes
107
+ const alias = findName(entry);
108
+ const name = findName(realpath);
109
+ res.add({
110
+ alias,
111
+ name,
112
+ realpath,
113
+ });
114
+ }
115
+ return res;
116
+ };
117
+ /**
118
+ * Parses the files located at `currDir` and place packages found inside
119
+ * as dependencies of `fromNode`, building the instantiated `graph`.
120
+ */
121
+ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, currDir) => {
122
+ const { loadManifests, modifiers } = options;
123
+ const dependencies = getRawDependencies(fromNode);
124
+ const seenDeps = new Set();
125
+ const readItems = readDir(scurry, currDir, fromNode.name);
126
+ // Get modifier references for this node's dependencies
127
+ const modifierRefs = modifiers?.tryDependencies(fromNode, [
128
+ ...getDependencies(fromNode, options).values(),
129
+ ]);
130
+ for (const { alias, name, realpath } of readItems) {
131
+ let node;
132
+ // tracks what dependencies have been seen
133
+ // so that we can mark missing dependencies
134
+ seenDeps.add(alias);
135
+ // places the package in the graph reading
136
+ // its manifest only if necessary
137
+ if (!loadManifests) {
138
+ const depId = findDepID(realpath);
139
+ if (depId) {
140
+ let h = hydrate(depId, alias, options);
141
+ // if the parsed registry value is using the default value, then
142
+ // the node should inherit the registry value from its parent node
143
+ if (h.type === 'registry' &&
144
+ h.registry === h.options.registry &&
145
+ fromNode.registry) {
146
+ h.registry = fromNode.registry;
147
+ }
148
+ // Check for active modifiers and replace spec even when not loading manifests
149
+ const { spec: modifiedSpec, queryModifier } = maybeApplyModifierToSpec(h, alias, modifierRefs);
150
+ h = modifiedSpec;
151
+ // graphs build with no manifest have no notion of
152
+ // dependency types and or spec definitions since those
153
+ // would have to be parsed from a manifest
154
+ node = graph.placePackage(fromNode, 'prod', // defaults to prod deps
155
+ h, // uses spec from hydrated id
156
+ {
157
+ name,
158
+ }, depId, joinExtra({ modifier: queryModifier }));
159
+ // Update active entry after placing package
160
+ const activeModifier = modifierRefs?.get(alias);
161
+ if (activeModifier && node) {
162
+ modifiers?.updateActiveEntry(node, activeModifier);
163
+ }
164
+ }
165
+ }
166
+ // retrieve references to the current folder name found in `fromNode`
167
+ // manifest listed dependencies, removing it from the map will leave
168
+ // a list of missing dependencies at the end of the iteration
169
+ const deps = dependencies.get(alias);
170
+ // in case this graph is skipping manifests, this next block might
171
+ // still need to execute, thus actually loading a manifest file, for
172
+ // edge-cases such as loading a linked node that is not going to have
173
+ // DepID info available in its realpath or extraneous nodes
174
+ if (!node) {
175
+ const mani = packageJson.read(realpath.fullpath());
176
+ // declares fallback default values for both depType and bareSpec
177
+ // in order to support loadManifests=false fallback for link nodes
178
+ const type = deps?.type || 'dependencies';
179
+ const bareSpec = deps?.bareSpec || '*';
180
+ const depType = shorten(type, alias, fromNode.manifest);
181
+ let spec = Spec.parse(alias, bareSpec, {
182
+ ...options,
183
+ registry: fromNode.registry,
184
+ });
185
+ // Check for active modifiers and replace spec if a modifier is complete
186
+ const { spec: modifiedSpec, queryModifier } = maybeApplyModifierToSpec(spec, alias, modifierRefs);
187
+ spec = modifiedSpec;
188
+ const maybeId = getPathBasedId(spec, realpath);
189
+ let peerSetHash;
190
+ if (maybeId) {
191
+ // parses extra info from depID to retrieve peerSetHash
192
+ try {
193
+ const tuple = splitDepID(maybeId);
194
+ const type = tuple[0];
195
+ const extra = type === 'registry' || type === 'git' ?
196
+ tuple[3]
197
+ : tuple[2];
198
+ peerSetHash =
199
+ extra ? splitExtra(extra).peerSetHash : undefined;
200
+ /* c8 ignore next - impossible: getPathBasedId asserts valid dep id */
201
+ }
202
+ catch { }
203
+ }
204
+ node = graph.placePackage(fromNode, depType, spec, mani, maybeId, joinExtra({ modifier: queryModifier, peerSetHash }));
205
+ // Update active entry after placing package
206
+ const activeModifier = modifierRefs?.get(alias);
207
+ if (activeModifier && node) {
208
+ modifiers?.updateActiveEntry(node, activeModifier);
209
+ }
210
+ }
211
+ if (node) {
212
+ // If a found dependency is not declared in any of the original
213
+ // node dependencies, then add an edge to the graph pointing to it
214
+ // and mark it as extraneous.
215
+ //
216
+ // This only makes sense if full manifests are being loaded
217
+ // so that we have reference to dependencies info.
218
+ if (loadManifests && !deps) {
219
+ const [edge] = node.edgesIn;
220
+ if (edge) {
221
+ graph.extraneousDependencies.add(edge);
222
+ }
223
+ }
224
+ // for a succesfully created node, add its location
225
+ // property and queue up to read its dependencies
226
+ node.location = `./${realpath.relativePosix()}`;
227
+ const node_modules = findNodeModules(realpath);
228
+ // queue items up to continue parsing dirs in case a node was succesfully
229
+ // placed in the graph and its node_modules folder was correctly found
230
+ if (node_modules) {
231
+ depsFound.set(node, node_modules);
232
+ }
233
+ }
234
+ }
235
+ // any remaining dependencies that have not been found
236
+ // when reading the directory should be marked as missing
237
+ for (const { name, type, bareSpec } of dependencies.values()) {
238
+ if (!seenDeps.has(name)) {
239
+ const depType = shorten(type, name, fromNode.manifest);
240
+ let spec = Spec.parse(name, bareSpec, {
241
+ ...options,
242
+ registry: fromNode.registry,
243
+ });
244
+ // Check for active modifiers and replace spec for missing dependencies
245
+ const { spec: modifiedSpec, queryModifier } = maybeApplyModifierToSpec(spec, name, modifierRefs);
246
+ spec = modifiedSpec;
247
+ graph.placePackage(fromNode, depType, spec, undefined, undefined, joinExtra({ modifier: queryModifier }));
248
+ }
249
+ }
250
+ };
251
+ /**
252
+ * Verify that all importer node_modules directories exist on disk.
253
+ * The hidden lockfile may report deps as installed even when a
254
+ * workspace's node_modules has been manually deleted. Throws if
255
+ * any importer node_modules directory is missing, causing the
256
+ * caller to fall through to filesystem-based graph loading.
257
+ */
258
+ export const verifyImporterNodeModules = (graph, projectRoot) => {
259
+ for (const importer of graph.importers) {
260
+ const nm = resolve(projectRoot, importer.location, 'node_modules');
261
+ if (!existsSync(nm)) {
262
+ throw new Error(`Missing node_modules for importer: ${importer.location}`);
263
+ }
264
+ }
265
+ };
266
+ /**
267
+ * Read the file system looking for `node_modules` folders and
268
+ * returns a new {@link Graph} that represents the relationship
269
+ * between the dependencies found.
270
+ */
271
+ export const load = (options) => {
272
+ const done = graphStep('actual');
273
+ const { modifiers, monorepo, projectRoot, packageJson, scurry, skipHiddenLockfile = false, skipLoadingNodesOnModifiersChange = false, } = options;
274
+ const mainManifest = options.mainManifest ?? packageJson.read(projectRoot);
275
+ if (!skipHiddenLockfile) {
276
+ try {
277
+ // if we reach here, the hidden lockfile is valid
278
+ const graph = loadHidden({
279
+ ...options,
280
+ projectRoot,
281
+ mainManifest,
282
+ packageJson,
283
+ monorepo,
284
+ scurry,
285
+ });
286
+ verifyImporterNodeModules(graph, projectRoot);
287
+ done();
288
+ return graph;
289
+ }
290
+ catch {
291
+ // Hidden lockfile is cache-only, safe to regenerate on any error
292
+ // as it will fall back to filesystem traversal
293
+ // TODO: Warn version mismatch to @vltpkg/output for debugging
294
+ }
295
+ }
296
+ const graph = new Graph({ ...options, mainManifest });
297
+ // retrieve the configuration object from the store
298
+ let storeConfig = { modifiers: undefined };
299
+ try {
300
+ storeConfig = asStoreConfigObject(JSON.parse(readFileSync(scurry.resolve('node_modules/.vlt/vlt.json'), 'utf8')));
301
+ }
302
+ catch { }
303
+ const storeModifiers = JSON.stringify(storeConfig.modifiers ?? {});
304
+ const optionsModifiers = JSON.stringify(modifiers?.config);
305
+ const modifiersChanged = storeModifiers !== optionsModifiers;
306
+ const shouldLoadDependencies = !(skipLoadingNodesOnModifiersChange && modifiersChanged);
307
+ // will only skip loading dependencies if the
308
+ // skipLoadingNodesOnModifiersChange option is set to true
309
+ // and the current modifiers have not changed when compared
310
+ // to the modifiers stored in the `node_modules/.vlt/vlt.json` store config
311
+ if (shouldLoadDependencies) {
312
+ const depsFound = new Map();
313
+ // starts the list of initial folders to parse using the importer nodes
314
+ for (const importer of graph.importers) {
315
+ modifiers?.tryImporter(importer);
316
+ depsFound.set(importer, scurry.cwd.resolve(`${importer.location}/node_modules`));
317
+ }
318
+ // breadth-first traversal of the file system tree reading deps found
319
+ // starting from the node_modules folder of every importer in order to
320
+ // find the actual installed dependencies at each location
321
+ for (const [node, path] of depsFound.entries()) {
322
+ parseDir(options, scurry, packageJson, depsFound, graph, node, path);
323
+ }
324
+ // Clean up any pending modifier entries that were never completed
325
+ modifiers?.rollbackActiveEntries();
326
+ // caches the load result to the hidden lockfile when enabled
327
+ if (scurry.cwd.resolve('node_modules').lstatSync()?.isDirectory()) {
328
+ saveHidden({
329
+ ...options,
330
+ graph,
331
+ });
332
+ }
333
+ }
334
+ done();
335
+ return graph;
336
+ };
@@ -0,0 +1,14 @@
1
+ import { longDependencyTypes } from '@vltpkg/types';
2
+ import { asDependencyTypeShort, shorten } from './dependencies.ts';
3
+ import { getBooleanFlagsFromNum } from './lockfile/types.ts';
4
+ import { stringifyNode } from './stringify-node.ts';
5
+ export type { LoadResult, TransferData, } from './transfer-data/load.ts';
6
+ export * from './virtual-root.ts';
7
+ declare const lockfile: {
8
+ loadEdges: (graph: import("@vltpkg/types").GraphLike, edges: import("./lockfile/types.ts").LockfileData["edges"], options: import("@vltpkg/spec").SpecOptions) => void;
9
+ 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;
10
+ };
11
+ declare const transfer: {
12
+ load: (transfered: import("./transfer-data/load.ts").TransferData) => import("./transfer-data/load.ts").LoadResult;
13
+ };
14
+ export { asDependencyTypeShort, getBooleanFlagsFromNum, lockfile, longDependencyTypes, shorten, stringifyNode, transfer, };
@@ -0,0 +1,16 @@
1
+ import { longDependencyTypes } from '@vltpkg/types';
2
+ import { asDependencyTypeShort, shorten } from "./dependencies.js";
3
+ import { getBooleanFlagsFromNum } from "./lockfile/types.js";
4
+ import { stringifyNode } from "./stringify-node.js";
5
+ import { loadEdges } from "./lockfile/load-edges.js";
6
+ import { loadNodes } from "./lockfile/load-nodes.js";
7
+ import { load } from "./transfer-data/load.js";
8
+ export * from "./virtual-root.js";
9
+ const lockfile = {
10
+ loadEdges,
11
+ loadNodes,
12
+ };
13
+ const transfer = {
14
+ load,
15
+ };
16
+ export { asDependencyTypeShort, getBooleanFlagsFromNum, lockfile, longDependencyTypes, shorten, stringifyNode, transfer, };
@@ -0,0 +1,28 @@
1
+ import { Monorepo } from '@vltpkg/workspaces';
2
+ import type { BuildResult } from './reify/build.ts';
3
+ import type { LoadOptions } from './actual/load.ts';
4
+ /**
5
+ * Options for the build process
6
+ */
7
+ export interface BuildOptions extends LoadOptions {
8
+ /**
9
+ * Optional monorepo configuration. If not provided, will attempt to load from project.
10
+ */
11
+ monorepo?: Monorepo;
12
+ /**
13
+ * DSS query string to filter which nodes to build.
14
+ */
15
+ target: string;
16
+ }
17
+ /**
18
+ * Build the project based on actual graph state and build state from lockfile
19
+ *
20
+ * This function:
21
+ * 1. Loads the actual graph from node_modules
22
+ * 2. Loads build data from lockfile and transfers it to the actual graph
23
+ * 3. Constructs a Diff object representing what needs to be built
24
+ * 4. Filters nodes based on buildState === 'needed'
25
+ * 5. Calls the reify build process with the constructed diff
26
+ * 6. Persists build results to lockfile
27
+ */
28
+ export declare const build: (options: BuildOptions) => Promise<BuildResult>;
package/dist/build.js ADDED
@@ -0,0 +1,78 @@
1
+ import { Monorepo } from '@vltpkg/workspaces';
2
+ import { load as loadActual } from "./actual/load.js";
3
+ import { build as reifyBuild } from "./reify/build.js";
4
+ import { Diff } from "./diff.js";
5
+ import { Graph } from "./graph.js";
6
+ import { Query } from '@vltpkg/query';
7
+ import { SecurityArchive } from '@vltpkg/security-archive';
8
+ import { saveHidden } from "./lockfile/save.js";
9
+ /**
10
+ * Filter nodes using a DSS query string
11
+ */
12
+ const filterNodesByQuery = async (targetQuery, graph) => {
13
+ /* c8 ignore start */
14
+ const securityArchive = Query.hasSecuritySelectors(targetQuery) ?
15
+ await SecurityArchive.start({
16
+ nodes: [...graph.nodes.values()],
17
+ })
18
+ : undefined;
19
+ /* c8 ignore stop */
20
+ const edges = graph.edges;
21
+ const nodes = new Set(graph.nodes.values());
22
+ const importers = graph.importers;
23
+ const query = new Query({
24
+ edges,
25
+ nodes,
26
+ importers,
27
+ securityArchive,
28
+ });
29
+ const { nodes: resultNodes } = await query.search(targetQuery, {
30
+ signal: new AbortController().signal,
31
+ });
32
+ return new Set(resultNodes.map(node => node.id));
33
+ };
34
+ /**
35
+ * Build the project based on actual graph state and build state from lockfile
36
+ *
37
+ * This function:
38
+ * 1. Loads the actual graph from node_modules
39
+ * 2. Loads build data from lockfile and transfers it to the actual graph
40
+ * 3. Constructs a Diff object representing what needs to be built
41
+ * 4. Filters nodes based on buildState === 'needed'
42
+ * 5. Calls the reify build process with the constructed diff
43
+ * 6. Persists build results to lockfile
44
+ */
45
+ export const build = async (options) => {
46
+ const { projectRoot, packageJson, monorepo = Monorepo.maybeLoad(projectRoot), scurry, mainManifest = packageJson.read(projectRoot), target, ...loadOptions } = options;
47
+ // Load the actual graph from node_modules
48
+ const actualGraph = loadActual({
49
+ ...loadOptions,
50
+ projectRoot,
51
+ packageJson,
52
+ monorepo,
53
+ scurry,
54
+ loadManifests: true,
55
+ });
56
+ // Filter nodes using target query provided
57
+ const targetFilteredNodes = await filterNodesByQuery(target, actualGraph);
58
+ // Create a total diff including the actual graph as 'to'
59
+ const diff = new Diff(new Graph({
60
+ ...options,
61
+ projectRoot,
62
+ monorepo,
63
+ mainManifest,
64
+ }), actualGraph);
65
+ // Now tweak the diff object to only include the nodes that need to be built
66
+ // Filter by buildState === 'needed' and target query
67
+ diff.nodes.add = new Set([...diff.nodes.add].filter(node => node.buildState === 'needed'));
68
+ // Call the reify build process with the constructed diff
69
+ // this will only build the nodes that need to be built
70
+ // as part of `diff.nodes.add`
71
+ const buildResult = await reifyBuild(diff, packageJson, scurry, targetFilteredNodes);
72
+ // Save hidden lockfile with updated buildState
73
+ saveHidden({
74
+ ...options,
75
+ graph: actualGraph,
76
+ });
77
+ return buildResult;
78
+ };
@@ -0,0 +1,65 @@
1
+ import type { DepID } from '@vltpkg/dep-id';
2
+ import { Spec } from '@vltpkg/spec/browser';
3
+ import type { SpecOptions } from '@vltpkg/spec';
4
+ import type { DependencySaveType, DependencyTypeLong, DependencyTypeShort, Manifest, NodeLike } from '@vltpkg/types';
5
+ export declare const isDependencyTypeShort: (obj: unknown) => obj is DependencyTypeShort;
6
+ export declare const isDependencySaveType: (obj: unknown) => obj is DependencyTypeShort;
7
+ export declare const asDependencyTypeShort: (obj: unknown) => DependencyTypeShort;
8
+ /**
9
+ * Dependency entries info as defined in a package.json file.
10
+ */
11
+ export type RawDependency = {
12
+ name: string;
13
+ bareSpec: string;
14
+ type: DependencyTypeLong;
15
+ registry?: string;
16
+ };
17
+ /**
18
+ * Parsed dependency entries info.
19
+ */
20
+ export type Dependency = {
21
+ /**
22
+ * The parsed {@link Spec} object describing the dependency requirements.
23
+ */
24
+ spec: Spec;
25
+ /**
26
+ * The {@link DependencySaveType}, describing the way this dependency should
27
+ * be saved back to the manifest.
28
+ */
29
+ type: DependencySaveType;
30
+ };
31
+ /**
32
+ * A `Map` in which keys are {@link DepID} linking to another `Map` in which
33
+ * keys are the dependency names and values are {@link Dependency}. This
34
+ * structure represents dependencies that need to be added to the importer
35
+ * represented by {@link DepID}.
36
+ *
37
+ * The `modifiedDependencies` property can be used to indicate that there
38
+ * are added dependencies to any of the importer nodes.
39
+ */
40
+ export type AddImportersDependenciesMap = Map<DepID, Map<string, Dependency>> & {
41
+ modifiedDependencies: boolean;
42
+ };
43
+ /**
44
+ * A `Map` object representing nodes to be removed from the ideal graph.
45
+ * Each {@link DepID} key represents an importer node and the `Set` of
46
+ * dependency names to be removed from its dependency list.
47
+ *
48
+ * The `modifiedDependencies` property can be used to indicate that there
49
+ * are added dependencies to any of the importer nodes.
50
+ */
51
+ export type RemoveImportersDependenciesMap = Map<DepID, Set<string>> & {
52
+ modifiedDependencies: boolean;
53
+ };
54
+ export declare const isDependency: (o: unknown) => o is Dependency;
55
+ export declare const asDependency: (obj: unknown) => Dependency;
56
+ /**
57
+ * Get the {@link DependencyTypeShort} from a {@link DependencyTypeLong}.
58
+ */
59
+ export declare const shorten: (typeLong: DependencyTypeLong, name?: string, manifest?: Pick<Manifest, "peerDependenciesMeta"> | null) => DependencyTypeShort;
60
+ export declare const getRawDependencies: (node: NodeLike) => Map<string, RawDependency>;
61
+ /**
62
+ * Retrieves a map of all dependencies, of all types, that can be inferred
63
+ * from a given node manifest, including missing dependencies.
64
+ */
65
+ export declare const getDependencies: (node: NodeLike, options: SpecOptions) => Map<string, Dependency>;