@vltpkg/graph 1.0.4 → 1.0.6

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.
@@ -102,11 +102,17 @@ export declare const asStoreConfigObject: (obj: unknown) => StoreConfigObject;
102
102
  */
103
103
  export declare const getPathBasedId: (spec: Spec, path: Path) => DepID | undefined;
104
104
  /**
105
- * Verify that all importer node_modules directories exist on disk.
105
+ * Verify that importer node_modules directories exist on disk.
106
106
  * The hidden lockfile may report deps as installed even when a
107
107
  * workspace's node_modules has been manually deleted. Throws if
108
- * any importer node_modules directory is missing, causing the
109
- * caller to fall through to filesystem-based graph loading.
108
+ * an importer with at least one resolved edgeOut is missing
109
+ * node_modules, causing the caller to fall through to
110
+ * filesystem-based graph loading.
111
+ *
112
+ * Importers with no resolved edgesOut (dep-less / types-only
113
+ * workspaces) never get a node_modules from reify, so they are
114
+ * skipped — requiring one would discard the hidden lockfile on
115
+ * every install.
110
116
  */
111
117
  export declare const verifyImporterNodeModules: (graph: Graph, projectRoot: string) => void;
112
118
  /**
@@ -252,14 +252,29 @@ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, curr
252
252
  }
253
253
  };
254
254
  /**
255
- * Verify that all importer node_modules directories exist on disk.
255
+ * Verify that importer node_modules directories exist on disk.
256
256
  * The hidden lockfile may report deps as installed even when a
257
257
  * workspace's node_modules has been manually deleted. Throws if
258
- * any importer node_modules directory is missing, causing the
259
- * caller to fall through to filesystem-based graph loading.
258
+ * an importer with at least one resolved edgeOut is missing
259
+ * node_modules, causing the caller to fall through to
260
+ * filesystem-based graph loading.
261
+ *
262
+ * Importers with no resolved edgesOut (dep-less / types-only
263
+ * workspaces) never get a node_modules from reify, so they are
264
+ * skipped — requiring one would discard the hidden lockfile on
265
+ * every install.
260
266
  */
