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
53 changes: 28 additions & 25 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,17 +82,22 @@ 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,
});
const computedKey = computedPath.get("key");
if (computedKey.isReferencedIdentifier()) {
handleClassTDZ(computedKey, state);
} else {
computedKey.traverse(classFieldDefinitionEvaluationTDZVisitor, state);
}

const computedNode = computedPath.node;
// Make sure computed property names are only evaluated once (upon class definition)
// and in the right order in combination with static properties
if (!computedPath.get("key").isConstantExpression()) {
if (!computedKey.isConstantExpression()) {
const ident = path.scope.generateUidIdentifierBasedOnNode(
computedNode.key,
);
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