metro 0.71.0 → 0.71.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +22 -21
- package/src/DeltaBundler/Worker.flow.js +78 -0
- package/src/DeltaBundler/Worker.flow.js.flow +121 -0
- package/src/DeltaBundler/Worker.js +8 -66
- package/src/DeltaBundler/Worker.js.flow +8 -107
- package/src/DeltaBundler/__fixtures__/hasteImpl.js +4 -0
- package/src/DeltaBundler/graphOperations.js +375 -222
- package/src/DeltaBundler/graphOperations.js.flow +401 -232
- package/src/cli.js +5 -0
- package/src/cli.js.flow +5 -0
- package/src/commands/build.js +4 -3
- package/src/commands/build.js.flow +3 -1
- package/src/commands/serve.js +3 -3
- package/src/commands/serve.js.flow +3 -1
- package/src/index.flow.js +392 -0
- package/src/index.flow.js.flow +480 -0
- package/src/index.js +8 -380
- package/src/index.js.flow +8 -466
- package/src/node-haste/DependencyGraph/ModuleResolution.js +15 -3
- package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +15 -0
- package/src/node-haste/DependencyGraph/createHasteMap.js +77 -19
- package/src/node-haste/DependencyGraph/createHasteMap.js.flow +12 -8
|
@@ -8,6 +8,26 @@
|
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Portions of this code are based on the Synchronous Cycle Collection
|
|
13
|
+
* algorithm described in:
|
|
14
|
+
*
|
|
15
|
+
* David F. Bacon and V. T. Rajan. 2001. Concurrent Cycle Collection in
|
|
16
|
+
* Reference Counted Systems. In Proceedings of the 15th European Conference on
|
|
17
|
+
* Object-Oriented Programming (ECOOP '01). Springer-Verlag, Berlin,
|
|
18
|
+
* Heidelberg, 207–235.
|
|
19
|
+
*
|
|
20
|
+
* Notable differences from the algorithm in the paper:
|
|
21
|
+
* 1. Our implementation uses the inverseDependencies set (which we already
|
|
22
|
+
* have to maintain) instead of a separate refcount variable. A module's
|
|
23
|
+
* reference count is equal to the size of its inverseDependencies set, plus
|
|
24
|
+
* 1 if it's an entry point of the graph.
|
|
25
|
+
* 2. We keep the "root buffer" (possibleCycleRoots) free of duplicates by
|
|
26
|
+
* making it a Set, instead of storing a "buffered" flag on each node.
|
|
27
|
+
* 3. On top of tracking edges between nodes, we also count references between
|
|
28
|
+
* nodes and entries in the importBundleNames set.
|
|
29
|
+
*/
|
|
30
|
+
|
|
11
31
|
'use strict';
|
|
12
32
|
|
|
13
33
|
import type {
|
|
@@ -19,11 +39,36 @@ import type {
|
|
|
19
39
|
TransformResultDependency,
|
|
20
40
|
} from './types.flow';
|
|
21
41
|
|
|
42
|
+
const invariant = require('invariant');
|
|
22
43
|
const nullthrows = require('nullthrows');
|
|
23
44
|
|
|
45
|
+
// TODO: Convert to a Flow enum
|
|
46
|
+
type NodeColor =
|
|
47
|
+
// In use or free
|
|
48
|
+
| 'black'
|
|
49
|
+
|
|
50
|
+
// Possible member of cycle
|
|
51
|
+
| 'gray'
|
|
52
|
+
|
|
53
|
+
// Member of garbage cycle
|
|
54
|
+
| 'white'
|
|
55
|
+
|
|
56
|
+
// Possible root of cycle
|
|
57
|
+
| 'purple'
|
|
58
|
+
|
|
59
|
+
// Inherently acyclic node (Not currently used)
|
|
60
|
+
| 'green';
|
|
61
|
+
|
|
24
62
|
// Private state for the graph that persists between operations.
|
|
25
63
|
export opaque type PrivateState = {
|
|
26
|
-
|
|
64
|
+
+gc: {
|
|
65
|
+
// GC state for nodes in the graph (graph.dependencies)
|
|
66
|
+
+color: Map<string, NodeColor>,
|
|
67
|
+
+possibleCycleRoots: Set<string>,
|
|
68
|
+
|
|
69
|
+
// Reference counts for entries in importBundleNames
|
|
70
|
+
+importBundleRefs: Map<string, number>,
|
|
71
|
+
},
|
|
27
72
|
};
|
|
28
73
|
|
|
29
74
|
function createGraph<T>(options: GraphInputOptions): Graph<T> {
|
|
@@ -31,7 +76,13 @@ function createGraph<T>(options: GraphInputOptions): Graph<T> {
|
|
|
31
76
|
...options,
|
|
32
77
|
dependencies: new Map(),
|
|
33
78
|
importBundleNames: new Set(),
|
|
34
|
-
privateState: {
|
|
79
|
+
privateState: {
|
|
80
|
+
gc: {
|
|
81
|
+
color: new Map(),
|
|
82
|
+
possibleCycleRoots: new Set(),
|
|
83
|
+
importBundleRefs: new Map(),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
35
86
|
};
|
|
36
87
|
}
|
|
37
88
|
|
|
@@ -39,7 +90,6 @@ type Result<T> = {
|
|
|
39
90
|
added: Map<string, Module<T>>,
|
|
40
91
|
modified: Map<string, Module<T>>,
|
|
41
92
|
deleted: Set<string>,
|
|
42
|
-
...
|
|
43
93
|
};
|
|
44
94
|
|
|
45
95
|
/**
|
|
@@ -51,7 +101,10 @@ type Delta = $ReadOnly<{
|
|
|
51
101
|
added: Set<string>,
|
|
52
102
|
modified: Set<string>,
|
|
53
103
|
deleted: Set<string>,
|
|
54
|
-
|
|
104
|
+
|
|
105
|
+
// A place to temporarily track inverse dependencies for a module while it is
|
|
106
|
+
// being processed but has not been added to `graph.dependencies` yet.
|
|
107
|
+
earlyInverseDependencies: Map<string, Set<string>>,
|
|
55
108
|
}>;
|
|
56
109
|
|
|
57
110
|
type InternalOptions<T> = $ReadOnly<{
|
|
@@ -103,7 +156,7 @@ async function traverseDependencies<T>(
|
|
|
103
156
|
added: new Set(),
|
|
104
157
|
modified: new Set(),
|
|
105
158
|
deleted: new Set(),
|
|
106
|
-
|
|
159
|
+
earlyInverseDependencies: new Map(),
|
|
107
160
|
};
|
|
108
161
|
|
|
109
162
|
const internalOptions = getInternalOptions(options);
|
|
@@ -122,6 +175,8 @@ async function traverseDependencies<T>(
|
|
|
122
175
|
}
|
|
123
176
|
}
|
|
124
177
|
|
|
178
|
+
collectCycles(graph, delta);
|
|
179
|
+
|
|
125
180
|
const added = new Map();
|
|
126
181
|
for (const path of delta.added) {
|
|
127
182
|
added.set(path, nullthrows(graph.dependencies.get(path)));
|
|
@@ -150,11 +205,29 @@ async function initialTraverseDependencies<T>(
|
|
|
150
205
|
added: new Set(),
|
|
151
206
|
modified: new Set(),
|
|
152
207
|
deleted: new Set(),
|
|
153
|
-
|
|
208
|
+
earlyInverseDependencies: new Map(),
|
|
154
209
|
};
|
|
155
210
|
|
|
156
211
|
const internalOptions = getInternalOptions(options);
|
|
157
212
|
|
|
213
|
+
invariant(
|
|
214
|
+
graph.dependencies.size === 0,
|
|
215
|
+
'initialTraverseDependencies called on nonempty graph',
|
|
216
|
+
);
|
|
217
|
+
invariant(
|
|
218
|
+
graph.importBundleNames.size === 0,
|
|
219
|
+
'initialTraverseDependencies called on nonempty graph',
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
graph.privateState.gc.color.clear();
|
|
223
|
+
graph.privateState.gc.possibleCycleRoots.clear();
|
|
224
|
+
graph.privateState.gc.importBundleRefs.clear();
|
|
225
|
+
|
|
226
|
+
for (const path of graph.entryPoints) {
|
|
227
|
+
// Each entry point implicitly has a refcount of 1, so mark them all black.
|
|
228
|
+
graph.privateState.gc.color.set(path, 'black');
|
|
229
|
+
}
|
|
230
|
+
|
|
158
231
|
await Promise.all(
|
|
159
232
|
[...graph.entryPoints].map((path: string) =>
|
|
160
233
|
traverseDependenciesForSingleFile(path, graph, delta, internalOptions),
|
|
@@ -204,7 +277,7 @@ async function processModule<T>(
|
|
|
204
277
|
);
|
|
205
278
|
|
|
206
279
|
const previousModule = graph.dependencies.get(path) || {
|
|
207
|
-
inverseDependencies: delta.
|
|
280
|
+
inverseDependencies: delta.earlyInverseDependencies.get(path) || new Set(),
|
|
208
281
|
path,
|
|
209
282
|
};
|
|
210
283
|
const previousDependencies = previousModule.dependencies || new Map();
|
|
@@ -212,273 +285,177 @@ async function processModule<T>(
|
|
|
212
285
|
// Update the module information.
|
|
213
286
|
const module = {
|
|
214
287
|
...previousModule,
|
|
215
|
-
dependencies: new Map(),
|
|
288
|
+
dependencies: new Map(previousDependencies),
|
|
216
289
|
getSource: result.getSource,
|
|
217
290
|
output: result.output,
|
|
218
291
|
};
|
|
219
292
|
graph.dependencies.set(module.path, module);
|
|
220
293
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
232
|
-
.forEach(([relativePath, dependency]) =>
|
|
294
|
+
// Diff dependencies (1/2): remove dependencies that have changed or been removed.
|
|
295
|
+
for (const [relativePath, prevDependency] of previousDependencies) {
|
|
296
|
+
const curDependency = currentDependencies.get(relativePath);
|
|
297
|
+
if (
|
|
298
|
+
!curDependency ||
|
|
299
|
+
curDependency.absolutePath !== prevDependency.absolutePath ||
|
|
300
|
+
(options.experimentalImportBundleSupport &&
|
|
301
|
+
curDependency.data.data.asyncType !==
|
|
302
|
+
prevDependency.data.data.asyncType)
|
|
303
|
+
) {
|
|
233
304
|
removeDependency(
|
|
234
305
|
module,
|
|
235
|
-
|
|
306
|
+
relativePath,
|
|
307
|
+
prevDependency,
|
|
236
308
|
graph,
|
|
237
309
|
delta,
|
|
238
|
-
|
|
239
|
-
)
|
|
240
|
-
|
|
310
|
+
options,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
241
314
|
|
|
242
|
-
//
|
|
243
|
-
// added and removed dependency, to get all the modules that have to be added
|
|
244
|
-
// and removed from the dependency graph.
|
|
315
|
+
// Diff dependencies (2/2): add dependencies that have changed or been added.
|
|
245
316
|
const promises = [];
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
317
|
+
for (const [relativePath, curDependency] of currentDependencies) {
|
|
318
|
+
const prevDependency = previousDependencies.get(relativePath);
|
|
319
|
+
if (
|
|
320
|
+
!prevDependency ||
|
|
321
|
+
prevDependency.absolutePath !== curDependency.absolutePath ||
|
|
322
|
+
(options.experimentalImportBundleSupport &&
|
|
323
|
+
prevDependency.data.data.asyncType !==
|
|
324
|
+
curDependency.data.data.asyncType)
|
|
325
|
+
) {
|
|
326
|
+
promises.push(
|
|
327
|
+
addDependency(
|
|
328
|
+
module,
|
|
329
|
+
relativePath,
|
|
330
|
+
curDependency,
|
|
331
|
+
graph,
|
|
332
|
+
delta,
|
|
333
|
+
options,
|
|
334
|
+
),
|
|
335
|
+
);
|
|
263
336
|
}
|
|
264
337
|
}
|
|
265
338
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
339
|
+
await Promise.all(promises);
|
|
340
|
+
|
|
341
|
+
// Replace dependencies with the correctly-ordered version. As long as all
|
|
342
|
+
// the above promises have resolved, this will be the same map but without
|
|
343
|
+
// the added nondeterminism of promise resolution order. Because this
|
|
344
|
+
// assignment does not add or remove edges, it does NOT invalidate any of the
|
|
345
|
+
// garbage collection state.
|
|
346
|
+
|
|
347
|
+
// Catch obvious errors with a cheap assertion.
|
|
348
|
+
invariant(
|
|
349
|
+
module.dependencies.size === currentDependencies.size,
|
|
350
|
+
'Failed to add the correct dependencies',
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// $FlowFixMe[cannot-write]
|
|
354
|
+
module.dependencies = currentDependencies;
|
|
355
|
+
|
|
275
356
|
return module;
|
|
276
357
|
}
|
|
277
358
|
|
|
278
359
|
async function addDependency<T>(
|
|
279
360
|
parentModule: Module<T>,
|
|
280
|
-
|
|
361
|
+
relativePath: string,
|
|
362
|
+
dependency: Dependency,
|
|
281
363
|
graph: Graph<T>,
|
|
282
364
|
delta: Delta,
|
|
283
365
|
options: InternalOptions<T>,
|
|
284
366
|
): Promise<void> {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (delta.deleted.has(path)) {
|
|
304
|
-
// Mark the addition by clearing a prior deletion.
|
|
305
|
-
delta.deleted.delete(path);
|
|
367
|
+
const path = dependency.absolutePath;
|
|
368
|
+
|
|
369
|
+
// The module may already exist, in which case we just need to update some
|
|
370
|
+
// bookkeeping instead of adding a new node to the graph.
|
|
371
|
+
let module = graph.dependencies.get(path);
|
|
372
|
+
|
|
373
|
+
if (options.shallow) {
|
|
374
|
+
// Don't add a node for the module if the graph is shallow (single-module).
|
|
375
|
+
} else if (
|
|
376
|
+
options.experimentalImportBundleSupport &&
|
|
377
|
+
dependency.data.data.asyncType != null
|
|
378
|
+
) {
|
|
379
|
+
// Don't add a node for the module if we are traversing async dependencies
|
|
380
|
+
// lazily (and this is an async dependency). Instead, record it in
|
|
381
|
+
// importBundleNames.
|
|
382
|
+
incrementImportBundleReference(dependency, graph);
|
|
306
383
|
} else {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
graph: Graph<T>,
|
|
331
|
-
currModule: string,
|
|
332
|
-
visited: Set<string>,
|
|
333
|
-
): Set<string> {
|
|
334
|
-
if (visited.has(currModule)) {
|
|
335
|
-
return new Set();
|
|
336
|
-
}
|
|
337
|
-
visited.add(currModule);
|
|
338
|
-
if (!inverseDependencies.size) {
|
|
339
|
-
return new Set([currModule]);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
return Array.from(inverseDependencies)
|
|
343
|
-
.filter(inverseDep => graph.dependencies.has(inverseDep))
|
|
344
|
-
.reduce((acc, inverseDep) => {
|
|
345
|
-
const mod = graph.dependencies.get(inverseDep);
|
|
346
|
-
if (!mod) {
|
|
347
|
-
return acc;
|
|
384
|
+
if (!module) {
|
|
385
|
+
// Add a new node to the graph.
|
|
386
|
+
const earlyInverseDependencies = delta.earlyInverseDependencies.get(path);
|
|
387
|
+
if (earlyInverseDependencies) {
|
|
388
|
+
// This module is being transformed at the moment in parallel, so we
|
|
389
|
+
// should only mark its parent as an inverse dependency.
|
|
390
|
+
earlyInverseDependencies.add(parentModule.path);
|
|
391
|
+
} else {
|
|
392
|
+
if (delta.deleted.has(path)) {
|
|
393
|
+
// Mark the addition by clearing a prior deletion.
|
|
394
|
+
delta.deleted.delete(path);
|
|
395
|
+
} else {
|
|
396
|
+
// Mark the addition in the added set.
|
|
397
|
+
delta.added.add(path);
|
|
398
|
+
delta.modified.delete(path);
|
|
399
|
+
}
|
|
400
|
+
delta.earlyInverseDependencies.set(path, new Set([parentModule.path]));
|
|
401
|
+
|
|
402
|
+
options.onDependencyAdd();
|
|
403
|
+
module = await processModule(path, graph, delta, options);
|
|
404
|
+
options.onDependencyAdded();
|
|
405
|
+
|
|
406
|
+
graph.dependencies.set(module.path, module);
|
|
348
407
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
acc.add(x);
|
|
356
|
-
});
|
|
357
|
-
return acc;
|
|
358
|
-
}, new Set());
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Given `inverseDependencies`, tracing back inverse dependencies to
|
|
363
|
-
* see if it only leads back to `parentModule`.
|
|
364
|
-
*/
|
|
365
|
-
function canSafelyRemoveFromParentModule<T>(
|
|
366
|
-
inverseDependencies: Set<string>,
|
|
367
|
-
parentModule: string,
|
|
368
|
-
graph: Graph<T>,
|
|
369
|
-
canBeRemovedSafely: Set<string>,
|
|
370
|
-
delta: Delta,
|
|
371
|
-
): boolean {
|
|
372
|
-
const visited = new Set();
|
|
373
|
-
const topInverseDependencies = getAllTopLevelInverseDependencies(
|
|
374
|
-
inverseDependencies,
|
|
375
|
-
graph,
|
|
376
|
-
'', // current module name
|
|
377
|
-
visited,
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
if (!topInverseDependencies.size) {
|
|
381
|
-
/**
|
|
382
|
-
* This happens when parentModule and inverseDependencies have a circular dependency.
|
|
383
|
-
* This will eventually become an empty set due to the `visited` Set being the
|
|
384
|
-
* base case for the recursive call.
|
|
385
|
-
*/
|
|
386
|
-
return true;
|
|
408
|
+
}
|
|
409
|
+
if (module) {
|
|
410
|
+
// We either added a new node to the graph, or we're updating an existing one.
|
|
411
|
+
module.inverseDependencies.add(parentModule.path);
|
|
412
|
+
markModuleInUse(module, graph);
|
|
413
|
+
}
|
|
387
414
|
}
|
|
388
415
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
* We can only mark the `visited` Set of modules to be safely removable if
|
|
395
|
-
* 1. We do not have top a level module to compare with parentModule.
|
|
396
|
-
* This can happen when trying to see if we can safely remove from
|
|
397
|
-
* a module that was deleted. This is why we filtered them out with `delta.deleted`
|
|
398
|
-
* 2. We have one top module and it is parentModule
|
|
399
|
-
*/
|
|
400
|
-
const canSafelyRemove =
|
|
401
|
-
!undeletedInverseDependencies.length ||
|
|
402
|
-
(undeletedInverseDependencies.length === 1 &&
|
|
403
|
-
undeletedInverseDependencies[0] === parentModule);
|
|
404
|
-
|
|
405
|
-
if (canSafelyRemove) {
|
|
406
|
-
visited.forEach(mod => {
|
|
407
|
-
canBeRemovedSafely.add(mod);
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
return canSafelyRemove;
|
|
416
|
+
// Always update the parent's dependency map.
|
|
417
|
+
// This means the parent's dependencies can get desynced from
|
|
418
|
+
// inverseDependencies and the other fields in the case of lazy edges.
|
|
419
|
+
// Not an optimal representation :(
|
|
420
|
+
parentModule.dependencies.set(relativePath, dependency);
|
|
411
421
|
}
|
|
412
422
|
|
|
413
423
|
function removeDependency<T>(
|
|
414
424
|
parentModule: Module<T>,
|
|
415
|
-
|
|
425
|
+
relativePath: string,
|
|
426
|
+
dependency: Dependency,
|
|
416
427
|
graph: Graph<T>,
|
|
417
428
|
delta: Delta,
|
|
418
|
-
|
|
419
|
-
// module(s) that we're sure can be removed. This will skip expensive
|
|
420
|
-
// inverse dependency traversals.
|
|
421
|
-
canBeRemovedSafely: Set<string> = new Set(),
|
|
429
|
+
options: InternalOptions<T>,
|
|
422
430
|
): void {
|
|
431
|
+
parentModule.dependencies.delete(relativePath);
|
|
432
|
+
|
|
433
|
+
const {absolutePath} = dependency;
|
|
434
|
+
|
|
435
|
+
if (
|
|
436
|
+
options.experimentalImportBundleSupport &&
|
|
437
|
+
dependency.data.data.asyncType != null
|
|
438
|
+
) {
|
|
439
|
+
decrementImportBundleReference(dependency, graph);
|
|
440
|
+
}
|
|
441
|
+
|
|
423
442
|
const module = graph.dependencies.get(absolutePath);
|
|
424
443
|
|
|
425
444
|
if (!module) {
|
|
426
445
|
return;
|
|
427
446
|
}
|
|
428
|
-
|
|
429
447
|
module.inverseDependencies.delete(parentModule.path);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
!canSafelyRemoveFromParentModule(
|
|
438
|
-
module.inverseDependencies,
|
|
439
|
-
module.path,
|
|
440
|
-
graph,
|
|
441
|
-
canBeRemovedSafely,
|
|
442
|
-
delta,
|
|
443
|
-
)
|
|
444
|
-
) {
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (delta.added.has(module.path)) {
|
|
450
|
-
// Mark the deletion by clearing a prior addition.
|
|
451
|
-
delta.added.delete(module.path);
|
|
448
|
+
if (
|
|
449
|
+
module.inverseDependencies.size > 0 ||
|
|
450
|
+
graph.entryPoints.has(absolutePath)
|
|
451
|
+
) {
|
|
452
|
+
// The reference count has decreased, but not to zero.
|
|
453
|
+
// NOTE: Each entry point implicitly has a refcount of 1.
|
|
454
|
+
markAsPossibleCycleRoot(module, graph);
|
|
452
455
|
} else {
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
delta.modified.delete(module.path);
|
|
456
|
+
// The reference count has decreased to zero.
|
|
457
|
+
releaseModule(module, graph, delta, options);
|
|
456
458
|
}
|
|
457
|
-
|
|
458
|
-
// This module is not used anywhere else! We can clear it from the bundle.
|
|
459
|
-
// Clean up all the state associated with this module in order to correctly
|
|
460
|
-
// re-add it if we encounter it again.
|
|
461
|
-
graph.dependencies.delete(module.path);
|
|
462
|
-
delta.inverseDependencies.delete(module.path);
|
|
463
|
-
|
|
464
|
-
// Now we need to iterate through the module dependencies in order to
|
|
465
|
-
// clean up everything (we cannot read the module because it may have
|
|
466
|
-
// been deleted).
|
|
467
|
-
Array.from(module.dependencies.values())
|
|
468
|
-
.filter(
|
|
469
|
-
dependency =>
|
|
470
|
-
graph.dependencies.has(dependency.absolutePath) &&
|
|
471
|
-
dependency.absolutePath !== parentModule.path,
|
|
472
|
-
)
|
|
473
|
-
.forEach(dependency =>
|
|
474
|
-
removeDependency(
|
|
475
|
-
module,
|
|
476
|
-
dependency.absolutePath,
|
|
477
|
-
graph,
|
|
478
|
-
delta,
|
|
479
|
-
canBeRemovedSafely,
|
|
480
|
-
),
|
|
481
|
-
);
|
|
482
459
|
}
|
|
483
460
|
|
|
484
461
|
function resolveDependencies<T>(
|
|
@@ -572,6 +549,198 @@ function reorderDependencies<T>(
|
|
|
572
549
|
});
|
|
573
550
|
}
|
|
574
551
|
|
|
552
|
+
/** Garbage collection functions */
|
|
553
|
+
|
|
554
|
+
// Add an entry to importBundleNames (or increase the reference count of an existing one)
|
|
555
|
+
function incrementImportBundleReference<T>(
|
|
556
|
+
dependency: Dependency,
|
|
557
|
+
graph: Graph<T>,
|
|
558
|
+
) {
|
|
559
|
+
const {absolutePath} = dependency;
|
|
560
|
+
|
|
561
|
+
graph.privateState.gc.importBundleRefs.set(
|
|
562
|
+
absolutePath,
|
|
563
|
+
(graph.privateState.gc.importBundleRefs.get(absolutePath) ?? 0) + 1,
|
|
564
|
+
);
|
|
565
|
+
graph.importBundleNames.add(absolutePath);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Decrease the reference count of an entry in importBundleNames (and delete it if necessary)
|
|
569
|
+
function decrementImportBundleReference<T>(
|
|
570
|
+
dependency: Dependency,
|
|
571
|
+
graph: Graph<T>,
|
|
572
|
+
) {
|
|
573
|
+
const {absolutePath} = dependency;
|
|
574
|
+
|
|
575
|
+
const prevRefCount = nullthrows(
|
|
576
|
+
graph.privateState.gc.importBundleRefs.get(absolutePath),
|
|
577
|
+
);
|
|
578
|
+
invariant(
|
|
579
|
+
prevRefCount > 0,
|
|
580
|
+
'experimentalImportBundleSupport: import bundle refcount not valid',
|
|
581
|
+
);
|
|
582
|
+
graph.privateState.gc.importBundleRefs.set(absolutePath, prevRefCount - 1);
|
|
583
|
+
if (prevRefCount === 1) {
|
|
584
|
+
graph.privateState.gc.importBundleRefs.delete(absolutePath);
|
|
585
|
+
graph.importBundleNames.delete(absolutePath);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Mark a module as in use (ref count >= 1)
|
|
590
|
+
function markModuleInUse<T>(module: Module<T>, graph: Graph<T>) {
|
|
591
|
+
graph.privateState.gc.color.set(module.path, 'black');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Delete an unreachable module from the graph immediately, unless it's queued
|
|
595
|
+
// for later deletion as a potential cycle root. Delete the module's outbound
|
|
596
|
+
// edges.
|
|
597
|
+
// Called when the reference count of a module has reached 0.
|
|
598
|
+
function releaseModule<T>(
|
|
599
|
+
module: Module<T>,
|
|
600
|
+
graph: Graph<T>,
|
|
601
|
+
delta: Delta,
|
|
602
|
+
options: InternalOptions<T>,
|
|
603
|
+
) {
|
|
604
|
+
for (const [relativePath, dependency] of module.dependencies) {
|
|
605
|
+
removeDependency(module, relativePath, dependency, graph, delta, options);
|
|
606
|
+
}
|
|
607
|
+
graph.privateState.gc.color.set(module.path, 'black');
|
|
608
|
+
if (!graph.privateState.gc.possibleCycleRoots.has(module.path)) {
|
|
609
|
+
freeModule(module, graph, delta);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Delete an unreachable module from the graph.
|
|
614
|
+
function freeModule<T>(module: Module<T>, graph: Graph<T>, delta: Delta) {
|
|
615
|
+
if (delta.added.has(module.path)) {
|
|
616
|
+
// Mark the deletion by clearing a prior addition.
|
|
617
|
+
delta.added.delete(module.path);
|
|
618
|
+
} else {
|
|
619
|
+
// Mark the deletion in the deleted set.
|
|
620
|
+
delta.deleted.add(module.path);
|
|
621
|
+
delta.modified.delete(module.path);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// This module is not used anywhere else! We can clear it from the bundle.
|
|
625
|
+
// Clean up all the state associated with this module in order to correctly
|
|
626
|
+
// re-add it if we encounter it again.
|
|
627
|
+
graph.dependencies.delete(module.path);
|
|
628
|
+
delta.earlyInverseDependencies.delete(module.path);
|
|
629
|
+
graph.privateState.gc.possibleCycleRoots.delete(module.path);
|
|
630
|
+
graph.privateState.gc.color.delete(module.path);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Mark a module as a possible cycle root
|
|
634
|
+
function markAsPossibleCycleRoot<T>(module: Module<T>, graph: Graph<T>) {
|
|
635
|
+
if (nullthrows(graph.privateState.gc.color.get(module.path)) !== 'purple') {
|
|
636
|
+
graph.privateState.gc.color.set(module.path, 'purple');
|
|
637
|
+
graph.privateState.gc.possibleCycleRoots.add(module.path);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Collect any unreachable cycles in the graph.
|
|
642
|
+
function collectCycles<T>(graph: Graph<T>, delta: Delta) {
|
|
643
|
+
// Mark recursively from roots (trial deletion)
|
|
644
|
+
for (const path of graph.privateState.gc.possibleCycleRoots) {
|
|
645
|
+
const module = nullthrows(graph.dependencies.get(path));
|
|
646
|
+
const color = nullthrows(graph.privateState.gc.color.get(path));
|
|
647
|
+
if (color === 'purple') {
|
|
648
|
+
markGray(module, graph);
|
|
649
|
+
} else {
|
|
650
|
+
graph.privateState.gc.possibleCycleRoots.delete(path);
|
|
651
|
+
if (
|
|
652
|
+
color === 'black' &&
|
|
653
|
+
module.inverseDependencies.size === 0 &&
|
|
654
|
+
!graph.entryPoints.has(path)
|
|
655
|
+
) {
|
|
656
|
+
freeModule(module, graph, delta);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
// Scan recursively from roots (undo unsuccessful trial deletions)
|
|
661
|
+
for (const path of graph.privateState.gc.possibleCycleRoots) {
|
|
662
|
+
const module = nullthrows(graph.dependencies.get(path));
|
|
663
|
+
scan(module, graph);
|
|
664
|
+
}
|
|
665
|
+
// Collect recursively from roots (free unreachable cycles)
|
|
666
|
+
for (const path of graph.privateState.gc.possibleCycleRoots) {
|
|
667
|
+
graph.privateState.gc.possibleCycleRoots.delete(path);
|
|
668
|
+
const module = nullthrows(graph.dependencies.get(path));
|
|
669
|
+
collectWhite(module, graph, delta);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function markGray<T>(module: Module<T>, graph: Graph<T>) {
|
|
674
|
+
const color = nullthrows(graph.privateState.gc.color.get(module.path));
|
|
675
|
+
if (color !== 'gray') {
|
|
676
|
+
graph.privateState.gc.color.set(module.path, 'gray');
|
|
677
|
+
for (const dependency of module.dependencies.values()) {
|
|
678
|
+
const childModule = nullthrows(
|
|
679
|
+
graph.dependencies.get(dependency.absolutePath),
|
|
680
|
+
);
|
|
681
|
+
// The inverse dependency will be restored during the scan phase if this module remains live.
|
|
682
|
+
childModule.inverseDependencies.delete(module.path);
|
|
683
|
+
markGray(childModule, graph);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function scan<T>(module: Module<T>, graph: Graph<T>) {
|
|
689
|
+
const color = nullthrows(graph.privateState.gc.color.get(module.path));
|
|
690
|
+
if (color === 'gray') {
|
|
691
|
+
if (
|
|
692
|
+
module.inverseDependencies.size > 0 ||
|
|
693
|
+
graph.entryPoints.has(module.path)
|
|
694
|
+
) {
|
|
695
|
+
scanBlack(module, graph);
|
|
696
|
+
} else {
|
|
697
|
+
graph.privateState.gc.color.set(module.path, 'white');
|
|
698
|
+
for (const dependency of module.dependencies.values()) {
|
|
699
|
+
const childModule = nullthrows(
|
|
700
|
+
graph.dependencies.get(dependency.absolutePath),
|
|
701
|
+
);
|
|
702
|
+
scan(childModule, graph);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function scanBlack<T>(module: Module<T>, graph: Graph<T>) {
|
|
709
|
+
graph.privateState.gc.color.set(module.path, 'black');
|
|
710
|
+
for (const dependency of module.dependencies.values()) {
|
|
711
|
+
const childModule = nullthrows(
|
|
712
|
+
graph.dependencies.get(dependency.absolutePath),
|
|
713
|
+
);
|
|
714
|
+
// The inverse dependency must have been deleted during the mark phase.
|
|
715
|
+
childModule.inverseDependencies.add(module.path);
|
|
716
|
+
const childColor = nullthrows(
|
|
717
|
+
graph.privateState.gc.color.get(childModule.path),
|
|
718
|
+
);
|
|
719
|
+
if (childColor !== 'black') {
|
|
720
|
+
scanBlack(childModule, graph);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function collectWhite<T>(module: Module<T>, graph: Graph<T>, delta: Delta) {
|
|
726
|
+
const color = nullthrows(graph.privateState.gc.color.get(module.path));
|
|
727
|
+
if (
|
|
728
|
+
color === 'white' &&
|
|
729
|
+
!graph.privateState.gc.possibleCycleRoots.has(module.path)
|
|
730
|
+
) {
|
|
731
|
+
graph.privateState.gc.color.set(module.path, 'black');
|
|
732
|
+
for (const dependency of module.dependencies.values()) {
|
|
733
|
+
const childModule = nullthrows(
|
|
734
|
+
graph.dependencies.get(dependency.absolutePath),
|
|
735
|
+
);
|
|
736
|
+
collectWhite(childModule, graph, delta);
|
|
737
|
+
}
|
|
738
|
+
freeModule(module, graph, delta);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/** End of garbage collection functions */
|
|
743
|
+
|
|
575
744
|
module.exports = {
|
|
576
745
|
createGraph,
|
|
577
746
|
initialTraverseDependencies,
|