diff --git a/lib/dependencies/HarmonyExportExpressionDependency.js b/lib/dependencies/HarmonyExportExpressionDependency.js index 29267d053..33776d45e 100644 --- a/lib/dependencies/HarmonyExportExpressionDependency.js +++ b/lib/dependencies/HarmonyExportExpressionDependency.js @@ -103,7 +103,7 @@ HarmonyExportExpressionDependency.Template = class HarmonyExportDependencyTempla dep.range[0] - 1, content + "(" + dep.prefix ); - source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");"); + source.replace(dep.range[1], dep.rangeStatement[1] - 0.5, ");"); return; } diff --git a/lib/optimize/ConcatenatedModule.js b/lib/optimize/ConcatenatedModule.js index 7cdd6eb3d..3a49e1832 100644 --- a/lib/optimize/ConcatenatedModule.js +++ b/lib/optimize/ConcatenatedModule.js @@ -1865,7 +1865,7 @@ class HarmonyExportExpressionDependencyConcatenatedTemplate extends DependencyTe dep.range[0] - 1, content + "(" + dep.prefix ); - source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");"); + source.replace(dep.range[1], dep.rangeStatement[1] - 0.5, ");"); return; } diff --git a/lib/optimize/InnerGraphPlugin.js b/lib/optimize/InnerGraphPlugin.js index 23b9d2908..6d00d8b49 100644 --- a/lib/optimize/InnerGraphPlugin.js +++ b/lib/optimize/InnerGraphPlugin.js @@ -11,6 +11,7 @@ const { const PureExpressionDependency = require("../dependencies/PureExpressionDependency"); const InnerGraph = require("./InnerGraph"); +/** @typedef {import("estree").Node} Node */ /** @typedef {import("../Compiler")} Compiler */ /** @typedef {import("../Dependency")} Dependency */ /** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */ @@ -34,6 +35,7 @@ const isPure = (expr, parser, commentsStartPos) => { parser.getTagData(expr.name, harmonySpecifierTag) ); + case "ClassDeclaration": case "ClassExpression": if (expr.body.type !== "ClassBody") return false; if (expr.superClass && !isPure(expr.superClass, parser, expr.range[0])) { @@ -50,6 +52,7 @@ const isPure = (expr, parser, commentsStartPos) => { return true; }); + case "FunctionDeclaration": case "FunctionExpression": case "ArrowFunctionExpression": case "Literal": @@ -124,8 +127,11 @@ class InnerGraphPlugin { InnerGraph.inferDependencyUsage(parser.state); logger.timeAggregate("infer dependency usage"); }); - /** @type {WeakMap<{}, TopLevelSymbol>} */ + /** @type {WeakMap} */ const statementWithTopLevelSymbol = new WeakMap(); + /** @type {WeakMap} */ + const statementPurePart = new WeakMap(); + parser.hooks.preStatement.tap("InnerGraphPlugin", statement => { if (!InnerGraph.isEnabled(parser.state)) return; @@ -150,22 +156,25 @@ class InnerGraphPlugin { } if (statement.type === "ExportDefaultDeclaration") { const decl = statement.declaration; - if ( - decl.type === "FunctionExpression" || - decl.type === "ArrowFunctionExpression" || - decl.type === "ClassExpression" || - decl.type === "Identifier" - ) { + if (isPure(decl, parser, decl.range[1])) { const name = "*default*"; const fn = InnerGraph.tagTopLevelSymbol(parser, name); statementWithTopLevelSymbol.set(statement, fn); + if ( + !decl.type.endsWith("FunctionExpression") && + !decl.type.endsWith("Declaration") && + decl.type !== "Literal" + ) { + statementPurePart.set(statement, decl); + } } } } }); - /** @type {WeakMap<{}, TopLevelSymbol>} */ + /** @type {WeakMap} */ const declWithTopLevelSymbol = new WeakMap(); const pureDeclarators = new WeakSet(); + parser.hooks.preDeclarator.tap( "InnerGraphPlugin", (decl, statement) => { @@ -175,21 +184,16 @@ class InnerGraphPlugin { decl.init && decl.id.type === "Identifier" ) { - if ( - decl.init.type === "FunctionExpression" || - decl.init.type === "ArrowFunctionExpression" || - decl.init.type === "ClassExpression" - ) { - const name = decl.id.name; - const fn = InnerGraph.tagTopLevelSymbol(parser, name); - declWithTopLevelSymbol.set(decl, fn); - return true; - } if (isPure(decl.init, parser, decl.id.range[1])) { const name = decl.id.name; const fn = InnerGraph.tagTopLevelSymbol(parser, name); declWithTopLevelSymbol.set(decl, fn); - pureDeclarators.add(decl); + if ( + !decl.init.type.endsWith("FunctionExpression") && + decl.init.type !== "Literal" + ) { + pureDeclarators.add(decl); + } return true; } } @@ -202,12 +206,32 @@ class InnerGraphPlugin { const fn = statementWithTopLevelSymbol.get(statement); if (fn) { InnerGraph.setTopLevelSymbol(parser.state, fn); + const purePart = statementPurePart.get(statement); + if (purePart) { + InnerGraph.onUsage(parser.state, usedByExports => { + switch (usedByExports) { + case undefined: + case true: + return; + default: { + const dep = new PureExpressionDependency( + purePart.range + ); + dep.loc = statement.loc; + dep.usedByExports = usedByExports; + parser.state.module.addDependency(dep); + break; + } + } + }); + } } } }); parser.hooks.declarator.tap("InnerGraphPlugin", (decl, statement) => { if (!InnerGraph.isEnabled(parser.state)) return; const fn = declWithTopLevelSymbol.get(decl); + if (fn) { InnerGraph.setTopLevelSymbol(parser.state, fn); if (pureDeclarators.has(decl)) { diff --git a/test/__snapshots__/StatsTestCases.test.js.snap b/test/__snapshots__/StatsTestCases.test.js.snap index b3a4c257c..9cc3bfe38 100644 --- a/test/__snapshots__/StatsTestCases.test.js.snap +++ b/test/__snapshots__/StatsTestCases.test.js.snap @@ -641,26 +641,26 @@ Entrypoint entry-1 = vendor-1.js entry-1.js `; exports[`StatsTestCases should print correct stats for commons-plugin-issue-4980 1`] = ` -"Hash: 97b9c00d6fc61229e73bd32d88a9cafb3cfa08af +"Hash: f2652f529f967f7f2bec5cac904b0fb414d6d93f Child - Hash: 97b9c00d6fc61229e73b + Hash: f2652f529f967f7f2bec Time: X ms Built at: 1970-04-20 12:42:42 Asset Size app.73e459ccd7f12100fa78-1.js 6.13 KiB [emitted] [immutable] [name: app] - vendor.611fd9bad8fe7de29fe7-1.js 615 bytes [emitted] [immutable] [name: vendor] [id hint: vendor] - Entrypoint app = vendor.611fd9bad8fe7de29fe7-1.js app.73e459ccd7f12100fa78-1.js + vendor.f0527072691801f43979-1.js 615 bytes [emitted] [immutable] [name: vendor] [id hint: vendor] + Entrypoint app = vendor.f0527072691801f43979-1.js app.73e459ccd7f12100fa78-1.js ./entry-1.js + 2 modules 190 bytes [built] ./constants.js 87 bytes [built] + 3 hidden modules Child - Hash: d32d88a9cafb3cfa08af + Hash: 5cac904b0fb414d6d93f Time: X ms Built at: 1970-04-20 12:42:42 Asset Size app.1db34ba9ee5e51d1e813-2.js 6.14 KiB [emitted] [immutable] [name: app] - vendor.611fd9bad8fe7de29fe7-2.js 615 bytes [emitted] [immutable] [name: vendor] [id hint: vendor] - Entrypoint app = vendor.611fd9bad8fe7de29fe7-2.js app.1db34ba9ee5e51d1e813-2.js + vendor.f0527072691801f43979-2.js 615 bytes [emitted] [immutable] [name: vendor] [id hint: vendor] + Entrypoint app = vendor.f0527072691801f43979-2.js app.1db34ba9ee5e51d1e813-2.js ./entry-2.js + 2 modules 197 bytes [built] ./constants.js 87 bytes [built] + 3 hidden modules" @@ -1190,7 +1190,7 @@ exports[`StatsTestCases should print correct stats for immutable 1`] = ` `; exports[`StatsTestCases should print correct stats for import-context-filter 1`] = ` -"Hash: 5e1865f3f46f484bbbaf +"Hash: 781698f35c52fda3a34c Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -1612,11 +1612,11 @@ If you don't want to include a polyfill, you can use an empty module like this: `; exports[`StatsTestCases should print correct stats for module-reasons 1`] = ` -"Hash: 7ad27c2fa358483a7fdd +"Hash: 6e6b4ee402334fe8ace1 Time: X ms Built at: 1970-04-20 12:42:42 - Asset Size -main.js 1.4 KiB [emitted] [name: main] + Asset Size +main.js 1.32 KiB [emitted] [name: main] Entrypoint main = main.js ./index.js + 2 modules 102 bytes [built] entry ./index main @@ -2733,7 +2733,7 @@ Entrypoint main = main.js `; exports[`StatsTestCases should print correct stats for side-effects-simple-unused 1`] = ` -"Hash: bba4214f926bfb74bb29 +"Hash: 4205efc704387d48d033 Time: X ms Built at: 1970-04-20 12:42:42 Asset Size @@ -3784,11 +3784,11 @@ chunk default/async-a.js (async-a) 134 bytes <{179}> [rendered] `; exports[`StatsTestCases should print correct stats for tree-shaking 1`] = ` -"Hash: 06b3690c160b35374c1d +"Hash: 4abb9dd9f28d864fe0ca Time: X ms Built at: 1970-04-20 12:42:42 Asset Size -bundle.js 7.17 KiB [emitted] [name: main] +bundle.js 7.09 KiB [emitted] [name: main] Entrypoint main = bundle.js ./index.js 316 bytes [built] [1 warning] [no exports] diff --git a/test/cases/inner-graph/reexport-namespace-and-default/index.js b/test/cases/inner-graph/reexport-namespace-and-default/index.js new file mode 100644 index 000000000..931a4048b --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/index.js @@ -0,0 +1,16 @@ +import {exportDefaultUsed as export1} from "./package1/script"; +import {exportDefaultUsed as export2} from "./package1/script2"; +import {exportDefaultUsed as export3} from "./package2/script"; + +it("should load module correctly", () => { + require('./module'); +}); + +it("default export should be unused", () => { + expect(export1).toBe(false); + expect(export2).toBe(false); +}); + +it("default export should be used", () => { + expect(export3).toBe(true); +}); diff --git a/test/cases/inner-graph/reexport-namespace-and-default/module.js b/test/cases/inner-graph/reexport-namespace-and-default/module.js new file mode 100644 index 000000000..8d8ef732e --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/module.js @@ -0,0 +1,4 @@ +import * as script from "./package1/script" +import script1 from "./package2/script"; + +export const mod = script1; diff --git a/test/cases/inner-graph/reexport-namespace-and-default/package1/script.js b/test/cases/inner-graph/reexport-namespace-and-default/package1/script.js new file mode 100644 index 000000000..71fe9544e --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/package1/script.js @@ -0,0 +1,5 @@ +import mod from "./script1"; +export default mod; +export * from "./script1"; + +export const exportDefaultUsed = __webpack_exports_info__.default.used; diff --git a/test/cases/inner-graph/reexport-namespace-and-default/package1/script1.js b/test/cases/inner-graph/reexport-namespace-and-default/package1/script1.js new file mode 100644 index 000000000..cc108809b --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/package1/script1.js @@ -0,0 +1,2 @@ +export * from "./script2" +export default 1 diff --git a/test/cases/inner-graph/reexport-namespace-and-default/package1/script2.js b/test/cases/inner-graph/reexport-namespace-and-default/package1/script2.js new file mode 100644 index 000000000..dd7b295d9 --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/package1/script2.js @@ -0,0 +1,5 @@ +import mod from "./script3"; +export default function () { return mod }; +export * from "./script3"; + +export const exportDefaultUsed = __webpack_exports_info__.default.used; diff --git a/test/cases/inner-graph/reexport-namespace-and-default/package1/script3.js b/test/cases/inner-graph/reexport-namespace-and-default/package1/script3.js new file mode 100644 index 000000000..b0d35f3a1 --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/package1/script3.js @@ -0,0 +1 @@ +export default 1 diff --git a/test/cases/inner-graph/reexport-namespace-and-default/package2/script.js b/test/cases/inner-graph/reexport-namespace-and-default/package2/script.js new file mode 100644 index 000000000..71fe9544e --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/package2/script.js @@ -0,0 +1,5 @@ +import mod from "./script1"; +export default mod; +export * from "./script1"; + +export const exportDefaultUsed = __webpack_exports_info__.default.used; diff --git a/test/cases/inner-graph/reexport-namespace-and-default/package2/script1.js b/test/cases/inner-graph/reexport-namespace-and-default/package2/script1.js new file mode 100644 index 000000000..b0d35f3a1 --- /dev/null +++ b/test/cases/inner-graph/reexport-namespace-and-default/package2/script1.js @@ -0,0 +1 @@ +export default 1