mirror of https://github.com/status-im/metro.git
Reduce memory in the traverse dependencies logic by reusing objects
Reviewed By: BYK Differential Revision: D7110823 fbshipit-source-id: 575da43e99bc2ba82b6e7bdcf61e933e95535710
This commit is contained in:
parent
5bec7748e4
commit
cd13bb80eb
|
@ -319,7 +319,7 @@ describe('edge cases', () => {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
getPaths(
|
getPaths(
|
||||||
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
await traverseDependencies([...files], dependencyGraph, {}, edges),
|
||||||
),
|
),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
added: new Set(['/foo', '/baz-moved']),
|
added: new Set(['/foo', '/baz-moved']),
|
||||||
|
@ -335,7 +335,7 @@ describe('edge cases', () => {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
getPaths(
|
getPaths(
|
||||||
await traverseDependencies(['/foo'], dependencyGraph, {}, edges),
|
await traverseDependencies([...files], dependencyGraph, {}, edges),
|
||||||
),
|
),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
added: new Set(['/foo', '/qux']),
|
added: new Set(['/foo', '/qux']),
|
||||||
|
|
|
@ -41,7 +41,7 @@ type Result = {added: Map<string, DependencyEdge>, deleted: Set<string>};
|
||||||
* (a file should not be deleted if it has been added, but it should if it
|
* (a file should not be deleted if it has been added, but it should if it
|
||||||
* just has been modified).
|
* just has been modified).
|
||||||
**/
|
**/
|
||||||
type ResultWithModifiedFiles = {
|
type Delta = {
|
||||||
added: Map<string, DependencyEdge>,
|
added: Map<string, DependencyEdge>,
|
||||||
modified: Map<string, DependencyEdge>,
|
modified: Map<string, DependencyEdge>,
|
||||||
deleted: Set<string>,
|
deleted: Set<string>,
|
||||||
|
@ -68,7 +68,11 @@ async function traverseDependencies(
|
||||||
edges: DependencyEdges,
|
edges: DependencyEdges,
|
||||||
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||||
): Promise<Result> {
|
): Promise<Result> {
|
||||||
const changes = [];
|
const delta = {
|
||||||
|
added: new Map(),
|
||||||
|
modified: new Map(),
|
||||||
|
deleted: new Set(),
|
||||||
|
};
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
paths.map(async path => {
|
paths.map(async path => {
|
||||||
|
@ -78,14 +82,15 @@ async function traverseDependencies(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.push(
|
delta.modified.set(edge.path, edge);
|
||||||
await traverseDependenciesForSingleFile(
|
|
||||||
edge,
|
await traverseDependenciesForSingleFile(
|
||||||
dependencyGraph,
|
edge,
|
||||||
transformOptions,
|
dependencyGraph,
|
||||||
edges,
|
transformOptions,
|
||||||
onProgress,
|
edges,
|
||||||
),
|
delta,
|
||||||
|
onProgress,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -94,29 +99,26 @@ async function traverseDependencies(
|
||||||
const deleted = new Set();
|
const deleted = new Set();
|
||||||
const modified = new Map();
|
const modified = new Map();
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const [path, edge] of delta.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, edge] of delta.modified) {
|
||||||
for (const path of change.deleted) {
|
added.set(path, edge);
|
||||||
// If a dependency has been marked as added, it should never be included
|
modified.set(path, edge);
|
||||||
// in as added.
|
}
|
||||||
// At the same time, if a dependency has been marked both as added and
|
|
||||||
// deleted, it means that this is a renamed file (or that dependency
|
|
||||||
// has been removed from one path but added back in a different path).
|
|
||||||
// In this case the addition and deletion "get cancelled".
|
|
||||||
const markedAsAdded = added.delete(path);
|
|
||||||
|
|
||||||
if (!markedAsAdded || modified.has(path)) {
|
for (const path of delta.deleted) {
|
||||||
deleted.add(path);
|
// If a dependency has been marked as deleted, it should never be included
|
||||||
}
|
// in the added group.
|
||||||
|
// At the same time, if a dependency has been marked both as added and
|
||||||
|
// deleted, it means that this is a renamed file (or that dependency
|
||||||
|
// has been removed from one path but added back in a different path).
|
||||||
|
// In this case the addition and deletion "get cancelled".
|
||||||
|
const markedAsAdded = added.delete(path);
|
||||||
|
|
||||||
|
if (!markedAsAdded || modified.has(path)) {
|
||||||
|
deleted.add(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,13 +137,25 @@ async function initialTraverseDependencies(
|
||||||
): Promise<Result> {
|
): Promise<Result> {
|
||||||
const edge = createEdge(dependencyGraph.getModuleForPath(path), edges);
|
const edge = createEdge(dependencyGraph.getModuleForPath(path), edges);
|
||||||
|
|
||||||
return await traverseDependenciesForSingleFile(
|
const delta = {
|
||||||
|
added: new Map([[edge.path, edge]]),
|
||||||
|
modified: new Map(),
|
||||||
|
deleted: new Set(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await traverseDependenciesForSingleFile(
|
||||||
edge,
|
edge,
|
||||||
dependencyGraph,
|
dependencyGraph,
|
||||||
transformOptions,
|
transformOptions,
|
||||||
edges,
|
edges,
|
||||||
|
delta,
|
||||||
onProgress,
|
onProgress,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
added: reorderDependencies(edge, delta.added),
|
||||||
|
deleted: delta.deleted,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function traverseDependenciesForSingleFile(
|
async function traverseDependenciesForSingleFile(
|
||||||
|
@ -149,17 +163,19 @@ async function traverseDependenciesForSingleFile(
|
||||||
dependencyGraph: DependencyGraph,
|
dependencyGraph: DependencyGraph,
|
||||||
transformOptions: JSTransformerOptions,
|
transformOptions: JSTransformerOptions,
|
||||||
edges: DependencyEdges,
|
edges: DependencyEdges,
|
||||||
|
delta: Delta,
|
||||||
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
onProgress?: (numProcessed: number, total: number) => mixed = () => {},
|
||||||
): Promise<ResultWithModifiedFiles> {
|
): Promise<void> {
|
||||||
let numProcessed = 0;
|
let numProcessed = 0;
|
||||||
let total = 1;
|
let total = 1;
|
||||||
onProgress(numProcessed, total);
|
onProgress(numProcessed, total);
|
||||||
|
|
||||||
const result = await processEdge(
|
await processEdge(
|
||||||
edge,
|
edge,
|
||||||
dependencyGraph,
|
dependencyGraph,
|
||||||
transformOptions,
|
transformOptions,
|
||||||
edges,
|
edges,
|
||||||
|
delta,
|
||||||
() => {
|
() => {
|
||||||
total++;
|
total++;
|
||||||
onProgress(numProcessed, total);
|
onProgress(numProcessed, total);
|
||||||
|
@ -172,14 +188,6 @@ async function traverseDependenciesForSingleFile(
|
||||||
|
|
||||||
numProcessed++;
|
numProcessed++;
|
||||||
onProgress(numProcessed, total);
|
onProgress(numProcessed, total);
|
||||||
|
|
||||||
const modified = new Map([[edge.path, edge]]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
added: reorderDependencies(edge, result.added),
|
|
||||||
deleted: result.deleted,
|
|
||||||
modified,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processEdge(
|
async function processEdge(
|
||||||
|
@ -187,9 +195,10 @@ async function processEdge(
|
||||||
dependencyGraph: DependencyGraph,
|
dependencyGraph: DependencyGraph,
|
||||||
transformOptions: JSTransformerOptions,
|
transformOptions: JSTransformerOptions,
|
||||||
edges: DependencyEdges,
|
edges: DependencyEdges,
|
||||||
|
delta: Delta,
|
||||||
onDependencyAdd: () => mixed,
|
onDependencyAdd: () => mixed,
|
||||||
onDependencyAdded: () => mixed,
|
onDependencyAdded: () => mixed,
|
||||||
): Promise<Result> {
|
): Promise<void> {
|
||||||
const previousDependencies = new Set(edge.dependencies.values());
|
const previousDependencies = new Set(edge.dependencies.values());
|
||||||
|
|
||||||
const result = await dependencyGraph
|
const result = await dependencyGraph
|
||||||
|
@ -217,15 +226,12 @@ async function processEdge(
|
||||||
edge.dependencies.set(relativePath, absolutePath);
|
edge.dependencies.set(relativePath, absolutePath);
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleted = [];
|
|
||||||
for (const absolutePath of previousDependencies.values()) {
|
for (const absolutePath of previousDependencies.values()) {
|
||||||
if (!currentDependencies.has(absolutePath)) {
|
if (!currentDependencies.has(absolutePath)) {
|
||||||
deleted.push(removeDependency(edge, absolutePath, edges));
|
removeDependency(edge, absolutePath, edges, delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const added = new Map([[edge.path, edge]]);
|
|
||||||
|
|
||||||
// Check all the module dependencies and start traversing the tree from each
|
// Check all the module dependencies and start traversing the tree from each
|
||||||
// added and removed dependency, to get all the modules that have to be added
|
// added and removed dependency, to get all the modules that have to be added
|
||||||
// and removed from the dependency graph.
|
// and removed from the dependency graph.
|
||||||
|
@ -235,26 +241,18 @@ async function processEdge(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dependencies = await addDependency(
|
await addDependency(
|
||||||
edge,
|
edge,
|
||||||
absolutePath,
|
absolutePath,
|
||||||
dependencyGraph,
|
dependencyGraph,
|
||||||
transformOptions,
|
transformOptions,
|
||||||
edges,
|
edges,
|
||||||
|
delta,
|
||||||
onDependencyAdd,
|
onDependencyAdd,
|
||||||
onDependencyAdded,
|
onDependencyAdded,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const [path, edge] of dependencies) {
|
|
||||||
added.set(path, edge);
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
|
||||||
added,
|
|
||||||
deleted: flatten(deleted),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addDependency(
|
async function addDependency(
|
||||||
|
@ -263,47 +261,49 @@ async function addDependency(
|
||||||
dependencyGraph: DependencyGraph,
|
dependencyGraph: DependencyGraph,
|
||||||
transformOptions: JSTransformerOptions,
|
transformOptions: JSTransformerOptions,
|
||||||
edges: DependencyEdges,
|
edges: DependencyEdges,
|
||||||
|
delta: Delta,
|
||||||
onDependencyAdd: () => mixed,
|
onDependencyAdd: () => mixed,
|
||||||
onDependencyAdded: () => mixed,
|
onDependencyAdded: () => mixed,
|
||||||
): Promise<Map<string, DependencyEdge>> {
|
): Promise<void> {
|
||||||
const existingEdge = edges.get(path);
|
const existingEdge = edges.get(path);
|
||||||
|
|
||||||
// The new dependency was already in the graph, we don't need to do anything.
|
// The new dependency was already in the graph, we don't need to do anything.
|
||||||
if (existingEdge) {
|
if (existingEdge) {
|
||||||
existingEdge.inverseDependencies.add(parentEdge.path);
|
existingEdge.inverseDependencies.add(parentEdge.path);
|
||||||
|
|
||||||
return new Map();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const edge = createEdge(dependencyGraph.getModuleForPath(path), edges);
|
const edge = createEdge(dependencyGraph.getModuleForPath(path), edges);
|
||||||
|
|
||||||
edge.inverseDependencies.add(parentEdge.path);
|
edge.inverseDependencies.add(parentEdge.path);
|
||||||
|
delta.added.set(edge.path, edge);
|
||||||
|
|
||||||
onDependencyAdd();
|
onDependencyAdd();
|
||||||
|
|
||||||
const {added} = await processEdge(
|
await processEdge(
|
||||||
edge,
|
edge,
|
||||||
dependencyGraph,
|
dependencyGraph,
|
||||||
transformOptions,
|
transformOptions,
|
||||||
edges,
|
edges,
|
||||||
|
delta,
|
||||||
onDependencyAdd,
|
onDependencyAdd,
|
||||||
onDependencyAdded,
|
onDependencyAdded,
|
||||||
);
|
);
|
||||||
|
|
||||||
onDependencyAdded();
|
onDependencyAdded();
|
||||||
|
|
||||||
return added;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeDependency(
|
function removeDependency(
|
||||||
parentEdge: DependencyEdge,
|
parentEdge: DependencyEdge,
|
||||||
absolutePath: string,
|
absolutePath: string,
|
||||||
edges: DependencyEdges,
|
edges: DependencyEdges,
|
||||||
): Set<string> {
|
delta: Delta,
|
||||||
|
): void {
|
||||||
const edge = edges.get(absolutePath);
|
const edge = edges.get(absolutePath);
|
||||||
|
|
||||||
if (!edge) {
|
if (!edge) {
|
||||||
return new Set();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
edge.inverseDependencies.delete(parentEdge.path);
|
edge.inverseDependencies.delete(parentEdge.path);
|
||||||
|
@ -311,26 +311,20 @@ function removeDependency(
|
||||||
// This module is still used by another modules, so we cannot remove it from
|
// This module is still used by another modules, so we cannot remove it from
|
||||||
// the bundle.
|
// the bundle.
|
||||||
if (edge.inverseDependencies.size) {
|
if (edge.inverseDependencies.size) {
|
||||||
return new Set();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const removedDependencies = new Set([edge.path]);
|
delta.deleted.add(edge.path);
|
||||||
|
|
||||||
// Now we need to iterate through the module dependencies in order to
|
// Now we need to iterate through the module dependencies in order to
|
||||||
// clean up everything (we cannot read the module because it may have
|
// clean up everything (we cannot read the module because it may have
|
||||||
// been deleted).
|
// been deleted).
|
||||||
for (const depAbsolutePath of edge.dependencies.values()) {
|
for (const depAbsolutePath of edge.dependencies.values()) {
|
||||||
const removed = removeDependency(edge, depAbsolutePath, edges);
|
removeDependency(edge, depAbsolutePath, edges, delta);
|
||||||
|
|
||||||
for (const removedDependency of removed.values()) {
|
|
||||||
removedDependencies.add(removedDependency);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This module is not used anywhere else!! we can clear it from the bundle
|
// This module is not used anywhere else!! we can clear it from the bundle
|
||||||
destroyEdge(edge, edges);
|
destroyEdge(edge, edges);
|
||||||
|
|
||||||
return removedDependencies;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEdge(module: Module, edges: DependencyEdges): DependencyEdge {
|
function createEdge(module: Module, edges: DependencyEdges): DependencyEdge {
|
||||||
|
@ -416,18 +410,6 @@ function reorderDependencies(
|
||||||
return orderedDependencies;
|
return orderedDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatten<T>(input: Iterable<Iterable<T>>): Set<T> {
|
|
||||||
const output = new Set();
|
|
||||||
|
|
||||||
for (const items of input) {
|
|
||||||
for (const item of items) {
|
|
||||||
output.add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
initialTraverseDependencies,
|
initialTraverseDependencies,
|
||||||
traverseDependencies,
|
traverseDependencies,
|
||||||
|
|
Loading…
Reference in New Issue