|
26 | 26 | * 2. We keep the "root buffer" (possibleCycleRoots) free of duplicates by
|
27 | 27 | * making it a Set, instead of storing a "buffered" flag on each node.
|
28 | 28 | * 3. On top of tracking edges between nodes, we also count references between
|
29 |
| - * nodes and entries in the importBundleNames set. |
| 29 | + * nodes and entries in the importBundleNodes set. |
30 | 30 | */
|
31 | 31 |
|
32 | 32 | import type {RequireContextParams} from '../ModuleGraph/worker/collectDependencies';
|
@@ -123,19 +123,25 @@ export class Graph<T = MixedOutput> {
|
123 | 123 | +entryPoints: $ReadOnlySet<string>;
|
124 | 124 | +transformOptions: TransformInputOptions;
|
125 | 125 | +dependencies: Dependencies<T> = new Map();
|
126 |
| - +importBundleNames: Set<string> = new Set(); |
| 126 | + +#importBundleNodes: Map< |
| 127 | + string, |
| 128 | + $ReadOnly<{ |
| 129 | + inverseDependencies: CountingSet<string>, |
| 130 | + }>, |
| 131 | + > = new Map(); |
| 132 | + |
| 133 | + // $FlowIgnore[unsafe-getters-setters] |
| 134 | + get importBundleNames(): $ReadOnlySet<string> { |
| 135 | + return new Set(this.#importBundleNodes.keys()); |
| 136 | + } |
127 | 137 |
|
128 | 138 | /// GC state for nodes in the graph (this.dependencies)
|
129 |
| - #gc: { |
| 139 | + +#gc: { |
130 | 140 | +color: Map<string, NodeColor>,
|
131 | 141 | +possibleCycleRoots: Set<string>,
|
132 |
| - |
133 |
| - // Reference counts for entries in importBundleNames |
134 |
| - +importBundleRefs: Map<string, number>, |
135 | 142 | } = {
|
136 | 143 | color: new Map(),
|
137 | 144 | possibleCycleRoots: new Set(),
|
138 |
| - importBundleRefs: new Map(), |
139 | 145 | };
|
140 | 146 |
|
141 | 147 | /** Resolved context parameters from `require.context`. */
|
@@ -219,14 +225,10 @@ export class Graph<T = MixedOutput> {
|
219 | 225 | this.dependencies.size === 0,
|
220 | 226 | 'initialTraverseDependencies called on nonempty graph',
|
221 | 227 | );
|
222 |
| - invariant( |
223 |
| - this.importBundleNames.size === 0, |
224 |
| - 'initialTraverseDependencies called on nonempty graph', |
225 |
| - ); |
226 | 228 |
|
227 | 229 | this.#gc.color.clear();
|
228 | 230 | this.#gc.possibleCycleRoots.clear();
|
229 |
| - this.#gc.importBundleRefs.clear(); |
| 231 | + this.#importBundleNodes.clear(); |
230 | 232 |
|
231 | 233 | for (const path of this.entryPoints) {
|
232 | 234 | // Each entry point implicitly has a refcount of 1, so mark them all black.
|
@@ -361,8 +363,8 @@ export class Graph<T = MixedOutput> {
|
361 | 363 | ) {
|
362 | 364 | // Don't add a node for the module if we are traversing async dependencies
|
363 | 365 | // lazily (and this is an async dependency). Instead, record it in
|
364 |
| - // importBundleNames. |
365 |
| - this._incrementImportBundleReference(dependency); |
| 366 | + // importBundleNodes. |
| 367 | + this._incrementImportBundleReference(dependency, parentModule); |
366 | 368 | } else {
|
367 | 369 | if (!module) {
|
368 | 370 | // Add a new node to the graph.
|
@@ -419,7 +421,7 @@ export class Graph<T = MixedOutput> {
|
419 | 421 | options.experimentalImportBundleSupport &&
|
420 | 422 | dependency.data.data.asyncType != null
|
421 | 423 | ) {
|
422 |
| - this._decrementImportBundleReference(dependency); |
| 424 | + this._decrementImportBundleReference(dependency, parentModule); |
423 | 425 | }
|
424 | 426 |
|
425 | 427 | const module = this.dependencies.get(absolutePath);
|
@@ -455,6 +457,16 @@ export class Graph<T = MixedOutput> {
|
455 | 457 | }
|
456 | 458 | }
|
457 | 459 |
|
| 460 | + /** |
| 461 | + * Gets the list of modules affected by the deletion of a given file. The |
| 462 | + * caller is expected to mark these modules as modified in the next call to |
| 463 | + * traverseDependencies. Note that the list may contain duplicates. |
| 464 | + */ |
| 465 | + *getModifiedModulesForDeletedPath(filePath: string): Iterable<string> { |
| 466 | + yield* this.dependencies.get(filePath)?.inverseDependencies ?? []; |
| 467 | + yield* this.#importBundleNodes.get(filePath)?.inverseDependencies ?? []; |
| 468 | + } |
| 469 | + |
458 | 470 | _resolveDependencies(
|
459 | 471 | parentPath: string,
|
460 | 472 | dependencies: $ReadOnlyArray<TransformResultDependency>,
|
@@ -588,32 +600,36 @@ export class Graph<T = MixedOutput> {
|
588 | 600 |
|
589 | 601 | /** Garbage collection functions */
|
590 | 602 |
|
591 |
| - // Add an entry to importBundleNames (or increase the reference count of an existing one) |
592 |
| - _incrementImportBundleReference(dependency: Dependency) { |
| 603 | + // Add an entry to importBundleNodes (or record an inverse dependency of an existing one) |
| 604 | + _incrementImportBundleReference( |
| 605 | + dependency: Dependency, |
| 606 | + parentModule: Module<T>, |
| 607 | + ) { |
593 | 608 | const {absolutePath} = dependency;
|
594 |
| - |
595 |
| - this.#gc.importBundleRefs.set( |
596 |
| - absolutePath, |
597 |
| - (this.#gc.importBundleRefs.get(absolutePath) ?? 0) + 1, |
598 |
| - ); |
599 |
| - this.importBundleNames.add(absolutePath); |
| 609 | + const importBundleNode = this.#importBundleNodes.get(absolutePath) ?? { |
| 610 | + inverseDependencies: new CountingSet(), |
| 611 | + }; |
| 612 | + importBundleNode.inverseDependencies.add(parentModule.path); |
| 613 | + this.#importBundleNodes.set(absolutePath, importBundleNode); |
600 | 614 | }
|
601 | 615 |
|
602 |
| - // Decrease the reference count of an entry in importBundleNames (and delete it if necessary) |
603 |
| - _decrementImportBundleReference(dependency: Dependency) { |
| 616 | + // Decrease the reference count of an entry in importBundleNodes (and delete it if necessary) |
| 617 | + _decrementImportBundleReference( |
| 618 | + dependency: Dependency, |
| 619 | + parentModule: Module<T>, |
| 620 | + ) { |
604 | 621 | const {absolutePath} = dependency;
|
605 | 622 |
|
606 |
| - const prevRefCount = nullthrows( |
607 |
| - this.#gc.importBundleRefs.get(absolutePath), |
| 623 | + const importBundleNode = nullthrows( |
| 624 | + this.#importBundleNodes.get(absolutePath), |
608 | 625 | );
|
609 | 626 | invariant(
|
610 |
| - prevRefCount > 0, |
611 |
| - 'experimentalImportBundleSupport: import bundle refcount not valid', |
| 627 | + importBundleNode.inverseDependencies.has(parentModule.path), |
| 628 | + 'experimentalImportBundleSupport: import bundle inverse references', |
612 | 629 | );
|
613 |
| - this.#gc.importBundleRefs.set(absolutePath, prevRefCount - 1); |
614 |
| - if (prevRefCount === 1) { |
615 |
| - this.#gc.importBundleRefs.delete(absolutePath); |
616 |
| - this.importBundleNames.delete(absolutePath); |
| 630 | + importBundleNode.inverseDependencies.delete(parentModule.path); |
| 631 | + if (importBundleNode.inverseDependencies.size === 0) { |
| 632 | + this.#importBundleNodes.delete(absolutePath); |
617 | 633 | }
|
618 | 634 | }
|
619 | 635 |
|
|
0 commit comments