assign class property keys to the correct top level symbol

This commit is contained in:
Tobias Koppers 2021-06-21 09:28:25 +02:00
parent 4a3fe2b7ac
commit 4e608c8723
8 changed files with 159 additions and 9 deletions

View File

@ -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<SyncBailHook<[LabeledStatementNode], boolean | void>>} */
label: new HookMap(() => new SyncBailHook(["statement"])),
/** @type {SyncBailHook<[StatementNode, ImportSource], boolean | void>} */

View File

@ -238,12 +238,25 @@ class InnerGraphPlugin {
}
);
parser.hooks.classBodyValue.tap(
parser.hooks.classBodyElement.tap(
"InnerGraphPlugin",
(expression, element, statement) => {
(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) {
InnerGraph.setTopLevelSymbol(parser.state, undefined);
}
}
}
);
parser.hooks.classBodyValue.tap(
"InnerGraphPlugin",
(expression, element, classDefinition) => {
if (!InnerGraph.isEnabled(parser.state)) return;
if (parser.scope.topLevelScope === true) {
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);
}

View File

@ -1,6 +1,6 @@
const { describeCases } = require("./TestCases.template");
describe("TestCases", () => {
describe("TestCasesProdGlobalUsed", () => {
describeCases({
name: "production with usedExports global",
mode: "production",

View File

@ -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");
});

View File

@ -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";

View File

@ -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 {};

View File

@ -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 };

View File

@ -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;
});