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