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,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,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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 = {
|
export const environmentVisitor = {
|
||||||
TypeAnnotation(path: NodePath) {
|
TypeAnnotation(path: NodePath) {
|
||||||
path.skip();
|
path.skip();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user