Fix class properties after nested class' bare super (#7671)
* Fix class properties after nested class' bare super Fixes #7371. * Fix node 4 test * This damn node 4 test * All of the ClassBody, but not the methods or field inits * tmp * tmp * Use common class environment visitor * Tests * Use skipKey to avoid recursive traversal * Remove old state * Use jest expect
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@babel/helper-function-name": "7.0.0-beta.44",
|
||||
"@babel/helper-plugin-utils": "7.0.0-beta.44",
|
||||
"@babel/helper-replace-supers": "7.0.0-beta.44",
|
||||
"@babel/plugin-syntax-class-properties": "7.0.0-beta.44"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import nameFunction from "@babel/helper-function-name";
|
||||
import syntaxClassProperties from "@babel/plugin-syntax-class-properties";
|
||||
import { template, types as t } from "@babel/core";
|
||||
import { template, traverse, types as t } from "@babel/core";
|
||||
import { environmentVisitor } from "@babel/helper-replace-supers";
|
||||
|
||||
export default declare((api, options) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
const { loose } = options;
|
||||
|
||||
const findBareSupers = {
|
||||
Super(path) {
|
||||
if (path.parentPath.isCallExpression({ callee: path.node })) {
|
||||
this.push(path.parentPath);
|
||||
}
|
||||
const findBareSupers = traverse.visitors.merge([
|
||||
{
|
||||
Super(path) {
|
||||
const { node, parentPath } = path;
|
||||
if (parentPath.isCallExpression({ callee: node })) {
|
||||
this.push(parentPath);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
environmentVisitor,
|
||||
]);
|
||||
|
||||
const referenceVisitor = {
|
||||
"TSTypeAnnotation|TypeAnnotation"(path) {
|
||||
path.skip();
|
||||
},
|
||||
|
||||
ReferencedIdentifier(path) {
|
||||
if (this.scope.hasOwnBinding(path.node.name)) {
|
||||
this.scope.rename(path.node.name);
|
||||
@@ -28,25 +34,22 @@ export default declare((api, options) => {
|
||||
},
|
||||
};
|
||||
|
||||
const ClassFieldDefinitionEvaluationTDZVisitor = {
|
||||
Expression(path) {
|
||||
if (path === this.shouldSkip) {
|
||||
path.skip();
|
||||
}
|
||||
},
|
||||
const classFieldDefinitionEvaluationTDZVisitor = traverse.visitors.merge([
|
||||
{
|
||||
ReferencedIdentifier(path) {
|
||||
if (this.classRef === path.scope.getBinding(path.node.name)) {
|
||||
const classNameTDZError = this.file.addHelper("classNameTDZError");
|
||||
const throwNode = t.callExpression(classNameTDZError, [
|
||||
t.stringLiteral(path.node.name),
|
||||
]);
|
||||
|
||||
ReferencedIdentifier(path) {
|
||||
if (this.classRef === 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 buildClassPropertySpec = (ref, { key, value, computed }, scope) => {
|
||||
return template.statement`
|
||||
@@ -122,10 +125,9 @@ export default declare((api, options) => {
|
||||
// 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()) {
|
||||
computedPath.traverse(ClassFieldDefinitionEvaluationTDZVisitor, {
|
||||
computedPath.traverse(classFieldDefinitionEvaluationTDZVisitor, {
|
||||
classRef: path.scope.getBinding(ref.name),
|
||||
file: this.file,
|
||||
shouldSkip: computedPath.get("value"),
|
||||
});
|
||||
const ident = path.scope.generateUidIdentifierBasedOnNode(
|
||||
computedNode.key,
|
||||
|
||||
99
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/exec.js
vendored
Normal file
99
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/exec.js
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
class C {
|
||||
}
|
||||
|
||||
class A extends C {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
class B extends C {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new A();
|
||||
|
||||
class Obj {
|
||||
constructor() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// ensure superClass is still transformed
|
||||
class SuperClass extends Obj {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
class B extends (super(), Obj) {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined()
|
||||
}
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new SuperClass();
|
||||
|
||||
// ensure ComputedKey Method is still transformed
|
||||
class ComputedMethod extends Obj {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
class B extends Obj {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined()
|
||||
}
|
||||
|
||||
[super()]() { }
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new ComputedMethod();
|
||||
|
||||
|
||||
// ensure ComputedKey Field is still transformed
|
||||
class ComputedField extends Obj {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
class B extends Obj {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined()
|
||||
}
|
||||
|
||||
[super()] = 1;
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new ComputedField();
|
||||
99
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/input.js
vendored
Normal file
99
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/input.js
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
class C {
|
||||
}
|
||||
|
||||
class A extends C {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
class B extends C {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new A();
|
||||
|
||||
class Obj {
|
||||
constructor() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// ensure superClass is still transformed
|
||||
class SuperClass extends Obj {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
class B extends (super(), Obj) {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined()
|
||||
}
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new SuperClass();
|
||||
|
||||
// ensure ComputedKey Method is still transformed
|
||||
class ComputedMethod extends Obj {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
class B extends Obj {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined()
|
||||
}
|
||||
|
||||
[super()]() { }
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new ComputedMethod();
|
||||
|
||||
|
||||
// ensure ComputedKey Field is still transformed
|
||||
class ComputedField extends Obj {
|
||||
field = 1;
|
||||
|
||||
constructor() {
|
||||
class B extends Obj {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
expect(this.field).toBeUndefined()
|
||||
}
|
||||
|
||||
[super()] = 1;
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1)
|
||||
|
||||
new B();
|
||||
}
|
||||
}
|
||||
|
||||
new ComputedField();
|
||||
3
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/options.json
vendored
Normal file
3
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/options.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "proposal-class-properties", "transform-arrow-functions"]
|
||||
}
|
||||
122
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/output.js
vendored
Normal file
122
packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/output.js
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
"use strict";
|
||||
|
||||
class C {}
|
||||
|
||||
class A extends C {
|
||||
constructor() {
|
||||
super();
|
||||
Object.defineProperty(this, "field", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
});
|
||||
|
||||
class B extends C {
|
||||
constructor() {
|
||||
super();
|
||||
expect(this.field).toBeUndefined();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1);
|
||||
new B();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new A();
|
||||
|
||||
class Obj {
|
||||
constructor() {
|
||||
return {};
|
||||
}
|
||||
|
||||
} // ensure superClass is still transformed
|
||||
|
||||
|
||||
class SuperClass extends Obj {
|
||||
constructor() {
|
||||
var _temp;
|
||||
|
||||
class B extends ((_temp = super(), Object.defineProperty(this, "field", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
}), _temp), Obj) {
|
||||
constructor() {
|
||||
super();
|
||||
expect(this.field).toBeUndefined();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1);
|
||||
new B();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new SuperClass(); // ensure ComputedKey Method is still transformed
|
||||
|
||||
class ComputedMethod extends Obj {
|
||||
constructor() {
|
||||
var _temp2;
|
||||
|
||||
class B extends Obj {
|
||||
constructor() {
|
||||
super();
|
||||
expect(this.field).toBeUndefined();
|
||||
}
|
||||
|
||||
[(_temp2 = super(), Object.defineProperty(this, "field", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
}), _temp2)]() {}
|
||||
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1);
|
||||
new B();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new ComputedMethod(); // ensure ComputedKey Field is still transformed
|
||||
|
||||
class ComputedField extends Obj {
|
||||
constructor() {
|
||||
var _temp3;
|
||||
|
||||
var _ref = (_temp3 = super(), Object.defineProperty(this, "field", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
}), _temp3);
|
||||
|
||||
class B extends Obj {
|
||||
constructor() {
|
||||
super();
|
||||
Object.defineProperty(this, _ref, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
});
|
||||
expect(this.field).toBeUndefined();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
expect(this.field).toBe(1);
|
||||
new B();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new ComputedField();
|
||||
Reference in New Issue
Block a user