Skip to content

Commit

Permalink
Fix classNameTDZError in computed prototype methods with class fields (
Browse files Browse the repository at this point in the history
…#11068)

* Draft fix for TDZError in computed prototype methods

* Added tests for loose:false and also extracted handleClassTDZ

* Added state parameter to handleClassTDZ function

* Extracted the state object to a variable

* Added helper comments for environmentVisitor

* Added else condition to traverse computed Path

* Cached computed path key into a variable
  • Loading branch information
sidntrivedi012 committed Feb 12, 2020
1 parent 157bb6e commit 3fc904e
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 25 deletions.
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,
]);
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 @@ -41,6 +41,7 @@ function skipAllButComputedKey(path: NodePath) {
}
}

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

0 comments on commit 3fc904e

Please sign in to comment.