Fix TSParameterProperty getting lost with transform-classes (#8682)
This commit is contained in:
parent
380f2a0297
commit
8897b67f40
@ -20,6 +20,8 @@ interface State {
|
|||||||
programPath: any;
|
programPath: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PARSED_PARAMS = new WeakSet();
|
||||||
|
|
||||||
export default declare((api, { jsxPragma = "React" }) => {
|
export default declare((api, { jsxPragma = "React" }) => {
|
||||||
api.assertVersion(7);
|
api.assertVersion(7);
|
||||||
|
|
||||||
@ -96,58 +98,6 @@ export default declare((api, { jsxPragma = "React" }) => {
|
|||||||
if (node.abstract) node.abstract = null;
|
if (node.abstract) node.abstract = null;
|
||||||
if (node.optional) node.optional = null;
|
if (node.optional) node.optional = null;
|
||||||
|
|
||||||
if (node.kind !== "constructor") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect parameter properties
|
|
||||||
const parameterProperties = [];
|
|
||||||
for (const param of node.params) {
|
|
||||||
if (param.type === "TSParameterProperty") {
|
|
||||||
parameterProperties.push(param.parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parameterProperties.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const assigns = parameterProperties.map(p => {
|
|
||||||
let name;
|
|
||||||
if (t.isIdentifier(p)) {
|
|
||||||
name = p.name;
|
|
||||||
} else if (t.isAssignmentPattern(p) && t.isIdentifier(p.left)) {
|
|
||||||
name = p.left.name;
|
|
||||||
} else {
|
|
||||||
throw path.buildCodeFrameError(
|
|
||||||
"Parameter properties can not be destructuring patterns.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const assign = t.assignmentExpression(
|
|
||||||
"=",
|
|
||||||
t.memberExpression(t.thisExpression(), t.identifier(name)),
|
|
||||||
t.identifier(name),
|
|
||||||
);
|
|
||||||
return t.expressionStatement(assign);
|
|
||||||
});
|
|
||||||
|
|
||||||
const statements = node.body.body;
|
|
||||||
|
|
||||||
const first = statements[0];
|
|
||||||
const startsWithSuperCall =
|
|
||||||
first !== undefined &&
|
|
||||||
t.isExpressionStatement(first) &&
|
|
||||||
t.isCallExpression(first.expression) &&
|
|
||||||
t.isSuper(first.expression.callee);
|
|
||||||
|
|
||||||
// Make sure to put parameter properties *after* the `super` call.
|
|
||||||
// TypeScript will enforce that a 'super()' call is the first statement
|
|
||||||
// when there are parameter properties.
|
|
||||||
node.body.body = startsWithSuperCall
|
|
||||||
? [first, ...assigns, ...statements.slice(1)]
|
|
||||||
: [...assigns, ...statements];
|
|
||||||
|
|
||||||
// Rest handled by Function visitor
|
// Rest handled by Function visitor
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -182,14 +132,74 @@ export default declare((api, { jsxPragma = "React" }) => {
|
|||||||
if (node.superTypeParameters) node.superTypeParameters = null;
|
if (node.superTypeParameters) node.superTypeParameters = null;
|
||||||
if (node.implements) node.implements = null;
|
if (node.implements) node.implements = null;
|
||||||
|
|
||||||
// Same logic is used in babel-plugin-transform-flow-strip-types:
|
// Similar to the logic in `transform-flow-strip-types`, we need to
|
||||||
// We do this here instead of in a `ClassProperty` visitor because the class transform
|
// handle `TSParameterProperty` and `ClassProperty` here because the
|
||||||
// would transform the class before we reached the class property.
|
// class transform would transform the class, causing more specific
|
||||||
|
// visitors to not run.
|
||||||
path.get("body.body").forEach(child => {
|
path.get("body.body").forEach(child => {
|
||||||
if (child.isClassProperty()) {
|
const childNode = child.node;
|
||||||
child.node.typeAnnotation = null;
|
|
||||||
|
|
||||||
if (!child.node.value && !child.node.decorators) {
|
if (t.isClassMethod(childNode, { kind: "constructor" })) {
|
||||||
|
// Collects parameter properties so that we can add an assignment
|
||||||
|
// for each of them in the constructor body
|
||||||
|
//
|
||||||
|
// We use a WeakSet to ensure an assignment for a parameter
|
||||||
|
// property is only added once. This is necessary for cases like
|
||||||
|
// using `transform-classes`, which causes this visitor to run
|
||||||
|
// twice.
|
||||||
|
const parameterProperties = [];
|
||||||
|
for (const param of childNode.params) {
|
||||||
|
if (
|
||||||
|
param.type === "TSParameterProperty" &&
|
||||||
|
!PARSED_PARAMS.has(param.parameter)
|
||||||
|
) {
|
||||||
|
PARSED_PARAMS.add(param.parameter);
|
||||||
|
parameterProperties.push(param.parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameterProperties.length) {
|
||||||
|
const assigns = parameterProperties.map(p => {
|
||||||
|
let name;
|
||||||
|
if (t.isIdentifier(p)) {
|
||||||
|
name = p.name;
|
||||||
|
} else if (t.isAssignmentPattern(p) && t.isIdentifier(p.left)) {
|
||||||
|
name = p.left.name;
|
||||||
|
} else {
|
||||||
|
throw path.buildCodeFrameError(
|
||||||
|
"Parameter properties can not be destructuring patterns.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const assign = t.assignmentExpression(
|
||||||
|
"=",
|
||||||
|
t.memberExpression(t.thisExpression(), t.identifier(name)),
|
||||||
|
t.identifier(name),
|
||||||
|
);
|
||||||
|
return t.expressionStatement(assign);
|
||||||
|
});
|
||||||
|
|
||||||
|
const statements = childNode.body.body;
|
||||||
|
|
||||||
|
const first = statements[0];
|
||||||
|
|
||||||
|
const startsWithSuperCall =
|
||||||
|
first !== undefined &&
|
||||||
|
t.isExpressionStatement(first) &&
|
||||||
|
t.isCallExpression(first.expression) &&
|
||||||
|
t.isSuper(first.expression.callee);
|
||||||
|
|
||||||
|
// Make sure to put parameter properties *after* the `super`
|
||||||
|
// call. TypeScript will enforce that a 'super()' call is the
|
||||||
|
// first statement when there are parameter properties.
|
||||||
|
childNode.body.body = startsWithSuperCall
|
||||||
|
? [first, ...assigns, ...statements.slice(1)]
|
||||||
|
: [...assigns, ...statements];
|
||||||
|
}
|
||||||
|
} else if (child.isClassProperty()) {
|
||||||
|
childNode.typeAnnotation = null;
|
||||||
|
|
||||||
|
if (!childNode.value && !childNode.decorators) {
|
||||||
child.remove();
|
child.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
class Employee extends Person {
|
||||||
|
constructor(public name: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["transform-typescript", "transform-classes"]
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||||
|
|
||||||
|
function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
||||||
|
|
||||||
|
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||||
|
|
||||||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||||||
|
|
||||||
|
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||||||
|
|
||||||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||||||
|
|
||||||
|
let Employee =
|
||||||
|
/*#__PURE__*/
|
||||||
|
function (_Person) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
_inherits(Employee, _Person);
|
||||||
|
|
||||||
|
function Employee(name) {
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
_classCallCheck(this, Employee);
|
||||||
|
|
||||||
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(Employee).call(this));
|
||||||
|
_this.name = name;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Employee;
|
||||||
|
}(Person);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class Person {
|
||||||
|
constructor(public name: string) {}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["transform-typescript", "transform-classes"]
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||||
|
|
||||||
|
let Person =
|
||||||
|
/*#__PURE__*/
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function Person(name) {
|
||||||
|
_classCallCheck(this, Person);
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Person;
|
||||||
|
}();
|
||||||
Loading…
x
Reference in New Issue
Block a user