You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix graph delta bugs when a dependency is added+modified+removed / removed+modified+added within a traversal
Summary:
I came across this edge case while working on module diffing.
Typically:
1. DeltaCalculator aggregates relevant file changes between requests for a delta (e.g., via HMR). (If no client is connected, these changes may accrue over a period of time)
2. When `getDelta` is called, this list of paths is provided to `Graph.traverseDependencies`, which determines the actual graph delta, performing any necessary transformation and dependency traversal.
3. `traverseDependencies` works through these paths sequentially, marking modules as modified and deeply adding/removing dependencies as it goes. It ignores any path that doesn't correspond to a module in the graph.
Previously, a path was added to `delta.modified` if only if it was given in `paths` *and* existed in the graph at the time we came to traverse it as part of the `traverseDependencies` sequence.
## Error cases
This led to issues in a couple of cases:
## Module modified while temporarily NOT part of the graph
For an existing graph with edges `A->B` and `A->C`.
1. The dependency `A->C` is removed, freeing `C` from the graph and adding it to `delta.deleted`. `A` is marked modified.
2. `C` is modified, but traversal skips it because it's not in the graph.
3. The dependency `B->C` is added. `C` is removed from `delta.deleted` and processed. `B` is marked modified.
Result: `{ added: [], modified: [A, B], deleted: [] }` - `C`'s modification is missed.
### Module modified while temporarily part of the graph
For an existing graph with edges `A->B`.
1. The dependency `B->C` is added, processing `C` as a new dependency and adding it to `delta.added`
2. `C` is modified, traversal marks it as modified because it's now part of the graph.
3. The dependency `A->B` is removed. `C` is removed from `delta.added` but remains in `delta.modified`. `A` is marked modified, `B` marked deleted.
Result: `C`'s path remains in `delta.modified` but not `added` or `deleted`, but it's not present in the graph at the end of the traversal. `traverseDependencies` throws, client sees a redbox from `nullthrows` on:
```
modified.set(pathOfC, nullthrows(this.dependencies.get(pathOfC)))
```
## This diff
- Mark modules as potentially modified when they are **processed**, *either directly or transitively*.
- Filter modifications to only those from the given `paths` that were present in the graph *before traversal started*. [Building this list feeds nicely into subsequent work on diffing modules.]
Changelog:
```
**[Fix]**: Fix crash on a module added+modified+removed between updates.
**[Fix]**: Fix missed modification on module removed+modified+added between updates.
```
Reviewed By: motiz88
Differential Revision: D45691691
fbshipit-source-id: 4f246582311063650f26678006b6089c778014f4
0 commit comments