Fix classNameTDZError in computed prototype methods with class fields (#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
This commit is contained in:
Siddhant N Trivedi 2020-02-12 16:25:38 +05:30 committed by GitHub
parent 157bb6e831
commit 3fc904e1d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 24 deletions

View File

@ -25,15 +25,12 @@ const referenceVisitor = {
} }
}, },
}; };
function handleClassTDZ(path, state) {
const classFieldDefinitionEvaluationTDZVisitor = traverse.visitors.merge([
{
ReferencedIdentifier(path) {
if ( if (
this.classBinding && state.classBinding &&
this.classBinding === path.scope.getBinding(path.node.name) state.classBinding === path.scope.getBinding(path.node.name)
) { ) {
const classNameTDZError = this.file.addHelper("classNameTDZError"); const classNameTDZError = state.file.addHelper("classNameTDZError");
const throwNode = t.callExpression(classNameTDZError, [ const throwNode = t.callExpression(classNameTDZError, [
t.stringLiteral(path.node.name), t.stringLiteral(path.node.name),
]); ]);
@ -41,10 +38,11 @@ const classFieldDefinitionEvaluationTDZVisitor = traverse.visitors.merge([
path.replaceWith(t.sequenceExpression([throwNode, path.node])); path.replaceWith(t.sequenceExpression([throwNode, path.node]));
path.skip(); path.skip();
} }
}, }
},
environmentVisitor, const classFieldDefinitionEvaluationTDZVisitor = {
]); ReferencedIdentifier: handleClassTDZ,
};
export function injectInitialization(path, constructor, nodes, renamer) { export function injectInitialization(path, constructor, nodes, renamer) {
if (!nodes.length) return; if (!nodes.length) return;
@ -84,17 +82,22 @@ export function injectInitialization(path, constructor, nodes, renamer) {
export function extractComputedKeys(ref, path, computedPaths, file) { export function extractComputedKeys(ref, path, computedPaths, file) {
const declarations = []; const declarations = [];
const state = {
for (const computedPath of computedPaths) {
computedPath.traverse(classFieldDefinitionEvaluationTDZVisitor, {
classBinding: path.node.id && path.scope.getBinding(path.node.id.name), classBinding: path.node.id && path.scope.getBinding(path.node.id.name),
file, file,
}); };
for (const computedPath of computedPaths) {
const computedKey = computedPath.get("key");
if (computedKey.isReferencedIdentifier()) {
handleClassTDZ(computedKey, state);
} else {
computedKey.traverse(classFieldDefinitionEvaluationTDZVisitor, state);
}
const computedNode = computedPath.node; const computedNode = computedPath.node;
// Make sure computed property names are only evaluated once (upon class definition) // Make sure computed property names are only evaluated once (upon class definition)
// and in the right order in combination with static properties // and in the right order in combination with static properties
if (!computedPath.get("key").isConstantExpression()) { if (!computedKey.isConstantExpression()) {
const ident = path.scope.generateUidIdentifierBasedOnNode( const ident = path.scope.generateUidIdentifierBasedOnNode(
computedNode.key, computedNode.key,
); );

View File

@ -0,0 +1,6 @@
class Foo {
static nickname = 'Tom';
['HELLO']() {
console.log('>>>>', Foo);
}
}

View File

@ -0,0 +1,10 @@
{
"plugins": [
[
"proposal-class-properties",
{
"loose": false
}
]
]
}

View File

@ -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');

View File

@ -0,0 +1,6 @@
class Foo {
static nickname = 'Tom';
['HELLO']() {
console.log('>>>>', Foo);
}
}

View File

@ -0,0 +1,10 @@
{
"plugins": [
[
"proposal-class-properties",
{
"loose": true
}
]
]
}

View File

@ -0,0 +1,8 @@
class Foo {
['HELLO']() {
console.log('>>>>', Foo);
}
}
Foo.nickname = 'Tom';

View File

@ -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 = { export const environmentVisitor = {
TypeAnnotation(path: NodePath) { TypeAnnotation(path: NodePath) {
path.skip(); path.skip();