metro 0.80.2 → 0.80.4
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 +17 -17
- package/src/DeltaBundler/DeltaCalculator.js +0 -8
- package/src/DeltaBundler/DeltaCalculator.js.flow +0 -9
- package/src/DeltaBundler/Graph.js +290 -275
- package/src/DeltaBundler/Graph.js.flow +314 -278
- package/src/DeltaBundler/buildSubgraph.js +138 -0
- package/src/DeltaBundler/buildSubgraph.js.flow +161 -0
- package/src/DeltaBundler/types.flow.js.flow +29 -18
- package/src/ModuleGraph/worker/collectDependencies.js.flow +1 -1
- package/src/index.flow.js +1 -0
- package/src/index.flow.js.flow +1 -1
- package/src/lib/splitBundleOptions.js +1 -0
- package/src/lib/splitBundleOptions.js.flow +1 -0
- package/src/node-haste/DependencyGraph/ModuleResolution.js +4 -1
- package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +2 -1
- package/src/node-haste/DependencyGraph.js +3 -14
- package/src/node-haste/DependencyGraph.js.flow +3 -14
- package/src/shared/types.flow.js.flow +1 -0
|
@@ -37,17 +37,14 @@ import type {
|
|
|
37
37
|
GraphInputOptions,
|
|
38
38
|
MixedOutput,
|
|
39
39
|
Module,
|
|
40
|
+
ModuleData,
|
|
40
41
|
Options,
|
|
41
42
|
TransformInputOptions,
|
|
42
|
-
TransformResultDependency,
|
|
43
43
|
} from './types.flow';
|
|
44
44
|
|
|
45
|
-
import {
|
|
46
|
-
deriveAbsolutePathFromContext,
|
|
47
|
-
fileMatchesContext,
|
|
48
|
-
} from '../lib/contextModule';
|
|
45
|
+
import {fileMatchesContext} from '../lib/contextModule';
|
|
49
46
|
import CountingSet from '../lib/CountingSet';
|
|
50
|
-
import
|
|
47
|
+
import {buildSubgraph} from './buildSubgraph';
|
|
51
48
|
|
|
52
49
|
const invariant = require('invariant');
|
|
53
50
|
const nullthrows = require('nullthrows');
|
|
@@ -80,17 +77,18 @@ export type Result<T> = {
|
|
|
80
77
|
* files have been modified. This allows to return the added modules before the
|
|
81
78
|
* modified ones (which is useful for things like Hot Module Reloading).
|
|
82
79
|
**/
|
|
83
|
-
type Delta = $ReadOnly<{
|
|
80
|
+
type Delta<T> = $ReadOnly<{
|
|
84
81
|
// `added` and `deleted` are mutually exclusive.
|
|
85
|
-
// Internally, a module can be in both `
|
|
86
|
-
// `deleted`.
|
|
82
|
+
// Internally, a module can be in both `touched` and (either) `added` or
|
|
83
|
+
// `deleted`. Before returning the result, we'll calculate
|
|
84
|
+
// modified = touched - added - deleted.
|
|
87
85
|
added: Set<string>,
|
|
88
|
-
|
|
86
|
+
touched: Set<string>,
|
|
89
87
|
deleted: Set<string>,
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
updatedModuleData: $ReadOnlyMap<string, ModuleData<T>>,
|
|
90
|
+
baseModuleData: Map<string, ModuleData<T>>,
|
|
91
|
+
errors: $ReadOnlyMap<string, Error>,
|
|
94
92
|
}>;
|
|
95
93
|
|
|
96
94
|
type InternalOptions<T> = $ReadOnly<{
|
|
@@ -122,6 +120,14 @@ function getInternalOptions<T>({
|
|
|
122
120
|
};
|
|
123
121
|
}
|
|
124
122
|
|
|
123
|
+
function isWeakOrLazy<T>(
|
|
124
|
+
dependency: Dependency,
|
|
125
|
+
options: InternalOptions<T>,
|
|
126
|
+
): boolean {
|
|
127
|
+
const asyncType = dependency.data.data.asyncType;
|
|
128
|
+
return asyncType === 'weak' || (asyncType != null && options.lazy);
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
export class Graph<T = MixedOutput> {
|
|
126
132
|
+entryPoints: $ReadOnlySet<string>;
|
|
127
133
|
+transformOptions: TransformInputOptions;
|
|
@@ -165,87 +171,124 @@ export class Graph<T = MixedOutput> {
|
|
|
165
171
|
paths: $ReadOnlyArray<string>,
|
|
166
172
|
options: Options<T>,
|
|
167
173
|
): Promise<Result<T>> {
|
|
168
|
-
const delta = {
|
|
169
|
-
added: new Set<string>(),
|
|
170
|
-
modified: new Set<string>(),
|
|
171
|
-
deleted: new Set<string>(),
|
|
172
|
-
earlyInverseDependencies: new Map<string, CountingSet<string>>(),
|
|
173
|
-
};
|
|
174
|
-
|
|
175
174
|
const internalOptions = getInternalOptions(options);
|
|
176
175
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
176
|
+
const modifiedPathsInBaseGraph = new Set(
|
|
177
|
+
paths.filter(path => this.dependencies.has(path)),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const allModifiedPaths = new Set(paths);
|
|
181
|
+
|
|
182
|
+
const delta = await this._buildDelta(
|
|
183
|
+
modifiedPathsInBaseGraph,
|
|
184
|
+
internalOptions,
|
|
185
|
+
// Traverse new or modified paths
|
|
186
|
+
absolutePath =>
|
|
187
|
+
!this.dependencies.has(absolutePath) ||
|
|
188
|
+
allModifiedPaths.has(absolutePath),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// If we have errors we might need to roll back any changes - take
|
|
192
|
+
// snapshots of all modified modules at the base state. We'll also snapshot
|
|
193
|
+
// unmodified modules that become unreachable as they are released, so that
|
|
194
|
+
// we have everything we need to restore the graph to base.
|
|
195
|
+
if (delta.errors.size > 0) {
|
|
196
|
+
for (const modified of modifiedPathsInBaseGraph) {
|
|
197
|
+
delta.baseModuleData.set(
|
|
198
|
+
modified,
|
|
199
|
+
this._moduleSnapshot(nullthrows(this.dependencies.get(modified))),
|
|
200
|
+
);
|
|
186
201
|
}
|
|
187
202
|
}
|
|
188
203
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
// Commit changes in a subtractive pass and then an additive pass - this
|
|
205
|
+
// ensures that any errors encountered on the additive pass would also be
|
|
206
|
+
// encountered on a fresh build (implying legitimate errors in the graph,
|
|
207
|
+
// rather than an error in a module that's no longer reachable).
|
|
208
|
+
for (const modified of modifiedPathsInBaseGraph) {
|
|
209
|
+
// Skip this module if it has errors. Hopefully it will be removed -
|
|
210
|
+
// if not, we'll throw during the additive pass.
|
|
211
|
+
if (delta.errors.has(modified)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const module = this.dependencies.get(modified);
|
|
215
|
+
// The module may have already been released from the graph - we'll readd
|
|
216
|
+
// it if necessary later.
|
|
217
|
+
if (module == null) {
|
|
218
|
+
continue;
|
|
202
219
|
}
|
|
220
|
+
// Process the transform result and dependency removals. This should
|
|
221
|
+
// never encounter an error.
|
|
222
|
+
this._recursivelyCommitModule(modified, delta, internalOptions, {
|
|
223
|
+
onlyRemove: true,
|
|
224
|
+
});
|
|
203
225
|
}
|
|
204
226
|
|
|
227
|
+
// Ensure we have released any unreachable modules before the additive
|
|
228
|
+
// pass.
|
|
205
229
|
this._collectCycles(delta, internalOptions);
|
|
206
230
|
|
|
231
|
+
// Additive pass - any errors we encounter here should be thrown after
|
|
232
|
+
// rolling back the commit.
|
|
233
|
+
try {
|
|
234
|
+
for (const modified of modifiedPathsInBaseGraph) {
|
|
235
|
+
const module = this.dependencies.get(modified);
|
|
236
|
+
// The module may have already been released from the graph (it may yet
|
|
237
|
+
// be readded via another dependency).
|
|
238
|
+
if (module == null) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this._recursivelyCommitModule(modified, delta, internalOptions);
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
// Roll back to base before re-throwing.
|
|
246
|
+
const rollbackDelta: Delta<T> = {
|
|
247
|
+
added: delta.added,
|
|
248
|
+
deleted: delta.deleted,
|
|
249
|
+
touched: new Set(),
|
|
250
|
+
updatedModuleData: delta.baseModuleData,
|
|
251
|
+
baseModuleData: new Map(),
|
|
252
|
+
errors: new Map(),
|
|
253
|
+
};
|
|
254
|
+
for (const modified of modifiedPathsInBaseGraph) {
|
|
255
|
+
const module = this.dependencies.get(modified);
|
|
256
|
+
// The module may have already been released from the graph (it may yet
|
|
257
|
+
// be readded via another dependency).
|
|
258
|
+
if (module == null) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
// Set the module and descendants back to base state.
|
|
262
|
+
this._recursivelyCommitModule(modified, rollbackDelta, internalOptions);
|
|
263
|
+
}
|
|
264
|
+
// Collect cycles again after rolling back. There's no need if we're
|
|
265
|
+
// not rolling back, because we have not removed any edges.
|
|
266
|
+
this._collectCycles(delta, internalOptions);
|
|
267
|
+
|
|
268
|
+
// Cheap check to validate the rollback.
|
|
269
|
+
invariant(
|
|
270
|
+
rollbackDelta.added.size === 0 && rollbackDelta.deleted.size === 0,
|
|
271
|
+
'attempted to roll back a graph commit but there were still changes',
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Re-throw the transform or resolution error originally seen by
|
|
275
|
+
// `buildSubgraph`.
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
|
|
207
279
|
const added = new Map<string, Module<T>>();
|
|
208
280
|
for (const path of delta.added) {
|
|
209
281
|
added.set(path, nullthrows(this.dependencies.get(path)));
|
|
210
282
|
}
|
|
211
283
|
|
|
212
284
|
const modified = new Map<string, Module<T>>();
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
!delta.added.has(path),
|
|
221
|
-
'delta.added has %s, but this path was already in the graph.',
|
|
222
|
-
path,
|
|
223
|
-
);
|
|
224
|
-
if (delta.modified.has(path)) {
|
|
225
|
-
// It's expected that a module may be both modified and subsequently
|
|
226
|
-
// deleted - we'll only return it as deleted.
|
|
227
|
-
if (!delta.deleted.has(path)) {
|
|
228
|
-
// If a module existed before and has not been deleted, it must be
|
|
229
|
-
// in the dependencies map.
|
|
230
|
-
const newModule = nullthrows(this.dependencies.get(path));
|
|
231
|
-
if (
|
|
232
|
-
// Module.dependencies is mutable, so it's not obviously the case
|
|
233
|
-
// that referential equality implies no modification. However, we
|
|
234
|
-
// only mutate dependencies in two cases:
|
|
235
|
-
// 1. Within _processModule. In that case, we always mutate a new
|
|
236
|
-
// module and set a new reference in this.dependencies.
|
|
237
|
-
// 2. During _releaseModule, when recursively removing
|
|
238
|
-
// dependencies. In that case, we immediately discard the module
|
|
239
|
-
// object.
|
|
240
|
-
// TODO: Refactor for more explicit immutability
|
|
241
|
-
newModule !== originalModule ||
|
|
242
|
-
transfromOutputMayDiffer(newModule, originalModule) ||
|
|
243
|
-
// $FlowFixMe[incompatible-call]
|
|
244
|
-
!allDependenciesEqual(newModule, originalModule)
|
|
245
|
-
) {
|
|
246
|
-
modified.set(path, newModule);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
285
|
+
for (const path of modifiedPathsInBaseGraph) {
|
|
286
|
+
if (
|
|
287
|
+
delta.touched.has(path) &&
|
|
288
|
+
!delta.deleted.has(path) &&
|
|
289
|
+
!delta.added.has(path)
|
|
290
|
+
) {
|
|
291
|
+
modified.set(path, nullthrows(this.dependencies.get(path)));
|
|
249
292
|
}
|
|
250
293
|
}
|
|
251
294
|
|
|
@@ -257,13 +300,6 @@ export class Graph<T = MixedOutput> {
|
|
|
257
300
|
}
|
|
258
301
|
|
|
259
302
|
async initialTraverseDependencies(options: Options<T>): Promise<Result<T>> {
|
|
260
|
-
const delta = {
|
|
261
|
-
added: new Set<string>(),
|
|
262
|
-
modified: new Set<string>(),
|
|
263
|
-
deleted: new Set<string>(),
|
|
264
|
-
earlyInverseDependencies: new Map<string, CountingSet<string>>(),
|
|
265
|
-
};
|
|
266
|
-
|
|
267
303
|
const internalOptions = getInternalOptions(options);
|
|
268
304
|
|
|
269
305
|
invariant(
|
|
@@ -280,11 +316,20 @@ export class Graph<T = MixedOutput> {
|
|
|
280
316
|
this.#gc.color.set(path, 'black');
|
|
281
317
|
}
|
|
282
318
|
|
|
283
|
-
await
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
319
|
+
const delta = await this._buildDelta(this.entryPoints, internalOptions);
|
|
320
|
+
|
|
321
|
+
if (delta.errors.size > 0) {
|
|
322
|
+
// If we encountered any errors during traversal, throw one of them.
|
|
323
|
+
// Since errors are encountered in a non-deterministic order, even on
|
|
324
|
+
// fresh builds, it's valid to arbitrarily pick the first.
|
|
325
|
+
throw delta.errors.values().next().value;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
for (const path of this.entryPoints) {
|
|
329
|
+
// We have already thrown on userland errors in the delta, so any error
|
|
330
|
+
// encountered here would be exceptional and fatal.
|
|
331
|
+
this._recursivelyCommitModule(path, delta, internalOptions);
|
|
332
|
+
}
|
|
288
333
|
|
|
289
334
|
this.reorderGraph({
|
|
290
335
|
shallow: options.shallow,
|
|
@@ -297,57 +342,86 @@ export class Graph<T = MixedOutput> {
|
|
|
297
342
|
};
|
|
298
343
|
}
|
|
299
344
|
|
|
300
|
-
async
|
|
301
|
-
|
|
302
|
-
delta: Delta,
|
|
345
|
+
async _buildDelta(
|
|
346
|
+
pathsToVisit: $ReadOnlySet<string>,
|
|
303
347
|
options: InternalOptions<T>,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
348
|
+
moduleFilter?: (path: string) => boolean,
|
|
349
|
+
): Promise<Delta<T>> {
|
|
350
|
+
const subGraph = await buildSubgraph(pathsToVisit, this.#resolvedContexts, {
|
|
351
|
+
resolve: options.resolve,
|
|
352
|
+
transform: async (absolutePath, requireContext) => {
|
|
353
|
+
options.onDependencyAdd();
|
|
354
|
+
const result = await options.transform(absolutePath, requireContext);
|
|
355
|
+
options.onDependencyAdded();
|
|
356
|
+
return result;
|
|
357
|
+
},
|
|
358
|
+
shouldTraverse: (dependency: Dependency) => {
|
|
359
|
+
if (options.shallow || isWeakOrLazy(dependency, options)) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
return moduleFilter == null || moduleFilter(dependency.absolutePath);
|
|
363
|
+
},
|
|
364
|
+
});
|
|
308
365
|
|
|
309
|
-
|
|
366
|
+
return {
|
|
367
|
+
added: new Set(),
|
|
368
|
+
touched: new Set(),
|
|
369
|
+
deleted: new Set(),
|
|
370
|
+
updatedModuleData: subGraph.moduleData,
|
|
371
|
+
baseModuleData: new Map(),
|
|
372
|
+
errors: subGraph.errors,
|
|
373
|
+
};
|
|
310
374
|
}
|
|
311
375
|
|
|
312
|
-
|
|
376
|
+
_recursivelyCommitModule(
|
|
313
377
|
path: string,
|
|
314
|
-
delta: Delta
|
|
378
|
+
delta: Delta<T>,
|
|
315
379
|
options: InternalOptions<T>,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// Get the absolute path of all sub-dependencies (some of them could have been
|
|
324
|
-
// moved but maintain the same relative path).
|
|
325
|
-
const currentDependencies = this._resolveDependencies(
|
|
326
|
-
path,
|
|
327
|
-
result.dependencies,
|
|
328
|
-
options,
|
|
329
|
-
);
|
|
380
|
+
commitOptions: $ReadOnly<{
|
|
381
|
+
onlyRemove: boolean,
|
|
382
|
+
}> = {onlyRemove: false},
|
|
383
|
+
): Module<T> {
|
|
384
|
+
if (delta.errors.has(path)) {
|
|
385
|
+
throw delta.errors.get(path);
|
|
386
|
+
}
|
|
330
387
|
|
|
331
388
|
const previousModule = this.dependencies.get(path);
|
|
389
|
+
const currentModule: ModuleData<T> = nullthrows(
|
|
390
|
+
delta.updatedModuleData.get(path) ?? delta.baseModuleData.get(path),
|
|
391
|
+
);
|
|
332
392
|
|
|
333
393
|
const previousDependencies = previousModule?.dependencies ?? new Map();
|
|
394
|
+
const {
|
|
395
|
+
dependencies: currentDependencies,
|
|
396
|
+
resolvedContexts,
|
|
397
|
+
...transformResult
|
|
398
|
+
} = currentModule;
|
|
334
399
|
|
|
335
400
|
const nextModule = {
|
|
336
401
|
...(previousModule ?? {
|
|
337
|
-
inverseDependencies:
|
|
338
|
-
delta.earlyInverseDependencies.get(path) ?? new CountingSet(),
|
|
402
|
+
inverseDependencies: new CountingSet(),
|
|
339
403
|
path,
|
|
340
404
|
}),
|
|
405
|
+
...transformResult,
|
|
341
406
|
dependencies: new Map(previousDependencies),
|
|
342
|
-
getSource: result.getSource,
|
|
343
|
-
output: result.output,
|
|
344
|
-
unstable_transformResultKey: result.unstable_transformResultKey,
|
|
345
407
|
};
|
|
346
408
|
|
|
347
409
|
// Update the module information.
|
|
348
410
|
this.dependencies.set(nextModule.path, nextModule);
|
|
349
411
|
|
|
350
|
-
|
|
412
|
+
if (previousModule == null) {
|
|
413
|
+
// If the module is not currently in the graph, it is either new or was
|
|
414
|
+
// released earlier in the commit.
|
|
415
|
+
if (delta.deleted.has(path)) {
|
|
416
|
+
// Mark the addition by clearing a prior deletion.
|
|
417
|
+
delta.deleted.delete(path);
|
|
418
|
+
} else {
|
|
419
|
+
// Mark the addition in the added set.
|
|
420
|
+
delta.added.add(path);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Diff dependencies (1/3): remove dependencies that have changed or been removed.
|
|
351
425
|
let dependenciesRemoved = false;
|
|
352
426
|
for (const [key, prevDependency] of previousDependencies) {
|
|
353
427
|
const curDependency = currentDependencies.get(key);
|
|
@@ -360,41 +434,62 @@ export class Graph<T = MixedOutput> {
|
|
|
360
434
|
}
|
|
361
435
|
}
|
|
362
436
|
|
|
363
|
-
// Diff dependencies (2/
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
437
|
+
// Diff dependencies (2/3): add dependencies that have changed or been added.
|
|
438
|
+
let dependenciesAdded = false;
|
|
439
|
+
if (!commitOptions.onlyRemove) {
|
|
440
|
+
for (const [key, curDependency] of currentDependencies) {
|
|
441
|
+
const prevDependency = previousDependencies.get(key);
|
|
442
|
+
if (
|
|
443
|
+
!prevDependency ||
|
|
444
|
+
!dependenciesEqual(prevDependency, curDependency, options)
|
|
445
|
+
) {
|
|
446
|
+
dependenciesAdded = true;
|
|
447
|
+
this._addDependency(
|
|
448
|
+
nextModule,
|
|
449
|
+
key,
|
|
450
|
+
curDependency,
|
|
451
|
+
resolvedContexts.get(key),
|
|
452
|
+
delta,
|
|
453
|
+
options,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
374
456
|
}
|
|
375
457
|
}
|
|
376
458
|
|
|
459
|
+
// Diff dependencies (3/3): detect changes in the ordering of dependency
|
|
460
|
+
// keys, which must be committed even if no other changes were made.
|
|
461
|
+
const previousDependencyKeys = [...previousDependencies.keys()];
|
|
462
|
+
const dependencyKeysChangedOrReordered =
|
|
463
|
+
currentDependencies.size !== previousDependencies.size ||
|
|
464
|
+
[...currentDependencies.keys()].some(
|
|
465
|
+
(currentKey, index) => currentKey !== previousDependencyKeys[index],
|
|
466
|
+
);
|
|
467
|
+
|
|
377
468
|
if (
|
|
378
|
-
previousModule &&
|
|
379
|
-
!
|
|
469
|
+
previousModule != null &&
|
|
470
|
+
!transformOutputMayDiffer(previousModule, nextModule) &&
|
|
380
471
|
!dependenciesRemoved &&
|
|
381
|
-
|
|
472
|
+
!dependenciesAdded &&
|
|
473
|
+
!dependencyKeysChangedOrReordered
|
|
382
474
|
) {
|
|
383
475
|
// We have not operated on nextModule, so restore previousModule
|
|
384
|
-
// to aid diffing.
|
|
476
|
+
// to aid diffing. Don't add this path to delta.touched.
|
|
385
477
|
this.dependencies.set(previousModule.path, previousModule);
|
|
386
478
|
return previousModule;
|
|
387
479
|
}
|
|
388
480
|
|
|
389
|
-
delta.
|
|
481
|
+
delta.touched.add(path);
|
|
390
482
|
|
|
391
|
-
|
|
483
|
+
// Replace dependencies with the correctly-ordered version, matching the
|
|
484
|
+
// transform output. Because this assignment does not add or remove edges,
|
|
485
|
+
// it does NOT invalidate any of the garbage collection state.
|
|
392
486
|
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
487
|
+
// A subtractive pass only partially commits modules, so our dependencies
|
|
488
|
+
// are not generally complete yet. We'll address ordering in the next pass
|
|
489
|
+
// after processing additions.
|
|
490
|
+
if (commitOptions.onlyRemove) {
|
|
491
|
+
return nextModule;
|
|
492
|
+
}
|
|
398
493
|
|
|
399
494
|
// Catch obvious errors with a cheap assertion.
|
|
400
495
|
invariant(
|
|
@@ -402,18 +497,19 @@ export class Graph<T = MixedOutput> {
|
|
|
402
497
|
'Failed to add the correct dependencies',
|
|
403
498
|
);
|
|
404
499
|
|
|
405
|
-
nextModule.dependencies = currentDependencies;
|
|
500
|
+
nextModule.dependencies = new Map(currentDependencies);
|
|
406
501
|
|
|
407
502
|
return nextModule;
|
|
408
503
|
}
|
|
409
504
|
|
|
410
|
-
|
|
505
|
+
_addDependency(
|
|
411
506
|
parentModule: Module<T>,
|
|
412
507
|
key: string,
|
|
413
508
|
dependency: Dependency,
|
|
414
|
-
|
|
509
|
+
requireContext: ?RequireContext,
|
|
510
|
+
delta: Delta<T>,
|
|
415
511
|
options: InternalOptions<T>,
|
|
416
|
-
):
|
|
512
|
+
): void {
|
|
417
513
|
const path = dependency.absolutePath;
|
|
418
514
|
|
|
419
515
|
// The module may already exist, in which case we just need to update some
|
|
@@ -431,38 +527,38 @@ export class Graph<T = MixedOutput> {
|
|
|
431
527
|
this._incrementImportBundleReference(dependency, parentModule);
|
|
432
528
|
} else {
|
|
433
529
|
if (!module) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
delta.added.add(path);
|
|
530
|
+
try {
|
|
531
|
+
module = this._recursivelyCommitModule(path, delta, options);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
// If we couldn't add this module but it was added to the graph
|
|
534
|
+
// before failing on a sub-dependency, it may be orphaned. Mark it as
|
|
535
|
+
// a possible garbage root.
|
|
536
|
+
const module = this.dependencies.get(path);
|
|
537
|
+
if (module) {
|
|
538
|
+
if (module.inverseDependencies.size > 0) {
|
|
539
|
+
this._markAsPossibleCycleRoot(module);
|
|
540
|
+
} else {
|
|
541
|
+
this._releaseModule(module, delta, options);
|
|
542
|
+
}
|
|
448
543
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
options.onDependencyAdd();
|
|
452
|
-
module = await this._processModule(path, delta, options);
|
|
453
|
-
options.onDependencyAdded();
|
|
454
|
-
|
|
455
|
-
this.dependencies.set(module.path, module);
|
|
544
|
+
throw error;
|
|
456
545
|
}
|
|
457
546
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
547
|
+
|
|
548
|
+
// We either added a new node to the graph, or we're updating an existing one.
|
|
549
|
+
module.inverseDependencies.add(parentModule.path);
|
|
550
|
+
this._markModuleInUse(module);
|
|
463
551
|
}
|
|
464
552
|
|
|
465
|
-
|
|
553
|
+
if (requireContext) {
|
|
554
|
+
this.#resolvedContexts.set(path, requireContext);
|
|
555
|
+
} else {
|
|
556
|
+
// This dependency may have existed previously as a require.context -
|
|
557
|
+
// clean it up.
|
|
558
|
+
this.#resolvedContexts.delete(path);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Update the parent's dependency map unless we failed to add a dependency.
|
|
466
562
|
// This means the parent's dependencies can get desynced from
|
|
467
563
|
// inverseDependencies and the other fields in the case of lazy edges.
|
|
468
564
|
// Not an optimal representation :(
|
|
@@ -473,7 +569,7 @@ export class Graph<T = MixedOutput> {
|
|
|
473
569
|
parentModule: Module<T>,
|
|
474
570
|
key: string,
|
|
475
571
|
dependency: Dependency,
|
|
476
|
-
delta: Delta
|
|
572
|
+
delta: Delta<T>,
|
|
477
573
|
options: InternalOptions<T>,
|
|
478
574
|
): void {
|
|
479
575
|
parentModule.dependencies.delete(key);
|
|
@@ -538,84 +634,6 @@ export class Graph<T = MixedOutput> {
|
|
|
538
634
|
yield* this.#importBundleNodes.get(filePath)?.inverseDependencies ?? [];
|
|
539
635
|
}
|
|
540
636
|
|
|
541
|
-
_resolveDependencies(
|
|
542
|
-
parentPath: string,
|
|
543
|
-
dependencies: $ReadOnlyArray<TransformResultDependency>,
|
|
544
|
-
options: InternalOptions<T>,
|
|
545
|
-
): Map<string, Dependency> {
|
|
546
|
-
const maybeResolvedDeps = new Map<
|
|
547
|
-
string,
|
|
548
|
-
void | {absolutePath: string, data: TransformResultDependency},
|
|
549
|
-
>();
|
|
550
|
-
for (const dep of dependencies) {
|
|
551
|
-
let resolvedDep;
|
|
552
|
-
|
|
553
|
-
// `require.context`
|
|
554
|
-
const {contextParams} = dep.data;
|
|
555
|
-
if (contextParams) {
|
|
556
|
-
// Ensure the filepath has uniqueness applied to ensure multiple `require.context`
|
|
557
|
-
// statements can be used to target the same file with different properties.
|
|
558
|
-
const from = path.join(parentPath, '..', dep.name);
|
|
559
|
-
const absolutePath = deriveAbsolutePathFromContext(from, contextParams);
|
|
560
|
-
|
|
561
|
-
const resolvedContext: RequireContext = {
|
|
562
|
-
from,
|
|
563
|
-
mode: contextParams.mode,
|
|
564
|
-
recursive: contextParams.recursive,
|
|
565
|
-
filter: new RegExp(
|
|
566
|
-
contextParams.filter.pattern,
|
|
567
|
-
contextParams.filter.flags,
|
|
568
|
-
),
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
this.#resolvedContexts.set(absolutePath, resolvedContext);
|
|
572
|
-
|
|
573
|
-
resolvedDep = {
|
|
574
|
-
absolutePath,
|
|
575
|
-
data: dep,
|
|
576
|
-
};
|
|
577
|
-
} else {
|
|
578
|
-
try {
|
|
579
|
-
resolvedDep = {
|
|
580
|
-
absolutePath: options.resolve(parentPath, dep).filePath,
|
|
581
|
-
data: dep,
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
// This dependency may have existed previously as a require.context -
|
|
585
|
-
// clean it up.
|
|
586
|
-
this.#resolvedContexts.delete(resolvedDep.absolutePath);
|
|
587
|
-
} catch (error) {
|
|
588
|
-
// Ignore unavailable optional dependencies. They are guarded
|
|
589
|
-
// with a try-catch block and will be handled during runtime.
|
|
590
|
-
if (dep.data.isOptional !== true) {
|
|
591
|
-
throw error;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
const key = dep.data.key;
|
|
597
|
-
if (maybeResolvedDeps.has(key)) {
|
|
598
|
-
throw new Error(
|
|
599
|
-
`resolveDependencies: Found duplicate dependency key '${key}' in ${parentPath}`,
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
maybeResolvedDeps.set(key, resolvedDep);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
const resolvedDeps = new Map<string, Dependency>();
|
|
606
|
-
// Return just the dependencies we successfully resolved.
|
|
607
|
-
// FIXME: This has a bad bug affecting all dependencies *after* an unresolved
|
|
608
|
-
// optional dependency. We'll need to propagate the nulls all the way to the
|
|
609
|
-
// serializer and the require() runtime to keep the dependency map from being
|
|
610
|
-
// desynced from the contents of the module.
|
|
611
|
-
for (const [key, resolvedDep] of maybeResolvedDeps) {
|
|
612
|
-
if (resolvedDep) {
|
|
613
|
-
resolvedDeps.set(key, resolvedDep);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
return resolvedDeps;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
637
|
/**
|
|
620
638
|
* Re-traverse the dependency graph in DFS order to reorder the modules and
|
|
621
639
|
* guarantee the same order between runs. This method mutates the passed graph.
|
|
@@ -716,18 +734,54 @@ export class Graph<T = MixedOutput> {
|
|
|
716
734
|
options: InternalOptions<T>,
|
|
717
735
|
): Iterator<Module<T>> {
|
|
718
736
|
for (const dependency of module.dependencies.values()) {
|
|
719
|
-
|
|
720
|
-
if (asyncType === 'weak' || (options.lazy && asyncType != null)) {
|
|
737
|
+
if (isWeakOrLazy(dependency, options)) {
|
|
721
738
|
continue;
|
|
722
739
|
}
|
|
723
740
|
yield nullthrows(this.dependencies.get(dependency.absolutePath));
|
|
724
741
|
}
|
|
725
742
|
}
|
|
726
743
|
|
|
744
|
+
_moduleSnapshot(module: Module<T>): ModuleData<T> {
|
|
745
|
+
const {dependencies, getSource, output, unstable_transformResultKey} =
|
|
746
|
+
module;
|
|
747
|
+
|
|
748
|
+
const resolvedContexts: Map<string, RequireContext> = new Map();
|
|
749
|
+
for (const [key, dependency] of dependencies) {
|
|
750
|
+
const resolvedContext = this.#resolvedContexts.get(
|
|
751
|
+
dependency.absolutePath,
|
|
752
|
+
);
|
|
753
|
+
if (resolvedContext != null) {
|
|
754
|
+
resolvedContexts.set(key, resolvedContext);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return {
|
|
758
|
+
dependencies: new Map(dependencies),
|
|
759
|
+
resolvedContexts,
|
|
760
|
+
getSource,
|
|
761
|
+
output,
|
|
762
|
+
unstable_transformResultKey,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
|
|
727
766
|
// Delete an unreachable module (and its outbound edges) from the graph
|
|
728
767
|
// immediately.
|
|
729
768
|
// Called when the reference count of a module has reached 0.
|
|
730
|
-
_releaseModule(
|
|
769
|
+
_releaseModule(
|
|
770
|
+
module: Module<T>,
|
|
771
|
+
delta: Delta<T>,
|
|
772
|
+
options: InternalOptions<T>,
|
|
773
|
+
) {
|
|
774
|
+
if (
|
|
775
|
+
!delta.updatedModuleData.has(module.path) &&
|
|
776
|
+
!delta.baseModuleData.has(module.path)
|
|
777
|
+
) {
|
|
778
|
+
// Before releasing a module, take a snapshot of the data we might need
|
|
779
|
+
// to reintroduce it to the graph later in this commit. As it is not
|
|
780
|
+
// already present in updatedModuleData we can infer it has not been modified,
|
|
781
|
+
// so the transform output and dependencies we copy here are current.
|
|
782
|
+
delta.baseModuleData.set(module.path, this._moduleSnapshot(module));
|
|
783
|
+
}
|
|
784
|
+
|
|
731
785
|
for (const [key, dependency] of module.dependencies) {
|
|
732
786
|
this._removeDependency(module, key, dependency, delta, options);
|
|
733
787
|
}
|
|
@@ -736,7 +790,7 @@ export class Graph<T = MixedOutput> {
|
|
|
736
790
|
}
|
|
737
791
|
|
|
738
792
|
// Delete an unreachable module from the graph.
|
|
739
|
-
_freeModule(module: Module<T>, delta: Delta) {
|
|
793
|
+
_freeModule(module: Module<T>, delta: Delta<T>) {
|
|
740
794
|
if (delta.added.has(module.path)) {
|
|
741
795
|
// Mark the deletion by clearing a prior addition.
|
|
742
796
|
delta.added.delete(module.path);
|
|
@@ -749,7 +803,6 @@ export class Graph<T = MixedOutput> {
|
|
|
749
803
|
// Clean up all the state associated with this module in order to correctly
|
|
750
804
|
// re-add it if we encounter it again.
|
|
751
805
|
this.dependencies.delete(module.path);
|
|
752
|
-
delta.earlyInverseDependencies.delete(module.path);
|
|
753
806
|
this.#gc.possibleCycleRoots.delete(module.path);
|
|
754
807
|
this.#gc.color.delete(module.path);
|
|
755
808
|
this.#resolvedContexts.delete(module.path);
|
|
@@ -757,14 +810,14 @@ export class Graph<T = MixedOutput> {
|
|
|
757
810
|
|
|
758
811
|
// Mark a module as a possible cycle root
|
|
759
812
|
_markAsPossibleCycleRoot(module: Module<T>) {
|
|
760
|
-
if (
|
|
813
|
+
if (this.#gc.color.get(module.path) !== 'purple') {
|
|
761
814
|
this.#gc.color.set(module.path, 'purple');
|
|
762
815
|
this.#gc.possibleCycleRoots.add(module.path);
|
|
763
816
|
}
|
|
764
817
|
}
|
|
765
818
|
|
|
766
819
|
// Collect any unreachable cycles in the graph.
|
|
767
|
-
_collectCycles(delta: Delta
|
|
820
|
+
_collectCycles(delta: Delta<T>, options: InternalOptions<T>) {
|
|
768
821
|
// Mark recursively from roots (trial deletion)
|
|
769
822
|
for (const path of this.#gc.possibleCycleRoots) {
|
|
770
823
|
const module = nullthrows(this.dependencies.get(path));
|
|
@@ -836,7 +889,7 @@ export class Graph<T = MixedOutput> {
|
|
|
836
889
|
}
|
|
837
890
|
}
|
|
838
891
|
|
|
839
|
-
_collectWhite(module: Module<T>, delta: Delta) {
|
|
892
|
+
_collectWhite(module: Module<T>, delta: Delta<T>) {
|
|
840
893
|
const color = nullthrows(this.#gc.color.get(module.path));
|
|
841
894
|
if (color === 'white' && !this.#gc.possibleCycleRoots.has(module.path)) {
|
|
842
895
|
this.#gc.color.set(module.path, 'black');
|
|
@@ -867,23 +920,6 @@ function dependenciesEqual(
|
|
|
867
920
|
);
|
|
868
921
|
}
|
|
869
922
|
|
|
870
|
-
function allDependenciesEqual<T>(
|
|
871
|
-
a: Module<T>,
|
|
872
|
-
b: Module<T>,
|
|
873
|
-
options: $ReadOnly<{lazy: boolean, ...}>,
|
|
874
|
-
): boolean {
|
|
875
|
-
if (a.dependencies.size !== b.dependencies.size) {
|
|
876
|
-
return false;
|
|
877
|
-
}
|
|
878
|
-
for (const [key, depA] of a.dependencies) {
|
|
879
|
-
const depB = b.dependencies.get(key);
|
|
880
|
-
if (!depB || !dependenciesEqual(depA, depB, options)) {
|
|
881
|
-
return false;
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
return true;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
923
|
function contextParamsEqual(
|
|
888
924
|
a: ?RequireContextParams,
|
|
889
925
|
b: ?RequireContextParams,
|
|
@@ -900,7 +936,7 @@ function contextParamsEqual(
|
|
|
900
936
|
);
|
|
901
937
|
}
|
|
902
938
|
|
|
903
|
-
function
|
|
939
|
+
function transformOutputMayDiffer<T>(a: Module<T>, b: Module<T>): boolean {
|
|
904
940
|
return (
|
|
905
941
|
a.unstable_transformResultKey == null ||
|
|
906
942
|
a.unstable_transformResultKey !== b.unstable_transformResultKey
|