261
267
  export const verifyImporterNodeModules = (graph, projectRoot) => {
262
268
  for (const importer of graph.importers) {
269
+ let linked = false;
270
+ for (const { to } of importer.edgesOut.values()) {
271
+ if (to) {
272
+ linked = true;
273
+ break;
274
+ }
275
+ }
276
+ if (!linked)
277
+ continue;
263
278
  const nm = resolve(projectRoot, importer.location, 'node_modules');
264
279
  if (!existsSync(nm)) {
265
280
  throw new Error(`Missing node_modules for importer: ${importer.location}`);
package/dist/diff.d.ts CHANGED
@@ -42,7 +42,7 @@ export declare class Diff {
42
42
  delete: Set<Edge>;
43
43
  };
44
44
  /**
45
- * True if the diff only contains optional nodes (computed during construction)
45
+ * True if every added node/edge is confined to an optional subtree
46
46
  */
47
47
  optionalOnly: boolean;
48
48
  get [Symbol.toStringTag](): string;
package/dist/diff.js CHANGED
@@ -43,7 +43,7 @@ export class Diff {
43
43
  delete: new Set(),
44
44
  };
45
45
  /**
46
- * True if the diff only contains optional nodes (computed during construction)
46
+ * True if every added node/edge is confined to an optional subtree
47
47
  */
48
48
  optionalOnly = true;
49
49
  get [Symbol.toStringTag]() {
@@ -82,7 +82,9 @@ export class Diff {
82
82
  this.edges.delete.add(fromEdge);
83
83
  if (edge.to) {
84
84
  this.edges.add.add(edge);
85
- if (!edge.optional) {
85
+ // edge.optional is the edge type only; a non-optional edge whose
86
+ // source is optional is still confined to an optional subtree
87
+ if (!edge.optional && !edge.from.optional) {
86
88
  this.optionalOnly = false;
87
89
  }
88
90
  }
@@ -361,7 +361,15 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
361
361
  []
362
362
  : bundleDeps);
363
363
  // setup next level to process all child dependencies in the manifest
364
- const nextDeps = [];
364
+ // Use a Map keyed by name so that later dependency types override
365
+ // earlier ones. This matches npm semantics where entries in
366
+ // `optionalDependencies` override entries of the same name in
367
+ // `dependencies`. Without this, a package listed in both
368
+ // (e.g. esbuild@0.18.20, typescript@7.0.2) would first be placed
369
+ // as a regular (non-optional) dep, and the later optional entry
370
+ // could not recover the `optional` flag due to the `&&=` operator
371
+ // in `placePackage`.
372
+ const nextDepsMap = new Map();
365
373
  // traverse actual dependency declarations in the manifest
366
374
  // creating dependency entries for them
367
375
  for (const depTypeName of longDependencyTypes) {
@@ -386,11 +394,12 @@ const processPlacementTasks = async (graph, options, placementTasks, add, modifi
386
394
  nextPeerDeps.set(name, dep);
387
395
  }
388
396
  else {
389
- nextDeps.push(dep);
397
+ nextDepsMap.set(name, dep);
390
398
  }
391
399
  }
392
400
  }
393
401
  }
402
+ const nextDeps = [...nextDepsMap.values()];
394
403
  // Inject transient dependencies for non-importer nodes (nested folders)
395
404
  // These are deps that were added from a nested folder context using
396
405
  // relative file: specs that should resolve relative to that folder
@@ -66,16 +66,19 @@ export const reify = async (options) => {
66
66
  const diff = new Diff(actual, graph);
67
67
  const noModifiedDependencies = !options.add?.modifiedDependencies &&
68
68
  !options.remove?.modifiedDependencies;
69
+ // Skip optional-only diffs only after a prior reify wrote lockfiles.
70
+ // Otherwise a first install of an optional-only tree would never extract.
71
+ const hasLockfiles = !!scurry.lstatSync('vlt-lock.json') &&
72
+ !!scurry.lstatSync('node_modules/.vlt-lock.json');
69
73
  const skipOptionalOnly = noModifiedDependencies && diff.optionalOnly;
70
- const skippable = skipOptionalOnly && !options.update;
74
+ const skippable = skipOptionalOnly && !options.update && hasLockfiles;
71
75
  const res = { diff };
72
76
  if (!diff.hasChanges() || skippable) {
73
77
  // Even when there are no changes to reify, ensure lockfiles
74
78
  // exist on disk. This handles the case where a project has no
75
79
  // dependencies (or only workspace importers) but still needs
76
80
  // lockfiles written on the first install.
77
- if (!scurry.lstatSync('vlt-lock.json') ||
78
- !scurry.lstatSync('node_modules/.vlt-lock.json')) {
81
+ if (!hasLockfiles) {
79
82
  saveHidden(options);
80
83
  const lfData = lockfileData(options);
81
84
  saveData(lfData, scurry.resolve('vlt-lock.json'), false);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/graph",
3
3
  "description": "A library that helps understanding & expressing what happens on an install",
4
- "version": "1.0.4",
4
+ "version": "1.0.6",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",
@@ -13,26 +13,26 @@
13
13
  "url": "http://vlt.sh"
14
14
  },
15
15
  "dependencies": {
16
- "@vltpkg/cmd-shim": "1.0.4",
17
- "@vltpkg/dep-id": "1.0.4",
18
- "@vltpkg/dss-breadcrumb": "1.0.4",
19
- "@vltpkg/error-cause": "1.0.4",
20
- "@vltpkg/fast-split": "1.0.4",
21
- "@vltpkg/graph-run": "1.0.4",
22
- "@vltpkg/init": "1.0.4",
23
- "@vltpkg/output": "1.0.4",
24
- "@vltpkg/package-info": "1.0.4",
25
- "@vltpkg/package-json": "1.0.4",
26
- "@vltpkg/pick-manifest": "1.0.4",
27
- "@vltpkg/query": "1.0.4",
28
- "@vltpkg/rollback-remove": "1.0.4",
29
- "@vltpkg/run": "1.0.4",
30
- "@vltpkg/satisfies": "1.0.4",
31
- "@vltpkg/security-archive": "1.0.4",
32
- "@vltpkg/spec": "1.0.4",
33
- "@vltpkg/types": "1.0.4",
34
- "@vltpkg/vlt-json": "1.0.4",
35
- "@vltpkg/workspaces": "1.0.4",
16
+ "@vltpkg/cmd-shim": "1.0.6",
17
+ "@vltpkg/dep-id": "1.0.6",
18
+ "@vltpkg/dss-breadcrumb": "1.0.6",
19
+ "@vltpkg/error-cause": "1.0.6",
20
+ "@vltpkg/fast-split": "1.0.6",
21
+ "@vltpkg/graph-run": "1.0.6",
22
+ "@vltpkg/init": "1.0.6",
23
+ "@vltpkg/output": "1.0.6",
24
+ "@vltpkg/package-info": "1.0.6",
25
+ "@vltpkg/package-json": "1.0.6",
26
+ "@vltpkg/pick-manifest": "1.0.6",
27
+ "@vltpkg/query": "1.0.6",
28
+ "@vltpkg/rollback-remove": "1.0.6",
29
+ "@vltpkg/run": "1.0.6",
30
+ "@vltpkg/satisfies": "1.0.6",
31
+ "@vltpkg/security-archive": "1.0.6",
32
+ "@vltpkg/spec": "1.0.6",
33
+ "@vltpkg/types": "1.0.6",
34
+ "@vltpkg/vlt-json": "1.0.6",
35
+ "@vltpkg/workspaces": "1.0.6",
36
36
  "path-scurry": "^2.0.1",
37
37
  "promise-call-limit": "^3.0.2"
38
38
  },