@vltpkg/graph 0.0.0-0.1730239248325

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 (168) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +33 -0
  3. package/dist/esm/actual/load.d.ts +54 -0
  4. package/dist/esm/actual/load.d.ts.map +1 -0
  5. package/dist/esm/actual/load.js +243 -0
  6. package/dist/esm/actual/load.js.map +1 -0
  7. package/dist/esm/browser.d.ts +9 -0
  8. package/dist/esm/browser.d.ts.map +1 -0
  9. package/dist/esm/browser.js +11 -0
  10. package/dist/esm/browser.js.map +1 -0
  11. package/dist/esm/dependencies.d.ts +69 -0
  12. package/dist/esm/dependencies.d.ts.map +1 -0
  13. package/dist/esm/dependencies.js +71 -0
  14. package/dist/esm/dependencies.js.map +1 -0
  15. package/dist/esm/diff.d.ts +49 -0
  16. package/dist/esm/diff.d.ts.map +1 -0
  17. package/dist/esm/diff.js +123 -0
  18. package/dist/esm/diff.js.map +1 -0
  19. package/dist/esm/edge.d.ts +41 -0
  20. package/dist/esm/edge.d.ts.map +1 -0
  21. package/dist/esm/edge.js +66 -0
  22. package/dist/esm/edge.js.map +1 -0
  23. package/dist/esm/graph.d.ts +123 -0
  24. package/dist/esm/graph.d.ts.map +1 -0
  25. package/dist/esm/graph.js +301 -0
  26. package/dist/esm/graph.js.map +1 -0
  27. package/dist/esm/ideal/add-nodes.d.ts +19 -0
  28. package/dist/esm/ideal/add-nodes.d.ts.map +1 -0
  29. package/dist/esm/ideal/add-nodes.js +28 -0
  30. package/dist/esm/ideal/add-nodes.js.map +1 -0
  31. package/dist/esm/ideal/append-nodes.d.ts +9 -0
  32. package/dist/esm/ideal/append-nodes.d.ts.map +1 -0
  33. package/dist/esm/ideal/append-nodes.js +121 -0
  34. package/dist/esm/ideal/append-nodes.js.map +1 -0
  35. package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts +15 -0
  36. package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts.map +1 -0
  37. package/dist/esm/ideal/build-ideal-from-starting-graph.js +28 -0
  38. package/dist/esm/ideal/build-ideal-from-starting-graph.js.map +1 -0
  39. package/dist/esm/ideal/build.d.ts +32 -0
  40. package/dist/esm/ideal/build.d.ts.map +1 -0
  41. package/dist/esm/ideal/build.js +42 -0
  42. package/dist/esm/ideal/build.js.map +1 -0
  43. package/dist/esm/ideal/get-importer-specs.d.ts +13 -0
  44. package/dist/esm/ideal/get-importer-specs.d.ts.map +1 -0
  45. package/dist/esm/ideal/get-importer-specs.js +76 -0
  46. package/dist/esm/ideal/get-importer-specs.js.map +1 -0
  47. package/dist/esm/ideal/remove-nodes.d.ts +7 -0
  48. package/dist/esm/ideal/remove-nodes.d.ts.map +1 -0
  49. package/dist/esm/ideal/remove-nodes.js +19 -0
  50. package/dist/esm/ideal/remove-nodes.js.map +1 -0
  51. package/dist/esm/ideal/remove-satisfied-specs.d.ts +8 -0
  52. package/dist/esm/ideal/remove-satisfied-specs.d.ts.map +1 -0
  53. package/dist/esm/ideal/remove-satisfied-specs.js +35 -0
  54. package/dist/esm/ideal/remove-satisfied-specs.js.map +1 -0
  55. package/dist/esm/ideal/types.d.ts +35 -0
  56. package/dist/esm/ideal/types.d.ts.map +1 -0
  57. package/dist/esm/ideal/types.js +2 -0
  58. package/dist/esm/ideal/types.js.map +1 -0
  59. package/dist/esm/index.d.ts +31 -0
  60. package/dist/esm/index.d.ts.map +1 -0
  61. package/dist/esm/index.js +26 -0
  62. package/dist/esm/index.js.map +1 -0
  63. package/dist/esm/lockfile/load-edges.d.ts +5 -0
  64. package/dist/esm/lockfile/load-edges.d.ts.map +1 -0
  65. package/dist/esm/lockfile/load-edges.js +40 -0
  66. package/dist/esm/lockfile/load-edges.js.map +1 -0
  67. package/dist/esm/lockfile/load-nodes.d.ts +4 -0
  68. package/dist/esm/lockfile/load-nodes.d.ts.map +1 -0
  69. package/dist/esm/lockfile/load-nodes.js +28 -0
  70. package/dist/esm/lockfile/load-nodes.js.map +1 -0
  71. package/dist/esm/lockfile/load.d.ts +33 -0
  72. package/dist/esm/lockfile/load.d.ts.map +1 -0
  73. package/dist/esm/lockfile/load.js +53 -0
  74. package/dist/esm/lockfile/load.js.map +1 -0
  75. package/dist/esm/lockfile/save.d.ts +18 -0
  76. package/dist/esm/lockfile/save.d.ts.map +1 -0
  77. package/dist/esm/lockfile/save.js +128 -0
  78. package/dist/esm/lockfile/save.js.map +1 -0
  79. package/dist/esm/lockfile/types.d.ts +60 -0
  80. package/dist/esm/lockfile/types.d.ts.map +1 -0
  81. package/dist/esm/lockfile/types.js +13 -0
  82. package/dist/esm/lockfile/types.js.map +1 -0
  83. package/dist/esm/node.d.ts +144 -0
  84. package/dist/esm/node.d.ts.map +1 -0
  85. package/dist/esm/node.js +266 -0
  86. package/dist/esm/node.js.map +1 -0
  87. package/dist/esm/non-empty-list.d.ts +3 -0
  88. package/dist/esm/non-empty-list.d.ts.map +1 -0
  89. package/dist/esm/non-empty-list.js +3 -0
  90. package/dist/esm/non-empty-list.js.map +1 -0
  91. package/dist/esm/package.json +3 -0
  92. package/dist/esm/reify/add-edge.d.ts +11 -0
  93. package/dist/esm/reify/add-edge.d.ts.map +1 -0
  94. package/dist/esm/reify/add-edge.js +43 -0
  95. package/dist/esm/reify/add-edge.js.map +1 -0
  96. package/dist/esm/reify/add-edges.d.ts +6 -0
  97. package/dist/esm/reify/add-edges.d.ts.map +1 -0
  98. package/dist/esm/reify/add-edges.js +13 -0
  99. package/dist/esm/reify/add-edges.js.map +1 -0
  100. package/dist/esm/reify/add-nodes.d.ts +7 -0
  101. package/dist/esm/reify/add-nodes.d.ts.map +1 -0
  102. package/dist/esm/reify/add-nodes.js +37 -0
  103. package/dist/esm/reify/add-nodes.js.map +1 -0
  104. package/dist/esm/reify/bin-paths.d.ts +4 -0
  105. package/dist/esm/reify/bin-paths.d.ts.map +1 -0
  106. package/dist/esm/reify/bin-paths.js +23 -0
  107. package/dist/esm/reify/bin-paths.js.map +1 -0
  108. package/dist/esm/reify/build.d.ts +5 -0
  109. package/dist/esm/reify/build.d.ts.map +1 -0
  110. package/dist/esm/reify/build.js +90 -0
  111. package/dist/esm/reify/build.js.map +1 -0
  112. package/dist/esm/reify/delete-edge.d.ts +5 -0
  113. package/dist/esm/reify/delete-edge.d.ts.map +1 -0
  114. package/dist/esm/reify/delete-edge.js +29 -0
  115. package/dist/esm/reify/delete-edge.js.map +1 -0
  116. package/dist/esm/reify/delete-edges.d.ts +5 -0
  117. package/dist/esm/reify/delete-edges.d.ts.map +1 -0
  118. package/dist/esm/reify/delete-edges.js +14 -0
  119. package/dist/esm/reify/delete-edges.js.map +1 -0
  120. package/dist/esm/reify/delete-nodes.d.ts +5 -0
  121. package/dist/esm/reify/delete-nodes.d.ts.map +1 -0
  122. package/dist/esm/reify/delete-nodes.js +12 -0
  123. package/dist/esm/reify/delete-nodes.js.map +1 -0
  124. package/dist/esm/reify/index.d.ts +16 -0
  125. package/dist/esm/reify/index.d.ts.map +1 -0
  126. package/dist/esm/reify/index.js +85 -0
  127. package/dist/esm/reify/index.js.map +1 -0
  128. package/dist/esm/reify/optional-fail.d.ts +16 -0
  129. package/dist/esm/reify/optional-fail.d.ts.map +1 -0
  130. package/dist/esm/reify/optional-fail.js +16 -0
  131. package/dist/esm/reify/optional-fail.js.map +1 -0
  132. package/dist/esm/reify/rollback.d.ts +5 -0
  133. package/dist/esm/reify/rollback.d.ts.map +1 -0
  134. package/dist/esm/reify/rollback.js +24 -0
  135. package/dist/esm/reify/rollback.js.map +1 -0
  136. package/dist/esm/reify/update-importers-package-json.d.ts +36 -0
  137. package/dist/esm/reify/update-importers-package-json.d.ts.map +1 -0
  138. package/dist/esm/reify/update-importers-package-json.js +95 -0
  139. package/dist/esm/reify/update-importers-package-json.js.map +1 -0
  140. package/dist/esm/remove-optional-subgraph.d.ts +34 -0
  141. package/dist/esm/remove-optional-subgraph.d.ts.map +1 -0
  142. package/dist/esm/remove-optional-subgraph.js +48 -0
  143. package/dist/esm/remove-optional-subgraph.js.map +1 -0
  144. package/dist/esm/stringify-node.d.ts +3 -0
  145. package/dist/esm/stringify-node.d.ts.map +1 -0
  146. package/dist/esm/stringify-node.js +24 -0
  147. package/dist/esm/stringify-node.js.map +1 -0
  148. package/dist/esm/types.d.ts +39 -0
  149. package/dist/esm/types.d.ts.map +1 -0
  150. package/dist/esm/types.js +2 -0
  151. package/dist/esm/types.js.map +1 -0
  152. package/dist/esm/visualization/human-readable-output.d.ts +25 -0
  153. package/dist/esm/visualization/human-readable-output.d.ts.map +1 -0
  154. package/dist/esm/visualization/human-readable-output.js +132 -0
  155. package/dist/esm/visualization/human-readable-output.js.map +1 -0
  156. package/dist/esm/visualization/json-output.d.ts +37 -0
  157. package/dist/esm/visualization/json-output.d.ts.map +1 -0
  158. package/dist/esm/visualization/json-output.js +28 -0
  159. package/dist/esm/visualization/json-output.js.map +1 -0
  160. package/dist/esm/visualization/mermaid-output.d.ts +11 -0
  161. package/dist/esm/visualization/mermaid-output.d.ts.map +1 -0
  162. package/dist/esm/visualization/mermaid-output.js +68 -0
  163. package/dist/esm/visualization/mermaid-output.js.map +1 -0
  164. package/dist/esm/visualization/object-like-output.d.ts +3 -0
  165. package/dist/esm/visualization/object-like-output.d.ts.map +1 -0
  166. package/dist/esm/visualization/object-like-output.js +49 -0
  167. package/dist/esm/visualization/object-like-output.js.map +1 -0
  168. package/package.json +86 -0
