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:
parent
157bb6e831
commit
3fc904e1d4
@ -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),
|
||||
]);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -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,
|
||||
);
|
||||
|
||||
@ -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';
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user