Make sure that files that have been modified and deleted afterwards are marked as deleted

Reviewed By: davidaurelio

Differential Revision: D7030986

fbshipit-source-id: e00fc80ff6f5cb7bb49714d35c91361cb795ad0b
This commit is contained in:
Rafael Oleza 2018-02-22 09:14:29 -08:00 committed by Facebook Github Bot
parent 40c2449eec
commit 6da4bc4d30
2 changed files with 67 additions and 28 deletions

View File

@ -225,6 +225,28 @@ describe('edge cases', () => {
}); });
}); });
it('modify a file and delete it afterwards', async () => {
const edges = new Map();
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);
mockedDependencyTree.set(moduleFoo.path, [moduleBar]);
// Modify /foo and /baz, and then remove the dependency from /foo to /baz
expect(
getPaths(
await traverseDependencies(
['/baz', '/foo'],
dependencyGraph,
{},
edges,
),
),
).toEqual({
added: new Set(['/foo']),
deleted: new Set(['/baz']),
});
});
it('move a file to a different folder', async () => { it('move a file to a different folder', async () => {
const edges = new Map(); const edges = new Map();
await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges); await initialTraverseDependencies('/bundle', dependencyGraph, {}, edges);

View File

@ -35,6 +35,18 @@ export type DependencyEdges = Map<string, DependencyEdge>;
type Result = {added: Map<string, DependencyEdge>, deleted: Set<string>}; type Result = {added: Map<string, DependencyEdge>, deleted: Set<string>};
/**
* Internal data structure that the traversal logic uses to know which of the
* files have been modified. This helps us know which files to mark as deleted
* (a file should not be deleted if it has been added, but it should if it
* just has been modified).
**/
type ResultWithModifiedFiles = {
added: Map<string, DependencyEdge>,
modified: Map<string, DependencyEdge>,
deleted: Set<string>,
};
/** /**
* Dependency Traversal logic for the Delta Bundler. This method calculates * Dependency Traversal logic for the Delta Bundler. This method calculates
* the modules that should be included in the bundle by traversing the * the modules that should be included in the bundle by traversing the
@ -80,20 +92,29 @@ async function traverseDependencies(
const added = new Map(); const added = new Map();
const deleted = new Set(); const deleted = new Set();
const modified = new Map();
for (const change of changes) { for (const change of changes) {
for (const [path, edge] of change.added) { for (const [path, edge] of change.added) {
added.set(path, edge); added.set(path, edge);
} }
for (const [path, edge] of change.modified) {
modified.set(path, edge);
}
}
for (const change of changes) {
for (const path of change.deleted) { for (const path of change.deleted) {
// If a path has been marked both as added and deleted, it means that this // If a dependency has been marked as added, it should never be included
// path is a dependency of a renamed file (or that dependency has been // in as added.
// removed from one path but added back in a different path). In this case // At the same time, if a dependency has been marked both as added and
// the addition and deletion "get cancelled". // deleted, it means that this is a renamed file (or that dependency
if (added.has(path)) { // has been removed from one path but added back in a different path).
added.delete(path); // In this case the addition and deletion "get cancelled".
} else { const markedAsAdded = added.delete(path);
if (!markedAsAdded || modified.has(path)) {
deleted.add(path); deleted.add(path);
} }
} }
@ -129,7 +150,7 @@ async function traverseDependenciesForSingleFile(
transformOptions: JSTransformerOptions, transformOptions: JSTransformerOptions,
edges: DependencyEdges, edges: DependencyEdges,
onProgress?: (numProcessed: number, total: number) => mixed = () => {}, onProgress?: (numProcessed: number, total: number) => mixed = () => {},
): Promise<Result> { ): Promise<ResultWithModifiedFiles> {
let numProcessed = 0; let numProcessed = 0;
let total = 1; let total = 1;
onProgress(numProcessed, total); onProgress(numProcessed, total);
@ -152,9 +173,12 @@ async function traverseDependenciesForSingleFile(
numProcessed++; numProcessed++;
onProgress(numProcessed, total); onProgress(numProcessed, total);
const modified = new Map([[edge.path, edge]]);
return { return {
added: reorderDependencies(edge.path, result.added, edges), added: reorderDependencies(edge, result.added),
deleted: result.deleted, deleted: result.deleted,
modified,
}; };
} }
@ -367,21 +391,26 @@ function resolveDependencies(
* guarantee the same order between runs. * guarantee the same order between runs.
*/ */
function reorderDependencies( function reorderDependencies(
path: string, edge: ?DependencyEdge,
dependencies: Map<string, DependencyEdge>, dependencies: Map<string, DependencyEdge>,
edges: DependencyEdges,
orderedDependencies?: Map<string, DependencyEdge> = new Map(), orderedDependencies?: Map<string, DependencyEdge> = new Map(),
): Map<string, DependencyEdge> { ): Map<string, DependencyEdge> {
const edge = edges.get(path); if (
!edge ||
if (!edge || !dependencies.has(path) || orderedDependencies.has(path)) { !dependencies.has(edge.path) ||
orderedDependencies.has(edge.path)
) {
return orderedDependencies; return orderedDependencies;
} }
orderedDependencies.set(path, edge); orderedDependencies.set(edge.path, edge);
edge.dependencies.forEach(path => edge.dependencies.forEach(path =>
reorderDependencies(path, dependencies, edges, orderedDependencies), reorderDependencies(
dependencies.get(path),
dependencies,
orderedDependencies,
),
); );
return orderedDependencies; return orderedDependencies;
@ -399,18 +428,6 @@ function flatten<T>(input: Iterable<Iterable<T>>): Set<T> {
return output; return output;
} }
function flattenMap<K, V>(input: Iterable<Map<K, V>>): Map<K, V> {
const output = new Map();
for (const items of input) {
for (const [key, value] of items.entries()) {
output.set(key, value);
}
}
return output;
}
module.exports = { module.exports = {
initialTraverseDependencies, initialTraverseDependencies,
traverseDependencies, traverseDependencies,