fix handling of circular references in the inner graph

This commit is contained in:
Tobias Koppers 2019-10-02 07:08:32 +02:00
parent c47e1f8071
commit f01e7d253b
5 changed files with 98 additions and 28 deletions

View File

@ -96,41 +96,49 @@ class InnerGraphPlugin {
const innerGraph = const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph); /** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
if (!innerGraph) return; if (!innerGraph) return;
// flatten graph // flatten graph to terminal nodes (string, undefined or true)
const unexpanded = new Set(innerGraph.keys()); const nonTerminal = new Set(innerGraph.keys());
const expand = key => { while (nonTerminal.size > 0) {
if (!unexpanded.has(key)) return; for (const key of nonTerminal) {
unexpanded.delete(key); /** @type {Set<string|TopLevelSymbol> | true} */
const value = innerGraph.get(key); let newSet = new Set();
if (value !== true && value !== undefined) { let isTerminal = true;
const newSet = new Set(); const value = innerGraph.get(key);
for (const item of value) { if (value !== true && value !== undefined) {
if (typeof item === "string") { for (const item of value) {
newSet.add(item); if (typeof item === "string") {
} else { newSet.add(item);
expand(item); } else {
const itemValue = innerGraph.get(item); const itemValue = innerGraph.get(item);
if (itemValue === true) { if (itemValue === true) {
innerGraph.set(key, true); newSet = true;
return; break;
} }
if (itemValue !== undefined) { if (itemValue !== undefined) {
for (const i of itemValue) { for (const i of itemValue) {
if (typeof i === "string") newSet.add(i); if (i === key) continue;
newSet.add(i);
if (typeof i !== "string") {
isTerminal = false;
}
}
} }
} }
} }
if (newSet === true) {
innerGraph.set(key, true);
} else if (newSet.size === 0) {
innerGraph.set(key, undefined);
} else {
innerGraph.set(key, newSet);
}
} }
if (newSet.size === 0) { if (isTerminal) {
innerGraph.set(key, undefined); nonTerminal.delete(key);
} else {
innerGraph.set(key, newSet);
} }
} }
};
for (const item of unexpanded) {
expand(item);
} }
for (const dep of parser.state for (const dep of parser.state
.harmonyAllExportDependentDependencies) { .harmonyAllExportDependentDependencies) {
const value = innerGraph.get(dep); const value = innerGraph.get(dep);

View File

@ -0,0 +1 @@
import "./inner";

View File

@ -0,0 +1,12 @@
import { exportAUsed, exportBUsed, exportCUsed } from "./inner";
import { y } from "./module";
it("export should be unused when only unused functions use it", () => {
expect(y("a")).toBe("okBAA");
expect(exportAUsed).toBe(true);
expect(exportBUsed).toBe(true);
if (process.env.NODE_ENV === "production") {
expect(exportCUsed).toBe(false);
}
return import("./chunk");
});

View File

@ -0,0 +1,13 @@
export function A(s) {
return s + "A";
}
export function B(s) {
return s + "B";
}
export function C(s) {
return s + "C";
}
export const exportAUsed = __webpack_exports_info__.A.used;
export const exportBUsed = __webpack_exports_info__.B.used;
export const exportCUsed = __webpack_exports_info__.C.used;

View File

@ -0,0 +1,36 @@
import { A, B, C } from "./inner";
function x(type) {
switch (type) {
case "a":
return withA("b");
case "b":
return withB("c");
case "c":
return "ok";
}
}
function y(v) {
return withA(v);
}
function withA(v) {
const value = x(v);
return A(value);
}
function withB(v) {
const value = x(v);
return B(value);
}
function withC(v) {
const value = x(v);
return C(value);
}
export { x, y };