@@ -0,0 +1,301 @@
1
+ import { getId, joinDepIDTuple } from '@vltpkg/dep-id';
2
+ import { error } from '@vltpkg/error-cause';
3
+ import { satisfies } from '@vltpkg/satisfies';
4
+ import { Spec } from '@vltpkg/spec';
5
+ import { inspect } from 'util';
6
+ import { lockfileData } from './lockfile/save.js';
7
+ import { Node } from './node.js';
8
+ const kCustomInspect = Symbol.for('nodejs.util.inspect.custom');
9
+ // this is always the same, but we don't hard code it as a string,
10
+ // in case the DepID module needs to change its delimiter again ever.
11
+ const mainDepID = joinDepIDTuple(['file', '.']);
12
+ export class Graph {
13
+ get [Symbol.toStringTag]() {
14
+ return '@vltpkg/graph.Graph';
15
+ }
16
+ #options;
17
+ #nodeOptions;
18
+ /**
19
+ * A {@link Monorepo} instance, used for managing workspaces.
20
+ */
21
+ monorepo;
22
+ /**
23
+ * An inventory with all manifests related to an install.
24
+ */
25
+ manifests;
26
+ /**
27
+ * A set of all edges in this graph.
28
+ */
29
+ edges = new Set();
30
+ /**
31
+ * Map registered dep ids to the node that represent them in the graph.
32
+ */
33
+ nodes = new Map();
34
+ /**
35
+ * Map of nodes by their name
36
+ */
37
+ nodesByName = new Map();
38
+ /**
39
+ * Cached resolutions for spec lookups
40
+ */
41
+ resolutions = new Map();
42
+ /**
43
+ * Reverse map of resolutions
44
+ */
45
+ resolutionsReverse = new Map();
46
+ /**
47
+ * A set of importer nodes in this graph.
48
+ */
49
+ importers = new Set();
50
+ /**
51
+ * The {@link Node} that represents the project root `package.json`.
52
+ */
53
+ mainImporter;
54
+ /**
55
+ * A set of extraneous dependencies found when building the graph.
56
+ */
57
+ extraneousDependencies = new Set();
58
+ /**
59
+ * The root of the project this graph represents
60
+ */
61
+ projectRoot;
62
+ constructor(options) {
63
+ const { mainManifest, manifests, monorepo, projectRoot } = options;
64
+ this.#options = options;
65
+ this.manifests = manifests ?? new Map();
66
+ this.projectRoot = projectRoot;
67
+ this.#nodeOptions = {
68
+ ...this.#options,
69
+ graph: this,
70
+ };
71
+ // add the project root node
72
+ const mainImporterLocation = '.';
73
+ const mainImporterSpec = Spec.parse(mainManifest.name || '(root)', mainImporterLocation);
74
+ const mainImporter = this.addNode(mainDepID, mainManifest, mainImporterSpec);
75
+ mainImporter.setImporterLocation(mainImporterLocation);
76
+ mainImporter.mainImporter = true;
77
+ this.mainImporter = mainImporter;
78
+ this.importers.add(mainImporter);
79
+ this.manifests.set(mainImporter.id, mainManifest);
80
+ // uses the monorepo instance in order to retrieve info on
81
+ // workspaces and create importer nodes for each of them
82
+ this.monorepo = monorepo;
83
+ if (this.monorepo) {
84
+ for (const ws of this.monorepo) {
85
+ const wsNode = this.addNode(ws.id, ws.manifest, undefined, ws.name);
86
+ wsNode.setImporterLocation(`./${ws.path}`);
87
+ if (wsNode.manifest) {
88
+ this.manifests.set(wsNode.id, wsNode.manifest);
89
+ }
90
+ this.importers.add(wsNode);
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * Delete all nodes that are unreachable from the importers.
96
+ * The collection of deleted nodes is returned.
97
+ *
98
+ * NOTE: This can be extremely slow for large graphs, and is almost always
99
+ * unnecessary! Only call when it is known that some unreachable nodes may
100
+ * have been created, for example when deleting the unneeded subgraph when an
101
+ * optional node fails to resolve/install.
102
+ */
103
+ gc() {
104
+ const { nodes } = this;
105
+ this.nodes = new Map();
106
+ const marked = new Set(this.importers);
107
+ for (const imp of marked) {
108
+ // don't delete the importer!
109
+ nodes.delete(imp.id);
110
+ this.nodes.set(imp.id, imp);
111
+ for (const { to } of imp.edgesOut.values()) {
112
+ if (!to || marked.has(to))
113
+ continue;
114
+ marked.add(to);
115
+ nodes.delete(to.id);
116
+ this.nodes.set(to.id, to);
117
+ }
118
+ }
119
+ for (const node of nodes.values()) {
120
+ this.removeNode(node);
121
+ }
122
+ return nodes;
123
+ }
124
+ /**
125
+ * Create a new edge between two nodes of the graph in case both exist,
126
+ * in case the destination node does not exists, then a dangling edge,
127
+ * pointing to nothing will be created to represent that missing dependency.
128
+ */
129
+ addEdge(type, spec, from, to) {
130
+ // fix any nameless spec
131
+ if (spec.name === '(unknown)') {
132
+ if (to) {
133
+ spec.name = to.name /* c8 ignore next */ || '(unknown)';
134
+ spec.spec = `${to.name}@${spec.bareSpec}`;
135
+ }
136
+ else {
137
+ throw error('Impossible to place a missing, nameless dependency', { spec });
138
+ }
139
+ }
140
+ const existing = from.edgesOut.get(spec.name);
141
+ if (existing) {
142
+ const edge = existing;
143
+ if (edge.type === type &&
144
+ edge.spec.bareSpec === spec.bareSpec) {
145
+ if (to && to !== edge.to) {
146
+ edge.to = to;
147
+ edge.to.edgesIn.add(edge);
148
+ }
149
+ return edge;
150
+ }
151
+ this.edges.delete(edge);
152
+ }
153
+ const f = from;
154
+ const edgeOut = f.addEdgesTo(type, spec, to);
155
+ this.edges.add(edgeOut);
156
+ return edgeOut;
157
+ }
158
+ /**
159
+ * Find an existing node to satisfy a dependency
160
+ */
161
+ findResolution(spec, fromNode) {
162
+ const f = spec.final;
163
+ // if it's a file: dep, then the fromNode location matters
164
+ const fromPref = f.type === 'file' ? fromNode.location + ' : ' : '';
165
+ const sf = fromPref + String(f);
166
+ const cached = this.resolutions.get(sf);
167
+ if (cached)
168
+ return cached;
169
+ const nbn = this.nodesByName.get(f.name);
170
+ if (!nbn)
171
+ return undefined;
172
+ for (const node of nbn) {
173
+ if (satisfies(node.id, f, fromNode.location, this.projectRoot, this.monorepo)) {
174
+ this.resolutions.set(sf, node);
175
+ // always set by now, because the node was added at some point
176
+ this.resolutionsReverse.get(node)?.add(sf);
177
+ return node;
178
+ }
179
+ }
180
+ }
181
+ /**
182
+ * Create a new node in the graph.
183
+ */
184
+ addNode(id, manifest, spec, name, version) {
185
+ const node = new Node(this.#nodeOptions, id, manifest, spec, name, version);
186
+ this.nodes.set(node.id, node);
187
+ const nbn = this.nodesByName.get(node.name) ?? new Set();
188
+ nbn.add(node);
189
+ this.nodesByName.set(node.name, nbn);
190
+ if (spec) {
191
+ const f = String(spec.final);
192
+ // if it's a file: type, then that is fromNode-specific,
193
+ // so we can't shortcut add it here.
194
+ if (spec.final.type !== 'file') {
195
+ this.resolutions.set(f, node);
196
+ const rrev = this.resolutionsReverse.get(node) ?? new Set();
197
+ rrev.add(f);
198
+ this.resolutionsReverse.set(node, rrev);
199
+ }
200
+ }
201
+ if (manifest) {
202
+ this.manifests.set(node.id, manifest);
203
+ }
204
+ return node;
205
+ }
206
+ /**
207
+ * Place a new package into the graph representation, creating the new
208
+ * edges and possibly new nodes that are to be expected when traversing
209
+ * the graph in a top-down direction, e.g: from importers to leafs.
210
+ *
211
+ * For different uses that are not a direct top-down traversal of the graph
212
+ * consider using `addNode()` and `addEdge()` instead.
213
+ */
214
+ placePackage(fromNode, depType, spec, manifest, id) {
215
+ // if no manifest is available, then create an edge that has no
216
+ // reference to any other node, representing a missing dependency
217
+ if (!manifest && !id) {
218
+ this.addEdge(depType, spec, fromNode);
219
+ return;
220
+ }
221
+ // flags set on the node we're about to create or find.
222
+ const flags = {
223
+ dev: fromNode.dev || depType === 'dev',
224
+ optional: fromNode.optional ||
225
+ depType === 'optional' ||
226
+ depType === 'peerOptional',
227
+ };
228
+ const depId = id || (manifest && getId(spec, manifest));
229
+ /* c8 ignore start - should not be possible */
230
+ if (!depId) {
231
+ throw error('Could not find dep id when placing package', {
232
+ spec,
233
+ manifest,
234
+ });
235
+ }
236
+ /* c8 ignore stop */
237
+ // if a node for this package is already represented by a node
238
+ // in the graph, then just creates a new edge to that node
239
+ const toFoundNode = this.nodes.get(depId);
240
+ if (toFoundNode) {
241
+ this.addEdge(depType, spec, fromNode, toFoundNode);
242
+ // the current only stays dev/optional if this dep lets it remain so
243
+ // if it's not already, we don't make it dev or optional.
244
+ toFoundNode.dev &&= flags.dev;
245
+ toFoundNode.optional &&= flags.optional;
246
+ return toFoundNode;
247
+ }
248
+ // creates a new node and edges to its parent
249
+ const toNode = this.addNode(depId, manifest);
250
+ toNode.registry = spec.registry;
251
+ toNode.dev = flags.dev;
252
+ toNode.optional = flags.optional;
253
+ this.addEdge(depType, spec, fromNode, toNode);
254
+ return toNode;
255
+ }
256
+ /**
257
+ * Removes a node and its relevant edges from the graph.
258
+ *
259
+ * If a replacement is provided, then any edges that were previously
260
+ * pointing to the removed node will be directed to the replacement,
261
+ * if it is valid to do so.
262
+ */
263
+ removeNode(node, replacement) {
264
+ this.nodes.delete(node.id);
265
+ const nbn = this.nodesByName.get(node.name);
266
+ // if it's the last one, just remove the set
267
+ if (nbn?.size === 1)
268
+ this.nodesByName.delete(node.name);
269
+ else
270
+ nbn?.delete(node);
271
+ for (const r of this.resolutionsReverse.get(node) ?? new Set()) {
272
+ this.resolutions.delete(r);
273
+ }
274
+ this.resolutionsReverse.delete(node);
275
+ this.manifests.delete(node.id);
276
+ for (const edge of node.edgesOut.values()) {
277
+ this.edges.delete(edge);
278
+ }
279
+ for (const edge of node.edgesIn) {
280
+ if (replacement &&
281
+ satisfies(replacement.id, edge.spec, edge.from.location, this.projectRoot, this.monorepo)) {
282
+ edge.to = replacement;
283
+ }
284
+ else {
285
+ edge.to = undefined;
286
+ }
287
+ }
288
+ }
289
+ toJSON() {
290
+ return lockfileData({
291
+ ...this.#options,
292
+ graph: this,
293
+ saveManifests: true,
294
+ });
295
+ }
296
+ [kCustomInspect](_, options) {
297
+ const data = this.toJSON();
298
+ return `${this[Symbol.toStringTag]} ${inspect(data, options)}`;
299
+ }
300
+ }
301
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { getId, joinDepIDTuple, type DepID } from '@vltpkg/dep-id'\nimport { error } from '@vltpkg/error-cause'\nimport { satisfies } from '@vltpkg/satisfies'\nimport { Spec, type SpecOptions } from '@vltpkg/spec'\nimport { type Manifest } from '@vltpkg/types'\nimport { Monorepo } from '@vltpkg/workspaces'\nimport { inspect, InspectOptions } from 'util'\nimport { DependencyTypeShort } from './dependencies.js'\nimport { type Edge } from './edge.js'\nimport { lockfileData } from './lockfile/save.js'\nimport { Node, NodeOptions } from './node.js'\nimport { GraphLike, NodeLike } from './types.js'\n\nconst kCustomInspect = Symbol.for('nodejs.util.inspect.custom')\n\n// this is always the same, but we don't hard code it as a string,\n// in case the DepID module needs to change its delimiter again ever.\nconst mainDepID = joinDepIDTuple(['file', '.'])\n\nexport type ManifestInventory = Map<DepID, Manifest>\n\nexport type GraphOptions = SpecOptions & {\n /**\n * The main importer manifest info.\n */\n mainManifest: Manifest\n /**\n * An inventory of seen manifests.\n */\n manifests?: ManifestInventory\n /**\n * A {@link Monorepo} object, for managing workspaces\n */\n monorepo?: Monorepo\n /**\n * Root of the project this graph represents\n */\n projectRoot: string\n}\n\nexport class Graph implements GraphLike {\n get [Symbol.toStringTag]() {\n return '@vltpkg/graph.Graph'\n }\n\n #options: GraphOptions\n\n #nodeOptions: NodeOptions\n\n /**\n * A {@link Monorepo} instance, used for managing workspaces.\n */\n monorepo?: Monorepo\n\n /**\n * An inventory with all manifests related to an install.\n */\n manifests: ManifestInventory\n\n /**\n * A set of all edges in this graph.\n */\n edges = new Set<Edge>()\n\n /**\n * Map registered dep ids to the node that represent them in the graph.\n */\n nodes = new Map<DepID, Node>()\n\n /**\n * Map of nodes by their name\n */\n nodesByName = new Map<string, Set<Node>>()\n\n /**\n * Cached resolutions for spec lookups\n */\n resolutions = new Map<string, Node>()\n\n /**\n * Reverse map of resolutions\n */\n resolutionsReverse = new Map<Node, Set<string>>()\n\n /**\n * A set of importer nodes in this graph.\n */\n importers = new Set<Node>()\n\n /**\n * The {@link Node} that represents the project root `package.json`.\n */\n mainImporter: Node\n\n /**\n * A set of extraneous dependencies found when building the graph.\n */\n extraneousDependencies = new Set<Edge>()\n\n /**\n * The root of the project this graph represents\n */\n projectRoot: string\n\n constructor(options: GraphOptions) {\n const { mainManifest, manifests, monorepo, projectRoot } = options\n this.#options = options\n this.manifests = manifests ?? new Map()\n this.projectRoot = projectRoot\n this.#nodeOptions = {\n ...this.#options,\n graph: this,\n }\n\n // add the project root node\n const mainImporterLocation = '.'\n const mainImporterSpec = Spec.parse(\n mainManifest.name || '(root)',\n mainImporterLocation,\n )\n const mainImporter = this.addNode(\n mainDepID,\n mainManifest,\n mainImporterSpec,\n )\n mainImporter.setImporterLocation(mainImporterLocation)\n mainImporter.mainImporter = true\n this.mainImporter = mainImporter\n this.importers.add(mainImporter)\n this.manifests.set(mainImporter.id, mainManifest)\n\n // uses the monorepo instance in order to retrieve info on\n // workspaces and create importer nodes for each of them\n this.monorepo = monorepo\n if (this.monorepo) {\n for (const ws of this.monorepo) {\n const wsNode = this.addNode(\n ws.id,\n ws.manifest,\n undefined,\n ws.name,\n )\n wsNode.setImporterLocation(`./${ws.path}`)\n if (wsNode.manifest) {\n this.manifests.set(wsNode.id, wsNode.manifest)\n }\n this.importers.add(wsNode)\n }\n }\n }\n\n /**\n * Delete all nodes that are unreachable from the importers.\n * The collection of deleted nodes is returned.\n *\n * NOTE: This can be extremely slow for large graphs, and is almost always\n * unnecessary! Only call when it is known that some unreachable nodes may\n * have been created, for example when deleting the unneeded subgraph when an\n * optional node fails to resolve/install.\n */\n gc() {\n const { nodes } = this\n this.nodes = new Map()\n const marked = new Set(this.importers)\n for (const imp of marked) {\n // don't delete the importer!\n nodes.delete(imp.id)\n this.nodes.set(imp.id, imp)\n for (const { to } of imp.edgesOut.values()) {\n if (!to || marked.has(to)) continue\n marked.add(to)\n nodes.delete(to.id)\n this.nodes.set(to.id, to)\n }\n }\n for (const node of nodes.values()) {\n this.removeNode(node)\n }\n return nodes\n }\n\n /**\n * Create a new edge between two nodes of the graph in case both exist,\n * in case the destination node does not exists, then a dangling edge,\n * pointing to nothing will be created to represent that missing dependency.\n */\n addEdge(\n type: DependencyTypeShort,\n spec: Spec,\n from: NodeLike,\n to?: NodeLike,\n ) {\n // fix any nameless spec\n if (spec.name === '(unknown)') {\n if (to) {\n spec.name = to.name /* c8 ignore next */ || '(unknown)'\n spec.spec = `${to.name}@${spec.bareSpec}`\n } else {\n throw error(\n 'Impossible to place a missing, nameless dependency',\n { spec },\n )\n }\n }\n const existing = from.edgesOut.get(spec.name)\n if (existing) {\n const edge = existing as Edge\n if (\n edge.type === type &&\n edge.spec.bareSpec === spec.bareSpec\n ) {\n if (to && to !== edge.to) {\n edge.to = to as Node\n edge.to.edgesIn.add(edge)\n }\n return edge\n }\n this.edges.delete(edge)\n }\n const f = from as Node\n const edgeOut = f.addEdgesTo(type, spec, to as Node | undefined)\n this.edges.add(edgeOut)\n return edgeOut\n }\n\n /**\n * Find an existing node to satisfy a dependency\n */\n findResolution(spec: Spec, fromNode: Node) {\n const f = spec.final\n // if it's a file: dep, then the fromNode location matters\n const fromPref =\n f.type === 'file' ? fromNode.location + ' : ' : ''\n const sf = fromPref + String(f)\n const cached = this.resolutions.get(sf)\n if (cached) return cached\n const nbn = this.nodesByName.get(f.name)\n if (!nbn) return undefined\n for (const node of nbn) {\n if (\n satisfies(\n node.id,\n f,\n fromNode.location,\n this.projectRoot,\n this.monorepo,\n )\n ) {\n this.resolutions.set(sf, node)\n // always set by now, because the node was added at some point\n this.resolutionsReverse.get(node)?.add(sf)\n return node\n }\n }\n }\n\n /**\n * Create a new node in the graph.\n */\n addNode(\n id?: DepID,\n manifest?: Manifest,\n spec?: Spec,\n name?: string,\n version?: string,\n ) {\n const node = new Node(\n this.#nodeOptions,\n id,\n manifest,\n spec,\n name,\n version,\n )\n this.nodes.set(node.id, node)\n const nbn = this.nodesByName.get(node.name) ?? new Set()\n nbn.add(node)\n this.nodesByName.set(node.name, nbn)\n if (spec) {\n const f = String(spec.final)\n // if it's a file: type, then that is fromNode-specific,\n // so we can't shortcut add it here.\n if (spec.final.type !== 'file') {\n this.resolutions.set(f, node)\n const rrev = this.resolutionsReverse.get(node) ?? new Set()\n rrev.add(f)\n this.resolutionsReverse.set(node, rrev)\n }\n }\n if (manifest) {\n this.manifests.set(node.id, manifest)\n }\n return node\n }\n\n /**\n * Place a new package into the graph representation, creating the new\n * edges and possibly new nodes that are to be expected when traversing\n * the graph in a top-down direction, e.g: from importers to leafs.\n *\n * For different uses that are not a direct top-down traversal of the graph\n * consider using `addNode()` and `addEdge()` instead.\n */\n placePackage(\n fromNode: Node,\n depType: DependencyTypeShort,\n spec: Spec,\n manifest?: Manifest,\n id?: DepID,\n ) {\n // if no manifest is available, then create an edge that has no\n // reference to any other node, representing a missing dependency\n if (!manifest && !id) {\n this.addEdge(depType, spec, fromNode)\n return\n }\n\n // flags set on the node we're about to create or find.\n const flags = {\n dev: fromNode.dev || depType === 'dev',\n optional:\n fromNode.optional ||\n depType === 'optional' ||\n depType === 'peerOptional',\n }\n\n const depId = id || (manifest && getId(spec, manifest))\n\n /* c8 ignore start - should not be possible */\n if (!depId) {\n throw error('Could not find dep id when placing package', {\n spec,\n manifest,\n })\n }\n /* c8 ignore stop */\n\n // if a node for this package is already represented by a node\n // in the graph, then just creates a new edge to that node\n const toFoundNode = this.nodes.get(depId)\n if (toFoundNode) {\n this.addEdge(depType, spec, fromNode, toFoundNode)\n // the current only stays dev/optional if this dep lets it remain so\n // if it's not already, we don't make it dev or optional.\n toFoundNode.dev &&= flags.dev\n toFoundNode.optional &&= flags.optional\n return toFoundNode\n }\n\n // creates a new node and edges to its parent\n const toNode = this.addNode(depId, manifest)\n toNode.registry = spec.registry\n toNode.dev = flags.dev\n toNode.optional = flags.optional\n this.addEdge(depType, spec, fromNode, toNode)\n return toNode\n }\n\n /**\n * Removes a node and its relevant edges from the graph.\n *\n * If a replacement is provided, then any edges that were previously\n * pointing to the removed node will be directed to the replacement,\n * if it is valid to do so.\n */\n removeNode(node: Node, replacement?: Node) {\n this.nodes.delete(node.id)\n const nbn = this.nodesByName.get(node.name)\n // if it's the last one, just remove the set\n if (nbn?.size === 1) this.nodesByName.delete(node.name)\n else nbn?.delete(node)\n for (const r of this.resolutionsReverse.get(node) ?? new Set()) {\n this.resolutions.delete(r)\n }\n this.resolutionsReverse.delete(node)\n this.manifests.delete(node.id)\n for (const edge of node.edgesOut.values()) {\n this.edges.delete(edge)\n }\n for (const edge of node.edgesIn) {\n if (\n replacement &&\n satisfies(\n replacement.id,\n edge.spec,\n edge.from.location,\n this.projectRoot,\n this.monorepo,\n )\n ) {\n edge.to = replacement\n } else {\n edge.to = undefined\n }\n }\n }\n\n toJSON() {\n return lockfileData({\n ...this.#options,\n graph: this,\n saveManifests: true,\n })\n }\n\n [kCustomInspect](_: number, options: InspectOptions) {\n const data = this.toJSON()\n return `${this[Symbol.toStringTag]} ${inspect(data, options)}`\n }\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import { PackageInfoClient } from '@vltpkg/package-info';
2
+ import { SpecOptions } from '@vltpkg/spec';
3
+ import { PathScurry } from 'path-scurry';
4
+ import { BuildIdealAddOptions, BuildIdealFromGraphOptions } from './types.js';
5
+ export type AddNodesOptions = BuildIdealAddOptions & BuildIdealFromGraphOptions & SpecOptions & {
6
+ /**
7
+ * A {@link PathScurry} instance based on the `projectRoot` path
8
+ */
9
+ scurry: PathScurry;
10
+ /**
11
+ * A {@link PackageInfoClient} instance to read manifest info from.
12
+ */
13
+ packageInfo: PackageInfoClient;
14
+ };
15
+ /**
16
+ * Add new nodes in the given `graph` for dependencies specified at `add`.
17
+ */
18
+ export declare const addNodes: ({ add, graph, packageInfo, scurry, ...specOptions }: AddNodesOptions) => Promise<void>;
19
+ //# sourceMappingURL=add-nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-nodes.d.ts","sourceRoot":"","sources":["../../../src/ideal/add-nodes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC3B,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAChD,0BAA0B,GAC1B,WAAW,GAAG;IACZ;;OAEG;IACH,MAAM,EAAE,UAAU,CAAA;IAElB;;OAEG;IACH,WAAW,EAAE,iBAAiB,CAAA;CAC/B,CAAA;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,wDAMlB,eAAe,kBA8BjB,CAAA"}
@@ -0,0 +1,28 @@
1
+ import { error } from '@vltpkg/error-cause';
2
+ import { appendNodes } from './append-nodes.js';
3
+ /**
4
+ * Add new nodes in the given `graph` for dependencies specified at `add`.
5
+ */
6
+ export const addNodes = async ({ add, graph, packageInfo, scurry, ...specOptions }) => {
7
+ const seen = new Set();
8
+ // iterates on the list of dependencies per importer updating
9
+ // the graph using metadata fetch from the registry manifest files
10
+ for (const [depID, dependencies] of add) {
11
+ const importer = graph.nodes.get(depID);
12
+ if (!importer) {
13
+ throw error('Could not find importer', { found: depID });
14
+ }
15
+ // Removes any edges and nodes that are currently part of the
16
+ // graph but are also in the list of dependencies to be installed
17
+ const deps = [...dependencies.values()];
18
+ for (const { spec } of deps) {
19
+ const node = importer.edgesOut.get(spec.name)?.to;
20
+ if (node)
21
+ graph.removeNode(node);
22
+ }
23
+ // Add new nodes for packages defined in the dependencies list fetching
24
+ // metadata from the registry manifests and updating the graph
25
+ await appendNodes(packageInfo, graph, importer, deps, scurry, specOptions, seen);
26
+ }
27
+ };
28
+ //# sourceMappingURL=add-nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-nodes.js","sourceRoot":"","sources":["../../../src/ideal/add-nodes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAI3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAoB/C;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAAE,EAC7B,GAAG,EACH,KAAK,EACL,WAAW,EACX,MAAM,EACN,GAAG,WAAW,EACE,EAAE,EAAE;IACpB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAS,CAAA;IAC7B,6DAA6D;IAC7D,kEAAkE;IAClE,KAAK,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,GAAG,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,KAAK,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1D,CAAC;QAED,6DAA6D;QAC7D,iEAAiE;QACjE,MAAM,IAAI,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,CAAA;QACvC,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAA;YACjD,IAAI,IAAI;gBAAE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAClC,CAAC;QAED,uEAAuE;QACvE,8DAA8D;QAC9D,MAAM,WAAW,CACf,WAAW,EACX,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,WAAW,EACX,IAAI,CACL,CAAA;IACH,CAAC;AACH,CAAC,CAAA","sourcesContent":["import { DepID } from '@vltpkg/dep-id'\nimport { error } from '@vltpkg/error-cause'\nimport { PackageInfoClient } from '@vltpkg/package-info'\nimport { SpecOptions } from '@vltpkg/spec'\nimport { PathScurry } from 'path-scurry'\nimport { appendNodes } from './append-nodes.js'\nimport {\n BuildIdealAddOptions,\n BuildIdealFromGraphOptions,\n} from './types.js'\n\nexport type AddNodesOptions = BuildIdealAddOptions &\n BuildIdealFromGraphOptions &\n SpecOptions & {\n /**\n * A {@link PathScurry} instance based on the `projectRoot` path\n */\n scurry: PathScurry\n\n /**\n * A {@link PackageInfoClient} instance to read manifest info from.\n */\n packageInfo: PackageInfoClient\n }\n\n/**\n * Add new nodes in the given `graph` for dependencies specified at `add`.\n */\nexport const addNodes = async ({\n add,\n graph,\n packageInfo,\n scurry,\n ...specOptions\n}: AddNodesOptions) => {\n const seen = new Set<DepID>()\n // iterates on the list of dependencies per importer updating\n // the graph using metadata fetch from the registry manifest files\n for (const [depID, dependencies] of add) {\n const importer = graph.nodes.get(depID)\n if (!importer) {\n throw error('Could not find importer', { found: depID })\n }\n\n // Removes any edges and nodes that are currently part of the\n // graph but are also in the list of dependencies to be installed\n const deps = [...dependencies.values()]\n for (const { spec } of deps) {\n const node = importer.edgesOut.get(spec.name)?.to\n if (node) graph.removeNode(node)\n }\n\n // Add new nodes for packages defined in the dependencies list fetching\n // metadata from the registry manifests and updating the graph\n await appendNodes(\n packageInfo,\n graph,\n importer,\n deps,\n scurry,\n specOptions,\n seen,\n )\n }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { DepID } from '@vltpkg/dep-id';
2
+ import { PackageInfoClient } from '@vltpkg/package-info';
3
+ import { type SpecOptions } from '@vltpkg/spec';
4
+ import { PathScurry } from 'path-scurry';
5
+ import { Dependency } from '../dependencies.js';
6
+ import { Graph } from '../graph.js';
7
+ import { Node } from '../node.js';
8
+ export declare const appendNodes: (packageInfo: PackageInfoClient, graph: Graph, fromNode: Node, deps: Dependency[], scurry: PathScurry, options: SpecOptions, seen: Set<DepID>) => Promise<void>;
9
+ //# sourceMappingURL=append-nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-nodes.d.ts","sourceRoot":"","sources":["../../../src/ideal/append-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EACL,UAAU,EAIX,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAuDjC,eAAO,MAAM,WAAW,gBACT,iBAAiB,SACvB,KAAK,YACF,IAAI,QACR,UAAU,EAAE,UACV,UAAU,WACT,WAAW,QACd,GAAG,CAAC,KAAK,CAAC,kBA+GjB,CAAA"}
@@ -0,0 +1,121 @@
1
+ import { joinDepIDTuple } from '@vltpkg/dep-id';
2
+ import { error } from '@vltpkg/error-cause';
3
+ import { Spec } from '@vltpkg/spec';
4
+ import { longDependencyTypes, shorten, } from '../dependencies.js';
5
+ import { removeOptionalSubgraph } from '../remove-optional-subgraph.js';
6
+ /**
7
+ * Only install devDeps for git dependencies and importers
8
+ * Everything else always gets installed
9
+ */
10
+ const shouldInstallDepType = (node, depType) => depType !== 'devDependencies' ||
11
+ node.importer ||
12
+ node.id.startsWith('git');
13
+ /**
14
+ * Retrieve the {@link DepID} and location for a `file:` type {@link Node}.
15
+ */
16
+ const getFileTypeInfo = (spec, fromNode, scurry) => {
17
+ const f = spec.final;
18
+ if (f.type !== 'file')
19
+ return;
20
+ /* c8 ignore start - should be impossible */
21
+ if (!f.file) {
22
+ throw error('no path on file specifier', { spec });
23
+ }
24
+ /* c8 ignore stop */
25
+ // Given that both linked folders and local tarballs (both defined with
26
+ // usage of the `file:` spec prefix) location needs to be relative to their
27
+ // parents, build the expected path and use it for both location and id
28
+ const target = scurry.cwd.resolve(fromNode.location).resolve(f.file);
29
+ const path = target.relativePosix();
30
+ const id = joinDepIDTuple(['file', path]);
31
+ return {
32
+ path,
33
+ id,
34
+ isDirectory: !!target.lstatSync()?.isDirectory(),
35
+ };
36
+ };
37
+ const isStringArray = (a) => Array.isArray(a) && !a.some(b => typeof b !== 'string');
38
+ export const appendNodes = async (packageInfo, graph, fromNode, deps, scurry, options, seen) => {
39
+ /* c8 ignore next */
40
+ if (seen.has(fromNode.id))
41
+ return;
42
+ seen.add(fromNode.id);
43
+ await Promise.all(deps.map(async ({ spec, type }) => {
44
+ // see if there's a satisfying node in the graph currently
45
+ const fileTypeInfo = getFileTypeInfo(spec, fromNode, scurry);
46
+ const existingNode = graph.findResolution(spec, fromNode);
47
+ if (existingNode) {
48
+ graph.addEdge(type, spec, fromNode, existingNode);
49
+ return;
50
+ }
51
+ const edgeOptional = type === 'optional' || type === 'peerOptional';
52
+ const mani = await packageInfo
53
+ .manifest(spec, { from: scurry.resolve(fromNode.location) })
54
+ .catch((er) => {
55
+ // optional deps ignored if inaccessible
56
+ if (edgeOptional || fromNode.optional) {
57
+ return undefined;
58
+ }
59
+ throw er;
60
+ });
61
+ if (!mani) {
62
+ if (!edgeOptional && fromNode.isOptional()) {
63
+ // failed resolution of a non-optional dep of an optional node
64
+ // have to clean up the dependents
65
+ removeOptionalSubgraph(graph, fromNode);
66
+ return;
67
+ }
68
+ else if (edgeOptional) {
69
+ // failed resolution of an optional dep, just ignore it,
70
+ // nothing to prune because we never added it in the first place.
71
+ return;
72
+ }
73
+ else {
74
+ throw error('failed to resolve dependency', {
75
+ spec,
76
+ from: fromNode.location,
77
+ });
78
+ }
79
+ }
80
+ const node = graph.placePackage(fromNode, type, spec, mani, fileTypeInfo?.id);
81
+ /* c8 ignore start - not possible, already ensured manifest */
82
+ if (!node) {
83
+ throw error('failed to place package', {
84
+ from: fromNode.location,
85
+ spec,
86
+ });
87
+ }
88
+ /* c8 ignore stop */
89
+ if (fileTypeInfo?.path && fileTypeInfo.isDirectory) {
90
+ node.location = fileTypeInfo.path;
91
+ }
92
+ node.setResolved();
93
+ const nestedAppends = [];
94
+ const bundleDeps = node.manifest?.bundleDependencies;
95
+ const bundled = new Set((node.id.startsWith('git') ||
96
+ node.importer ||
97
+ !isStringArray(bundleDeps)) ?
98
+ []
99
+ : bundleDeps);
100
+ for (const depTypeName of longDependencyTypes) {
101
+ const depRecord = mani[depTypeName];
102
+ if (depRecord && shouldInstallDepType(node, depTypeName)) {
103
+ const nextDeps = Object.entries(depRecord)
104
+ .filter(([name]) => !bundled.has(name))
105
+ .map(([name, bareSpec]) => ({
106
+ // if foo:a@1 depends on b@2, then it must be foo:b@2
107
+ spec: Spec.parse(name, bareSpec, {
108
+ ...options,
109
+ registry: spec.registry,
110
+ }),
111
+ type: shorten(depTypeName, name, mani),
112
+ }));
113
+ if (nextDeps.length) {
114
+ nestedAppends.push(appendNodes(packageInfo, graph, node, nextDeps, scurry, options, seen));
115
+ }
116
+ }
117
+ }
118
+ await Promise.all(nestedAppends);
119
+ }));
120
+ };
121
+ //# sourceMappingURL=append-nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-nodes.js","sourceRoot":"","sources":["../../../src/ideal/append-nodes.ts"],"names":[],"mappings":"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","sourcesContent":["import { DepID, joinDepIDTuple } from '@vltpkg/dep-id'\nimport { error } from '@vltpkg/error-cause'\nimport { PackageInfoClient } from '@vltpkg/package-info'\nimport { Spec, type SpecOptions } from '@vltpkg/spec'\nimport { PathScurry } from 'path-scurry'\nimport {\n Dependency,\n DependencyTypeLong,\n longDependencyTypes,\n shorten,\n} from '../dependencies.js'\nimport { Graph } from '../graph.js'\nimport { Node } from '../node.js'\nimport { removeOptionalSubgraph } from '../remove-optional-subgraph.js'\n\ntype FileTypeInfo = {\n id: DepID\n path: string\n isDirectory: boolean\n}\n\n/**\n * Only install devDeps for git dependencies and importers\n * Everything else always gets installed\n */\nconst shouldInstallDepType = (\n node: Node,\n depType: DependencyTypeLong,\n) =>\n depType !== 'devDependencies' ||\n node.importer ||\n node.id.startsWith('git')\n\n/**\n * Retrieve the {@link DepID} and location for a `file:` type {@link Node}.\n */\nconst getFileTypeInfo = (\n spec: Spec,\n fromNode: Node,\n scurry: PathScurry,\n): FileTypeInfo | undefined => {\n const f = spec.final\n if (f.type !== 'file') return\n\n /* c8 ignore start - should be impossible */\n if (!f.file) {\n throw error('no path on file specifier', { spec })\n }\n /* c8 ignore stop */\n\n // Given that both linked folders and local tarballs (both defined with\n // usage of the `file:` spec prefix) location needs to be relative to their\n // parents, build the expected path and use it for both location and id\n const target = scurry.cwd.resolve(fromNode.location).resolve(f.file)\n const path = target.relativePosix()\n const id = joinDepIDTuple(['file', path])\n\n return {\n path,\n id,\n isDirectory: !!target.lstatSync()?.isDirectory(),\n }\n}\n\nconst isStringArray = (a: unknown): a is string[] =>\n Array.isArray(a) && !a.some(b => typeof b !== 'string')\n\nexport const appendNodes = async (\n packageInfo: PackageInfoClient,\n graph: Graph,\n fromNode: Node,\n deps: Dependency[],\n scurry: PathScurry,\n options: SpecOptions,\n seen: Set<DepID>,\n) => {\n /* c8 ignore next */\n if (seen.has(fromNode.id)) return\n seen.add(fromNode.id)\n\n await Promise.all(\n deps.map(async ({ spec, type }) => {\n // see if there's a satisfying node in the graph currently\n const fileTypeInfo = getFileTypeInfo(spec, fromNode, scurry)\n const existingNode = graph.findResolution(spec, fromNode)\n if (existingNode) {\n graph.addEdge(type, spec, fromNode, existingNode)\n return\n }\n const edgeOptional =\n type === 'optional' || type === 'peerOptional'\n const mani = await packageInfo\n .manifest(spec, { from: scurry.resolve(fromNode.location) })\n .catch((er: unknown) => {\n // optional deps ignored if inaccessible\n if (edgeOptional || fromNode.optional) {\n return undefined\n }\n throw er\n })\n\n if (!mani) {\n if (!edgeOptional && fromNode.isOptional()) {\n // failed resolution of a non-optional dep of an optional node\n // have to clean up the dependents\n removeOptionalSubgraph(graph, fromNode)\n return\n } else if (edgeOptional) {\n // failed resolution of an optional dep, just ignore it,\n // nothing to prune because we never added it in the first place.\n return\n } else {\n throw error('failed to resolve dependency', {\n spec,\n from: fromNode.location,\n })\n }\n }\n\n const node = graph.placePackage(\n fromNode,\n type,\n spec,\n mani,\n fileTypeInfo?.id,\n )\n\n /* c8 ignore start - not possible, already ensured manifest */\n if (!node) {\n throw error('failed to place package', {\n from: fromNode.location,\n spec,\n })\n }\n /* c8 ignore stop */\n\n if (fileTypeInfo?.path && fileTypeInfo.isDirectory) {\n node.location = fileTypeInfo.path\n }\n node.setResolved()\n const nestedAppends: Promise<unknown>[] = []\n\n const bundleDeps = node.manifest?.bundleDependencies\n const bundled = new Set<string>(\n (\n node.id.startsWith('git') ||\n node.importer ||\n !isStringArray(bundleDeps)\n ) ?\n []\n : bundleDeps,\n )\n for (const depTypeName of longDependencyTypes) {\n const depRecord: Record<string, string> | undefined =\n mani[depTypeName]\n\n if (depRecord && shouldInstallDepType(node, depTypeName)) {\n const nextDeps: Dependency[] = Object.entries(depRecord)\n .filter(([name]) => !bundled.has(name))\n .map(([name, bareSpec]) => ({\n // if foo:a@1 depends on b@2, then it must be foo:b@2\n spec: Spec.parse(name, bareSpec, {\n ...options,\n registry: spec.registry,\n }),\n type: shorten(depTypeName, name, mani),\n }))\n if (nextDeps.length) {\n nestedAppends.push(\n appendNodes(\n packageInfo,\n graph,\n node,\n nextDeps,\n scurry,\n options,\n seen,\n ),\n )\n }\n }\n }\n await Promise.all(nestedAppends)\n }),\n )\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import { Graph } from '../graph.js';
2
+ import { AddNodesOptions } from './add-nodes.js';
3
+ import { RemoveNodesOptions } from './remove-nodes.js';
4
+ export type BuildIdealFromStartingGraphOptions = AddNodesOptions & RemoveNodesOptions & {
5
+ projectRoot: string;
6
+ };
7
+ /**
8
+ * Builds an ideal {@link Graph} representing the dependencies that
9
+ * should be present in order to fulfill the requirements defined
10
+ * by the `package.json` and `vlt-lock.json` files using the `graph` set
11
+ * in options as a starting point. Also add / remove any dependencies
12
+ * listed in the `add` and `remove` properties.
13
+ */
14
+ export declare const buildIdealFromStartingGraph: (options: BuildIdealFromStartingGraphOptions) => Promise<Graph>;
15
+ //# sourceMappingURL=build-ideal-from-starting-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-ideal-from-starting-graph.d.ts","sourceRoot":"","sources":["../../../src/ideal/build-ideal-from-starting-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,OAAO,EAAE,eAAe,EAAY,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAe,MAAM,mBAAmB,CAAA;AAEnE,MAAM,MAAM,kCAAkC,GAAG,eAAe,GAC9D,kBAAkB,GAAG;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,YAC7B,kCAAkC,KAC1C,OAAO,CAAC,KAAK,CAqBf,CAAA"}
@@ -0,0 +1,28 @@
1
+ import { getImporterSpecs } from './get-importer-specs.js';
2
+ import { addNodes } from './add-nodes.js';
3
+ import { removeNodes } from './remove-nodes.js';
4
+ /**
5
+ * Builds an ideal {@link Graph} representing the dependencies that
6
+ * should be present in order to fulfill the requirements defined
7
+ * by the `package.json` and `vlt-lock.json` files using the `graph` set
8
+ * in options as a starting point. Also add / remove any dependencies
9
+ * listed in the `add` and `remove` properties.
10
+ */
11
+ export const buildIdealFromStartingGraph = async (options) => {
12
+ // Gets a map of dependencies that are keyed to its importer node ids,
13
+ // merging values already found in the graph with user specified values.
14
+ // Any dependencies that are already satisfied in the starting `graph`
15
+ // are going to be pruned from the resulting object.
16
+ const importerSpecs = getImporterSpecs(options);
17
+ // add nodes, fetching remote manifests for each dependency to be added
18
+ await addNodes({ ...options, ...importerSpecs });
19
+ // move things into their default locations, if possible
20
+ for (const node of options.graph.nodes.values()) {
21
+ node.setDefaultLocation();
22
+ }
23
+ // removes any dependencies that are listed in the `remove` option
24
+ removeNodes(options);
25
+ options.graph.gc();
26
+ return options.graph;
27
+ };
28
+ //# sourceMappingURL=build-ideal-from-starting-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-ideal-from-starting-graph.js","sourceRoot":"","sources":["../../../src/ideal/build-ideal-from-starting-graph.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAmB,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAsB,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAOnE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,EAC9C,OAA2C,EAC3B,EAAE;IAClB,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,oDAAoD;IACpD,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAE/C,uEAAuE;IACvE,MAAM,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC,CAAA;IAEhD,wDAAwD;IACxD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAChD,IAAI,CAAC,kBAAkB,EAAE,CAAA;IAC3B,CAAC;IAED,kEAAkE;IAClE,WAAW,CAAC,OAAO,CAAC,CAAA;IAEpB,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAA;IAElB,OAAO,OAAO,CAAC,KAAK,CAAA;AACtB,CAAC,CAAA","sourcesContent":["import { Graph } from '../graph.js'\nimport { getImporterSpecs } from './get-importer-specs.js'\nimport { AddNodesOptions, addNodes } from './add-nodes.js'\nimport { RemoveNodesOptions, removeNodes } from './remove-nodes.js'\n\nexport type BuildIdealFromStartingGraphOptions = AddNodesOptions &\n RemoveNodesOptions & {\n projectRoot: string\n }\n\n/**\n * Builds an ideal {@link Graph} representing the dependencies that\n * should be present in order to fulfill the requirements defined\n * by the `package.json` and `vlt-lock.json` files using the `graph` set\n * in options as a starting point. Also add / remove any dependencies\n * listed in the `add` and `remove` properties.\n */\nexport const buildIdealFromStartingGraph = async (\n options: BuildIdealFromStartingGraphOptions,\n): Promise<Graph> => {\n // Gets a map of dependencies that are keyed to its importer node ids,\n // merging values already found in the graph with user specified values.\n // Any dependencies that are already satisfied in the starting `graph`\n // are going to be pruned from the resulting object.\n const importerSpecs = getImporterSpecs(options)\n\n // add nodes, fetching remote manifests for each dependency to be added\n await addNodes({ ...options, ...importerSpecs })\n\n // move things into their default locations, if possible\n for (const node of options.graph.nodes.values()) {\n node.setDefaultLocation()\n }\n\n // removes any dependencies that are listed in the `remove` option\n removeNodes(options)\n\n options.graph.gc()\n\n return options.graph\n}\n"]}