Compile static blocks without the intermediate priv field step (#13297)
* Remove ordering constraints for `static-blocks` plugin * Handle static blocks directly in `helper-create-class-features-plugin`
This commit is contained in:
parent
b3d35cd412
commit
8732dd39c6
@ -6,6 +6,7 @@ export const FEATURES = Object.freeze({
|
||||
privateMethods: 1 << 2,
|
||||
decorators: 1 << 3,
|
||||
privateIn: 1 << 4,
|
||||
staticBlocks: 1 << 5,
|
||||
});
|
||||
|
||||
const featuresSameLoose = new Map([
|
||||
@ -144,8 +145,8 @@ export function verifyUsedFeatures(path, file) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: We can't use path.isPrivateMethod() because it isn't supported in <7.2.0
|
||||
if (path.isPrivate() && path.isMethod()) {
|
||||
// NOTE: path.isPrivateMethod() it isn't supported in <7.2.0
|
||||
if (path.isPrivateMethod?.()) {
|
||||
if (!hasFeature(file, FEATURES.privateMethods)) {
|
||||
throw path.buildCodeFrameError("Class private methods are not enabled.");
|
||||
}
|
||||
@ -170,4 +171,13 @@ export function verifyUsedFeatures(path, file) {
|
||||
throw path.buildCodeFrameError("Class fields are not enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
if (path.isStaticBlock?.()) {
|
||||
if (!hasFeature(file, FEATURES.staticBlocks)) {
|
||||
throw path.buildCodeFrameError(
|
||||
"Static class blocks are not enabled. " +
|
||||
"Please add `@babel/plugin-proposal-class-static-block` to your configuration.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -669,7 +669,14 @@ const thisContextVisitor = traverse.visitors.merge([
|
||||
environmentVisitor,
|
||||
]);
|
||||
|
||||
function replaceThisContext(path, ref, superRef, file, constantSuper) {
|
||||
function replaceThisContext(
|
||||
path,
|
||||
ref,
|
||||
superRef,
|
||||
file,
|
||||
isStaticBlock,
|
||||
constantSuper,
|
||||
) {
|
||||
const state = { classRef: ref, needsClassRef: false };
|
||||
|
||||
const replacer = new ReplaceSupers({
|
||||
@ -680,13 +687,13 @@ function replaceThisContext(path, ref, superRef, file, constantSuper) {
|
||||
refToPreserve: ref,
|
||||
getObjectRef() {
|
||||
state.needsClassRef = true;
|
||||
return path.node.static
|
||||
return isStaticBlock || path.node.static
|
||||
? ref
|
||||
: t.memberExpression(ref, t.identifier("prototype"));
|
||||
},
|
||||
});
|
||||
replacer.replace();
|
||||
if (path.isProperty()) {
|
||||
if (isStaticBlock || path.isProperty()) {
|
||||
path.traverse(thisContextVisitor, state);
|
||||
}
|
||||
return state.needsClassRef;
|
||||
@ -717,19 +724,26 @@ export function buildFieldsInitNodes(
|
||||
const isPublic = !isPrivate;
|
||||
const isField = prop.isProperty();
|
||||
const isMethod = !isField;
|
||||
const isStaticBlock = prop.isStaticBlock?.();
|
||||
|
||||
if (isStatic || (isMethod && isPrivate)) {
|
||||
if (isStatic || (isMethod && isPrivate) || isStaticBlock) {
|
||||
const replaced = replaceThisContext(
|
||||
prop,
|
||||
ref,
|
||||
superRef,
|
||||
state,
|
||||
isStaticBlock,
|
||||
constantSuper,
|
||||
);
|
||||
needsClassRef = needsClassRef || replaced;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case isStaticBlock:
|
||||
staticNodes.push(
|
||||
template.statement.ast`(() => ${t.blockStatement(prop.node.body)})()`,
|
||||
);
|
||||
break;
|
||||
case isStatic && isPrivate && isField && privateFieldsAsProperties:
|
||||
needsClassRef = true;
|
||||
staticNodes.push(
|
||||
|
||||
@ -146,24 +146,16 @@ export function createClassFeaturePlugin({
|
||||
constructor = path;
|
||||
} else {
|
||||
elements.push(path);
|
||||
if (path.isProperty() || path.isPrivate()) {
|
||||
if (
|
||||
path.isProperty() ||
|
||||
path.isPrivate() ||
|
||||
path.isStaticBlock?.()
|
||||
) {
|
||||
props.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDecorated) isDecorated = hasOwnDecorators(path.node);
|
||||
|
||||
if (path.isStaticBlock?.()) {
|
||||
throw path.buildCodeFrameError(`Incorrect plugin order, \`@babel/plugin-proposal-class-static-block\` should be placed before class features plugins
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-static-block",
|
||||
"@babel/plugin-proposal-private-property-in-object",
|
||||
"@babel/plugin-proposal-private-methods",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
]
|
||||
}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!props.length && !isDecorated) return;
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
#x;
|
||||
static {}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"plugins": ["proposal-class-properties", "syntax-class-static-block"],
|
||||
"throws": "Static class blocks are not enabled. Please add `@babel/plugin-proposal-class-static-block` to your configuration."
|
||||
}
|
||||
@ -281,7 +281,8 @@ export default class ReplaceSupers {
|
||||
this.methodPath = path;
|
||||
this.isDerivedConstructor =
|
||||
path.isClassMethod({ kind: "constructor" }) && !!opts.superRef;
|
||||
this.isStatic = path.isObjectMethod() || path.node.static;
|
||||
this.isStatic =
|
||||
path.isObjectMethod() || path.node.static || path.isStaticBlock?.();
|
||||
this.isPrivateMethod = path.isPrivate() && path.isMethod();
|
||||
|
||||
this.file = opts.file;
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
"babel-plugin"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "workspace:^7.13.11",
|
||||
"@babel/helper-plugin-utils": "workspace:^7.13.0",
|
||||
"@babel/plugin-syntax-class-static-block": "workspace:^7.12.13"
|
||||
},
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import syntaxClassStaticBlock from "@babel/plugin-syntax-class-static-block";
|
||||
|
||||
import {
|
||||
enableFeature,
|
||||
FEATURES,
|
||||
} from "@babel/helper-create-class-features-plugin";
|
||||
|
||||
/**
|
||||
* Generate a uid that is not in `denyList`
|
||||
*
|
||||
@ -25,10 +30,19 @@ export default declare(({ types: t, template, assertVersion }) => {
|
||||
return {
|
||||
name: "proposal-class-static-block",
|
||||
inherits: syntaxClassStaticBlock,
|
||||
|
||||
pre() {
|
||||
// Enable this in @babel/helper-create-class-features-plugin, so that it
|
||||
// can be handled by the private fields and methods transform.
|
||||
enableFeature(this.file, FEATURES.staticBlocks, /* loose */ false);
|
||||
},
|
||||
|
||||
visitor: {
|
||||
Class(path: NodePath<Class>) {
|
||||
const { scope } = path;
|
||||
const classBody = path.get("body");
|
||||
// Run on ClassBody and not on class so that if @babel/helper-create-class-features-plugin
|
||||
// is enabled it can generte optimized output without passing from the intermediate
|
||||
// private fields representation.
|
||||
ClassBody(classBody: NodePath<Class>) {
|
||||
const { scope } = classBody;
|
||||
const privateNames = new Set();
|
||||
const body = classBody.get("body");
|
||||
for (const path of body) {
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
var _ = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_");
|
||||
|
||||
class Foo {}
|
||||
|
||||
Foo.bar = 42;
|
||||
Object.defineProperty(Foo, _, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})()
|
||||
});
|
||||
|
||||
(() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
var _ = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_");
|
||||
|
||||
class Foo {}
|
||||
|
||||
Foo.bar = 42;
|
||||
Object.defineProperty(Foo, _, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})()
|
||||
});
|
||||
|
||||
(() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,23 +1,13 @@
|
||||
var _class, _2, _temp, _class2, _3, _temp2;
|
||||
var _class, _temp, _class2, _temp2;
|
||||
|
||||
var _ = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_");
|
||||
class Foo extends (_temp = _class = class extends (_temp2 = _class2 = class Base {}, (() => {
|
||||
_class2.qux = 21;
|
||||
})(), _temp2) {}, (() => {
|
||||
_class.bar = 21;
|
||||
})(), _temp) {}
|
||||
|
||||
class Foo extends (_temp = (_2 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_"), _class = class extends (_temp2 = (_3 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_"), _class2 = class Base {}), Object.defineProperty(_class2, _3, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
_class2.qux = 21;
|
||||
})()
|
||||
}), _temp2) {}), Object.defineProperty(_class, _2, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
_class.bar = 21;
|
||||
})()
|
||||
}), _temp) {}
|
||||
(() => {
|
||||
Foo.foo = Foo.bar + Foo.qux;
|
||||
})();
|
||||
|
||||
Object.defineProperty(Foo, _, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = Foo.bar + Foo.qux;
|
||||
})()
|
||||
});
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,26 +1,19 @@
|
||||
var _bar = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("bar");
|
||||
|
||||
var _ = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_");
|
||||
|
||||
var _2 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_2");
|
||||
|
||||
class Foo {}
|
||||
|
||||
Object.defineProperty(Foo, _bar, {
|
||||
writable: true,
|
||||
value: 21
|
||||
});
|
||||
Object.defineProperty(Foo, _, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = babelHelpers.classPrivateFieldLooseBase(Foo, _bar)[_bar];
|
||||
Foo.qux1 = Foo.qux;
|
||||
})()
|
||||
});
|
||||
|
||||
(() => {
|
||||
Foo.foo = babelHelpers.classPrivateFieldLooseBase(Foo, _bar)[_bar];
|
||||
Foo.qux1 = Foo.qux;
|
||||
})();
|
||||
|
||||
Foo.qux = 21;
|
||||
Object.defineProperty(Foo, _2, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.qux2 = Foo.qux;
|
||||
})()
|
||||
});
|
||||
|
||||
(() => {
|
||||
Foo.qux2 = Foo.qux;
|
||||
})();
|
||||
|
||||
@ -1,17 +1,14 @@
|
||||
var _ = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_");
|
||||
|
||||
var _2 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_2");
|
||||
|
||||
class Foo {}
|
||||
|
||||
Object.defineProperty(Foo, _, {
|
||||
writable: true,
|
||||
value: 42
|
||||
});
|
||||
Object.defineProperty(Foo, _2, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = babelHelpers.classPrivateFieldLooseBase(Foo, _)[_];
|
||||
})()
|
||||
});
|
||||
|
||||
(() => {
|
||||
Foo.foo = babelHelpers.classPrivateFieldLooseBase(Foo, _)[_];
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,26 +1,17 @@
|
||||
var _class, _2, _temp;
|
||||
var _class, _temp;
|
||||
|
||||
var _ = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_");
|
||||
|
||||
class Foo extends (_temp = (_2 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_"), _class = class {}), Object.defineProperty(_class, _2, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
_class.bar = 42;
|
||||
})()
|
||||
}), _temp) {}
|
||||
class Foo extends (_temp = _class = class {}, (() => {
|
||||
_class.bar = 42;
|
||||
})(), _temp) {}
|
||||
|
||||
Foo.bar = 21;
|
||||
Object.defineProperty(Foo, _, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
var _class2, _3, _temp2;
|
||||
|
||||
Foo.foo = (_temp2 = (_3 = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("_"), _class2 = class {}), Object.defineProperty(_class2, _3, {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
_class2.bar = 42;
|
||||
})()
|
||||
}), _temp2).bar;
|
||||
})()
|
||||
});
|
||||
(() => {
|
||||
var _class2, _temp2;
|
||||
|
||||
Foo.foo = (_temp2 = _class2 = class {}, (() => {
|
||||
_class2.bar = 42;
|
||||
})(), _temp2).bar;
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
class Foo {}
|
||||
|
||||
babelHelpers.defineProperty(Foo, "bar", 42);
|
||||
var _ = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})()
|
||||
};
|
||||
|
||||
(() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
class Foo {}
|
||||
|
||||
babelHelpers.defineProperty(Foo, "bar", 42);
|
||||
var _ = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})()
|
||||
};
|
||||
|
||||
(() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -1,21 +1,13 @@
|
||||
var _class, _temp, _2, _class2, _temp2, _3;
|
||||
var _class, _temp, _class2, _temp2;
|
||||
|
||||
class Foo extends (_temp = _class = class extends (_temp2 = _class2 = class Base {}, _3 = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
_class2.qux = 21;
|
||||
})()
|
||||
}, _temp2) {}, _2 = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
_class.bar = 21;
|
||||
})()
|
||||
}, _temp) {}
|
||||
class Foo extends (_temp = _class = class extends (_temp2 = _class2 = class Base {}, (() => {
|
||||
_class2.qux = 21;
|
||||
})(), _temp2) {}, (() => {
|
||||
_class.bar = 21;
|
||||
})(), _temp) {}
|
||||
|
||||
(() => {
|
||||
Foo.foo = Foo.bar + Foo.qux;
|
||||
})();
|
||||
|
||||
var _ = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = Foo.bar + Foo.qux;
|
||||
})()
|
||||
};
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -4,17 +4,14 @@ var _bar = {
|
||||
writable: true,
|
||||
value: 21
|
||||
};
|
||||
var _ = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||
Foo.qux1 = Foo.qux;
|
||||
})()
|
||||
};
|
||||
|
||||
(() => {
|
||||
Foo.foo = babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||
Foo.qux1 = Foo.qux;
|
||||
})();
|
||||
|
||||
babelHelpers.defineProperty(Foo, "qux", 21);
|
||||
var _2 = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.qux2 = Foo.qux;
|
||||
})()
|
||||
};
|
||||
|
||||
(() => {
|
||||
Foo.qux2 = Foo.qux;
|
||||
})();
|
||||
|
||||
@ -4,10 +4,9 @@ var _ = {
|
||||
writable: true,
|
||||
value: 42
|
||||
};
|
||||
var _2 = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
Foo.foo = babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _);
|
||||
})()
|
||||
};
|
||||
|
||||
(() => {
|
||||
Foo.foo = babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _);
|
||||
})();
|
||||
|
||||
expect(Foo.foo).toBe(42);
|
||||
|
||||
@ -2,15 +2,15 @@ import * as babel from "@babel/core";
|
||||
import proposalClassStaticBlock from "..";
|
||||
|
||||
describe("plugin ordering", () => {
|
||||
it("should throw when @babel/plugin-proposal-class-static-block is after class features plugin", () => {
|
||||
it("should work when @babel/plugin-proposal-class-static-block is after class features plugin", () => {
|
||||
const source = `class Foo {
|
||||
static {
|
||||
this.foo = Foo.bar;
|
||||
}
|
||||
static bar = 42;
|
||||
}
|
||||
`;
|
||||
expect(() => {
|
||||
static {
|
||||
this.foo = Foo.bar;
|
||||
}
|
||||
static bar = 42;
|
||||
}
|
||||
`;
|
||||
expect(
|
||||
babel.transform(source, {
|
||||
filename: "example.js",
|
||||
highlightCode: false,
|
||||
@ -20,22 +20,17 @@ describe("plugin ordering", () => {
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
proposalClassStaticBlock,
|
||||
],
|
||||
});
|
||||
})
|
||||
.toThrow(`Incorrect plugin order, \`@babel/plugin-proposal-class-static-block\` should be placed before class features plugins
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-static-block",
|
||||
"@babel/plugin-proposal-private-property-in-object",
|
||||
"@babel/plugin-proposal-private-methods",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
]
|
||||
}
|
||||
1 | class Foo {
|
||||
> 2 | static {
|
||||
| ^
|
||||
3 | this.foo = Foo.bar;
|
||||
4 | }
|
||||
5 | static bar = 42;`);
|
||||
}).code,
|
||||
).toMatchInlineSnapshot(`
|
||||
"function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
class Foo {}
|
||||
|
||||
(() => {
|
||||
Foo.foo = Foo.bar;
|
||||
})();
|
||||
|
||||
_defineProperty(Foo, \\"bar\\", 42);"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,9 +10,6 @@ class A {
|
||||
|
||||
}
|
||||
|
||||
var _ = {
|
||||
writable: true,
|
||||
value: (() => {
|
||||
register(A, _foo.has(A));
|
||||
})()
|
||||
};
|
||||
(() => {
|
||||
register(A, _foo.has(A));
|
||||
})();
|
||||
|
||||
@ -1111,6 +1111,7 @@ __metadata:
|
||||
resolution: "@babel/plugin-proposal-class-static-block@workspace:packages/babel-plugin-proposal-class-static-block"
|
||||
dependencies:
|
||||
"@babel/core": "workspace:*"
|
||||
"@babel/helper-create-class-features-plugin": "workspace:^7.13.11"
|
||||
"@babel/helper-plugin-test-runner": "workspace:*"
|
||||
"@babel/helper-plugin-utils": "workspace:^7.13.0"
|
||||
"@babel/plugin-syntax-class-static-block": "workspace:^7.12.13"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user