diff --git a/lib/javascript/JavascriptParser.js b/lib/javascript/JavascriptParser.js index 70985412083..e1519340745 100644 --- a/lib/javascript/JavascriptParser.js +++ b/lib/javascript/JavascriptParser.js @@ -175,11 +175,18 @@ class JavascriptParser extends Parser { /** @type {SyncBailHook<[IfStatementNode], boolean | void>} */ statementIf: new SyncBailHook(["statement"]), /** @type {SyncBailHook<[ExpressionNode, ClassExpressionNode | ClassDeclarationNode], boolean | void>} */ - classExtendsExpression: new SyncBailHook(["expression", "statement"]), + classExtendsExpression: new SyncBailHook([ + "expression", + "classDefinition" + ]), /** @type {SyncBailHook<[MethodDefinitionNode | PropertyDefinitionNode, ClassExpressionNode | ClassDeclarationNode], boolean | void>} */ - classBodyElement: new SyncBailHook(["element", "statement"]), + classBodyElement: new SyncBailHook(["element", "classDefinition"]), /** @type {SyncBailHook<[ExpressionNode, MethodDefinitionNode | PropertyDefinitionNode, ClassExpressionNode | ClassDeclarationNode], boolean | void>} */ - classBodyValue: new SyncBailHook(["expression", "element", "statement"]), + classBodyValue: new SyncBailHook([ + "expression", + "element", + "classDefinition" + ]), /** @type {HookMap>} */ label: new HookMap(() => new SyncBailHook(["statement"])), /** @type {SyncBailHook<[StatementNode, ImportSource], boolean | void>} */ diff --git a/lib/optimize/InnerGraphPlugin.js b/lib/optimize/InnerGraphPlugin.js index 7a182c341a0..7cb101add78 100644 --- a/lib/optimize/InnerGraphPlugin.js +++ b/lib/optimize/InnerGraphPlugin.js @@ -238,12 +238,25 @@ class InnerGraphPlugin { } ); + parser.hooks.classBodyElement.tap( + "InnerGraphPlugin", + (element, classDefinition) => { + if (!InnerGraph.isEnabled(parser.state)) return; + if (parser.scope.topLevelScope === true) { + const fn = classWithTopLevelSymbol.get(classDefinition); + if (fn) { + InnerGraph.setTopLevelSymbol(parser.state, undefined); + } + } + } + ); + parser.hooks.classBodyValue.tap( "InnerGraphPlugin", - (expression, element, statement) => { + (expression, element, classDefinition) => { if (!InnerGraph.isEnabled(parser.state)) return; if (parser.scope.topLevelScope === true) { - const fn = classWithTopLevelSymbol.get(statement); + const fn = classWithTopLevelSymbol.get(classDefinition); if (fn) { if ( !element.static || @@ -253,6 +266,24 @@ class InnerGraphPlugin { ) ) { InnerGraph.setTopLevelSymbol(parser.state, fn); + if (element.type !== "MethodDefinition" && element.static) { + InnerGraph.onUsage(parser.state, usedByExports => { + switch (usedByExports) { + case undefined: + case true: + return; + default: { + const dep = new PureExpressionDependency( + expression.range + ); + dep.loc = expression.loc; + dep.usedByExports = usedByExports; + parser.state.module.addDependency(dep); + break; + } + } + }); + } } else { InnerGraph.setTopLevelSymbol(parser.state, undefined); } diff --git a/test/TestCasesProdGlobalUsed.test.js b/test/TestCasesProdGlobalUsed.test.js index abbdbc0818e..ee3d0fbb2c8 100644 --- a/test/TestCasesProdGlobalUsed.test.js +++ b/test/TestCasesProdGlobalUsed.test.js @@ -1,6 +1,6 @@ const { describeCases } = require("./TestCases.template"); -describe("TestCases", () => { +describe("TestCasesProdGlobalUsed", () => { describeCases({ name: "production with usedExports global", mode: "production", diff --git a/test/cases/inner-graph/class-dynamic-props/index.js b/test/cases/inner-graph/class-dynamic-props/index.js new file mode 100644 index 00000000000..a8b69c9ead4 --- /dev/null +++ b/test/cases/inner-graph/class-dynamic-props/index.js @@ -0,0 +1,22 @@ +it("should not throw when using dynamic properties in unused classes", () => { + require("./unused1"); +}); + +it("should not throw when using dynamic properties in used classes", () => { + const exports = require("./used1"); + const x = new exports.Used(); + expect(x.a()).toBe("A"); + expect(x.b).toBe("B"); + expect(x.c).toBe("C"); + expect(exports.Used.d()).toBe("D"); + expect(exports.Used.e).toBe("E"); + expect(exports.Used.f).toBe("F"); + const x2 = new exports.Used2(); + expect(x2.a()).toBe("A"); + expect(x2.b).toBe("B"); + expect(x2.c).toBe("C"); + expect(exports.Used2.d()).toBe("D"); + expect(exports.Used2.e).toBe("E"); + expect(exports.Used2.f).toBe("F"); + expect(x2.x).toBe("X"); +}); diff --git a/test/cases/inner-graph/class-dynamic-props/module.js b/test/cases/inner-graph/class-dynamic-props/module.js new file mode 100644 index 00000000000..f7f9dad52d2 --- /dev/null +++ b/test/cases/inner-graph/class-dynamic-props/module.js @@ -0,0 +1,16 @@ +export const a = () => "a"; +export const A = "A"; +export const b = "b"; +export const B = "B"; +export const c = "c"; +export const C = "C"; +export const d = () => "d"; +export const D = "D"; +export const e = "e"; +export const E = "E"; +export const f = "f"; +export const F = "F"; +export class X { + x = "X"; +} +export const y = "y"; diff --git a/test/cases/inner-graph/class-dynamic-props/unused1.js b/test/cases/inner-graph/class-dynamic-props/unused1.js new file mode 100644 index 00000000000..6abeb0f71c1 --- /dev/null +++ b/test/cases/inner-graph/class-dynamic-props/unused1.js @@ -0,0 +1,37 @@ +import { a, b, c, d, e, f, A, B, C, D, E, F, X } from "./module"; + +class Unused { + [a()]() { + return A; + } + [b] = B; + get [c]() { + return C; + } + static [d()]() { + return D; + } + static [e] = E; + static get [f]() { + return F; + } +} + +class Unused2 extends X { + [a()]() { + return A; + } + [b] = B; + get [c]() { + return C; + } + static [d()]() { + return D; + } + static [e] = E; + static get [f]() { + return F; + } +} + +export {}; diff --git a/test/cases/inner-graph/class-dynamic-props/used1.js b/test/cases/inner-graph/class-dynamic-props/used1.js new file mode 100644 index 00000000000..f9ce10b5bc9 --- /dev/null +++ b/test/cases/inner-graph/class-dynamic-props/used1.js @@ -0,0 +1,37 @@ +import { a, b, c, d, e, f, A, B, C, D, E, F, X } from "./module?1"; + +class Used { + [a()]() { + return A; + } + [b] = B; + get [c]() { + return C; + } + static [d()]() { + return D; + } + static [e] = E; + static get [f]() { + return F; + } +} + +class Used2 extends X { + [a()]() { + return A; + } + [b] = B; + get [c]() { + return C; + } + static [d()]() { + return D; + } + static [e] = E; + static get [f]() { + return F; + } +} + +export { Used, Used2 }; diff --git a/test/cases/inner-graph/simple/module.js b/test/cases/inner-graph/simple/module.js index a02c0fceb84..e59ea2672c0 100644 --- a/test/cases/inner-graph/simple/module.js +++ b/test/cases/inner-graph/simple/module.js @@ -12,7 +12,7 @@ function f3() { return EXPORT; } -const f4 = function() { +const f4 = function () { return EXPORT; }; @@ -95,6 +95,6 @@ export function fWithDefault(r = EXPORT4) { return r; } -export default (function() { +export default (function () { return EXPORT; });