Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix classNameTDZError in computed prototype methods with class fields #11068

Merged
merged 7 commits into from Feb 12, 2020
52 changes: 28 additions & 24 deletions packages/babel-helper-create-class-features-plugin/src/misc.js
Expand Up @@ -25,26 +25,24 @@ const referenceVisitor = {
}
},
};
function handleClassTDZ(path, state) {
if (
state.classBinding &&
state.classBinding === path.scope.getBinding(path.node.name)
) {
const classNameTDZError = state.file.addHelper("classNameTDZError");
const throwNode = t.callExpression(classNameTDZError, [
t.stringLiteral(path.node.name),
]);

path.replaceWith(t.sequenceExpression([throwNode, path.node]));
path.skip();
}
}

const classFieldDefinitionEvaluationTDZVisitor = traverse.visitors.merge([
{
ReferencedIdentifier(path) {
if (
this.classBinding &&
this.classBinding === path.scope.getBinding(path.node.name)
) {
const classNameTDZError = this.file.addHelper("classNameTDZError");
const throwNode = t.callExpression(classNameTDZError, [
t.stringLiteral(path.node.name),
]);

path.replaceWith(t.sequenceExpression([throwNode, path.node]));
path.skip();
}
},
},
environmentVisitor,
sidntrivedi012 marked this conversation as resolved.
Show resolved Hide resolved
]);
const classFieldDefinitionEvaluationTDZVisitor = {
ReferencedIdentifier: handleClassTDZ,
};

export function injectInitialization(path, constructor, nodes, renamer) {
if (!nodes.length) return;
Expand Down Expand Up @@ -84,12 +82,18 @@ export function injectInitialization(path, constructor, nodes, renamer) {

export function extractComputedKeys(ref, path, computedPaths, file) {
const declarations = [];

const state = {
classBinding: path.node.id && path.scope.getBinding(path.node.id.name),
file,
};
for (const computedPath of computedPaths) {
computedPath.traverse(classFieldDefinitionEvaluationTDZVisitor, {
classBinding: path.node.id && path.scope.getBinding(path.node.id.name),
file,
});
if (computedPath.get("key").isReferencedIdentifier()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we cache computedPath.get("key")? It is also used in !computedPath.get("key").isConstantExpression() later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JLHwung Done. PTAL 🙂

handleClassTDZ(computedPath.get("key"), state);
} else {
computedPath
.get("key")
.traverse(classFieldDefinitionEvaluationTDZVisitor, state);
}

const computedNode = computedPath.node;
// Make sure computed property names are only evaluated once (upon class definition)
Expand Down
@@ -0,0 +1,6 @@
class Foo {
static nickname = 'Tom';
['HELLO']() {
console.log('>>>>', Foo);
}
}
@@ -0,0 +1,10 @@
{
"plugins": [
[
"proposal-class-properties",
{
"loose": false
}
]
]
}
@@ -0,0 +1,10 @@
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 {
['HELLO']() {
console.log('>>>>', Foo);
}

}

_defineProperty(Foo, "nickname", 'Tom');
@@ -0,0 +1,6 @@
class Foo {
static nickname = 'Tom';
['HELLO']() {
console.log('>>>>', Foo);
}
}
@@ -0,0 +1,10 @@
{
"plugins": [
[
"proposal-class-properties",
{
"loose": true
}
]
]
}
@@ -0,0 +1,8 @@
class Foo {
['HELLO']() {
console.log('>>>>', Foo);
}

}

Foo.nickname = 'Tom';
1 change: 1 addition & 0 deletions packages/babel-helper-replace-supers/src/index.js
Expand Up @@ -40,6 +40,7 @@ function skipAllButComputedKey(path) {
}
}

// environmentVisitor should be used when traversing the whole class and not for specific class elements/methods.
export const environmentVisitor = {
TypeAnnotation(path) {
path.skip();
Expand Down