Skip to content

Commit

Permalink
refactor: tap in Class visitor to play nicely with class transforms
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Oct 6, 2020
1 parent cf10e41 commit 271a56b
Show file tree
Hide file tree
Showing 21 changed files with 243 additions and 21 deletions.
40 changes: 19 additions & 21 deletions packages/babel-plugin-proposal-class-static-block/src/index.js
Expand Up @@ -19,22 +19,6 @@ function generateUid(scope, denyList: Set<string>) {
return uid;
}

/**
* Get private names defined in current class body
*
* @param {NodePath<ClassBody>} classBody
* @returns {Set<string>} A set of defined private names
*/
function getPrivateNames(classBody: NodePath<ClassBody>): Set<string> {
const privateNames = new Set();
for (const path of classBody.get("body")) {
if (path.isPrivate()) {
privateNames.add(path.get("key.id").node.name);
}
}
return privateNames;
}

export default declare(({ types: t, template, assertVersion }) => {
// todo remove this check after Babel 7.12.0 is published
if (process.env.NODE_ENV !== "test") {
Expand All @@ -45,21 +29,35 @@ export default declare(({ types: t, template, assertVersion }) => {
name: "proposal-class-static-block",
inherits: syntaxClassStaticBlock,
visitor: {
StaticBlock(path: NodePath<StaticBlock>) {
const { parentPath: classBody, scope } = path;
Class(path: NodePath<Class>) {
const { scope } = path;
const classBody = path.get("body");
const privateNames = new Set();
let staticBlockPath;
for (const path of classBody.get("body")) {
if (path.isPrivate()) {
privateNames.add(path.get("key.id").node.name);
} else if (path.isStaticBlock()) {
staticBlockPath = path;
}
}
if (!staticBlockPath) {
return;
}
const staticBlockRef = t.privateName(
t.identifier(generateUid(scope, getPrivateNames(classBody))),
t.identifier(generateUid(scope, privateNames)),
);
classBody.pushContainer(
"body",
t.classPrivateProperty(
staticBlockRef,
template.expression.ast`(() => { ${path.node.body} })()`,
template.expression.ast`(() => { ${staticBlockPath.node.body} })()`,
[],
/* static */ true,
),
);
path.remove();
staticBlockPath.remove();
path.requeue();
},
},
};
Expand Down
@@ -0,0 +1,7 @@
class Foo {
bar = 21;
static {
this.foo = this.bar;
}
}
expect(Foo.foo).toBe(undefined);
@@ -0,0 +1,7 @@
class Foo {
static {
this.foo = Foo.bar;
}
static bar = 42;
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,7 @@
class Foo {
static {
this.foo = Foo.bar;
}
static bar = 42;
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,10 @@
class Foo {}

babelHelpers.defineProperty(Foo, "bar", 42);
var _ = {
writable: true,
value: (() => {
Foo.foo = Foo.bar;
})()
};
expect(Foo.foo).toBe(42);
@@ -0,0 +1,7 @@
class Foo {
static {
this.foo = this.bar;
}
static bar = 42;
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,7 @@
class Foo {
static {
this.foo = this.bar;
}
static bar = 42;
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,10 @@
class Foo {}

babelHelpers.defineProperty(Foo, "bar", 42);
var _ = {
writable: true,
value: (() => {
Foo.foo = Foo.bar;
})()
};
expect(Foo.foo).toBe(42);
@@ -0,0 +1,6 @@
class Foo {
static {
this.foo = 42;
}
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,14 @@
class Foo extends class extends class Base {
static {
this.qux = 21;
}
} {
static {
this.bar = 21;
}
} {
static {
this.foo = this.bar + this.qux;
}
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,14 @@
class Foo extends class extends class Base {
static {
this.qux = 21;
}
} {
static {
this.bar = 21;
}
} {
static {
this.foo = this.bar + this.qux;
}
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,21 @@
var _class, _temp, _2, _class2, _temp2, _3;

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) {}

var _ = {
writable: true,
value: (() => {
Foo.foo = Foo.bar + Foo.qux;
})()
};
expect(Foo.foo).toBe(42);
@@ -0,0 +1,8 @@
class Foo {
static #bar = 21;
static {
this.foo = this.#bar + this.qux;
}
static qux = 21;
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,8 @@
class Foo {
static #_ = 42;
// static block can not be tranformed as `#_` here
static {
this.foo = this.#_;
}
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,8 @@
class Foo {
static #_ = 42;
// static block can not be tranformed as `#_` here
static {
this.foo = this.#_;
}
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,15 @@
function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { if (receiver !== classConstructor) { throw new TypeError("Private static access of wrong provenance"); } if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }

class Foo {}

var _ = {
writable: true,
value: 42
};
var _2 = {
writable: true,
value: (() => {
Foo.foo = _classStaticPrivateFieldSpecGet(Foo, Foo, _);
})()
};
expect(Foo.foo).toBe(42);
@@ -0,0 +1,7 @@
class Foo {
bar = 21;
static {
this.foo = this.bar;
}
}
expect(Foo.foo).toBe(undefined);
@@ -0,0 +1,7 @@
{
"plugins": [
"proposal-class-static-block",
"proposal-class-properties",
"external-helpers"
]
}
@@ -0,0 +1,8 @@
let getFoo;
class Foo {
static #foo = 42;
static {
getFoo = () => this.#foo;
}
}
expect(getFoo()).toBe(42);
@@ -0,0 +1,11 @@
class Foo extends class {
static {
this.bar = 42;
}
} {
static bar = 21;
static {
this.foo = super.bar;
}
}
expect(Foo.foo).toBe(42);
@@ -0,0 +1,42 @@
import * as babel from "@babel/core";
import proposalClassStaticBlock from "../lib/index.js";

describe("plugin ordering", () => {
it("should throw 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(() => {
babel.transform(source, {
filename: "example.js",
highlightCode: false,
configFile: false,
babelrc: false,
plugins: [
"@babel/plugin-proposal-class-properties",
proposalClassStaticBlock,
],
});
}).toThrowErrorMatchingInlineSnapshot(`
"/Users/jh/code/babel/example.js: Incorrect plugin orders, \`@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;"
`);
});
});

0 comments on commit 271a56b

Please sign in to comment.