Add support for TS declare modifier on fields (#10545)
* [parser] Add support for TS declare modifier on fields (#10484) * [parser] Add support for TS declare modifier on fields * Use Object.create(null) * Comment * Add support for TS declare types to types and generator (#10544) * Transform TypeScript "declare" fields (#10546) * Transform TypeScript "declare" fields * Remove multiple spaces * declareFields -> allowDeclareFields * Update after rebase
This commit is contained in:
parent
87feda7c2a
commit
e9c1bce50f
@ -71,26 +71,8 @@ export function ClassBody(node: Object) {
|
||||
|
||||
export function ClassProperty(node: Object) {
|
||||
this.printJoin(node.decorators, node);
|
||||
this.tsPrintClassMemberModifiers(node, /* isField */ true);
|
||||
|
||||
if (node.accessibility) {
|
||||
// TS
|
||||
this.word(node.accessibility);
|
||||
this.space();
|
||||
}
|
||||
if (node.static) {
|
||||
this.word("static");
|
||||
this.space();
|
||||
}
|
||||
if (node.abstract) {
|
||||
// TS
|
||||
this.word("abstract");
|
||||
this.space();
|
||||
}
|
||||
if (node.readonly) {
|
||||
// TS
|
||||
this.word("readonly");
|
||||
this.space();
|
||||
}
|
||||
if (node.computed) {
|
||||
this.token("[");
|
||||
this.print(node.key, node);
|
||||
@ -148,23 +130,6 @@ export function ClassPrivateMethod(node: Object) {
|
||||
|
||||
export function _classMethodHead(node) {
|
||||
this.printJoin(node.decorators, node);
|
||||
|
||||
if (node.accessibility) {
|
||||
// TS
|
||||
this.word(node.accessibility);
|
||||
this.space();
|
||||
}
|
||||
|
||||
if (node.abstract) {
|
||||
// TS
|
||||
this.word("abstract");
|
||||
this.space();
|
||||
}
|
||||
|
||||
if (node.static) {
|
||||
this.word("static");
|
||||
this.space();
|
||||
}
|
||||
|
||||
this.tsPrintClassMemberModifiers(node, /* isField */ false);
|
||||
this._methodHead(node);
|
||||
}
|
||||
|
||||
@ -556,3 +556,26 @@ export function tsPrintSignatureDeclarationBase(node) {
|
||||
this.token(")");
|
||||
this.print(node.typeAnnotation, node);
|
||||
}
|
||||
|
||||
export function tsPrintClassMemberModifiers(node, isField) {
|
||||
if (isField && node.declare) {
|
||||
this.word("declare");
|
||||
this.space();
|
||||
}
|
||||
if (node.accessibility) {
|
||||
this.word(node.accessibility);
|
||||
this.space();
|
||||
}
|
||||
if (node.static) {
|
||||
this.word("static");
|
||||
this.space();
|
||||
}
|
||||
if (node.abstract) {
|
||||
this.word("abstract");
|
||||
this.space();
|
||||
}
|
||||
if (isField && node.readonly) {
|
||||
this.word("readonly");
|
||||
this.space();
|
||||
}
|
||||
}
|
||||
|
||||
5
packages/babel-generator/test/fixtures/typescript/class-field-declare/input.js
vendored
Normal file
5
packages/babel-generator/test/fixtures/typescript/class-field-declare/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
class A {
|
||||
declare foo;
|
||||
declare bar: string;
|
||||
declare readonly bax: number;
|
||||
}
|
||||
4
packages/babel-generator/test/fixtures/typescript/class-field-declare/options.json
vendored
Normal file
4
packages/babel-generator/test/fixtures/typescript/class-field-declare/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["typescript", "classProperties"]
|
||||
}
|
||||
5
packages/babel-generator/test/fixtures/typescript/class-field-declare/output.js
vendored
Normal file
5
packages/babel-generator/test/fixtures/typescript/class-field-declare/output.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
class A {
|
||||
declare foo;
|
||||
declare bar: string;
|
||||
declare readonly bax: number;
|
||||
}
|
||||
@ -5,6 +5,8 @@ import ReplaceSupers, {
|
||||
import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions";
|
||||
import optimiseCall from "@babel/helper-optimise-call-expression";
|
||||
|
||||
import * as ts from "./typescript";
|
||||
|
||||
export function buildPrivateNamesMap(props) {
|
||||
const privateNamesMap = new Map();
|
||||
for (const prop of props) {
|
||||
@ -556,6 +558,8 @@ export function buildFieldsInitNodes(
|
||||
let needsClassRef = false;
|
||||
|
||||
for (const prop of props) {
|
||||
ts.assertFieldTransformed(prop);
|
||||
|
||||
const isStatic = prop.node.static;
|
||||
const isInstance = !isStatic;
|
||||
const isPrivate = prop.isPrivate();
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
// @flow
|
||||
|
||||
import type { NodePath } from "@babel/traverse";
|
||||
|
||||
export function assertFieldTransformed(path: NodePath) {
|
||||
// TODO (Babel 8): Also check path.node.definite
|
||||
|
||||
if (path.node.declare) {
|
||||
throw path.buildCodeFrameError(
|
||||
`TypeScript 'declare' fields must first be transformed by ` +
|
||||
`@babel/plugin-transform-typescript.\n` +
|
||||
`If you have already enabled that plugin (or '@babel/preset-typescript'), make sure ` +
|
||||
`that it runs before any plugin related to additional class features:\n` +
|
||||
` - @babel/plugin-proposal-class-properties\n` +
|
||||
` - @babel/plugin-proposal-private-methods\n` +
|
||||
` - @babel/plugin-proposal-decorators`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,7 @@ import * as charCodes from "charcodes";
|
||||
type TsModifier =
|
||||
| "readonly"
|
||||
| "abstract"
|
||||
| "declare"
|
||||
| "static"
|
||||
| "public"
|
||||
| "private"
|
||||
@ -129,6 +130,31 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Parses a list of modifiers, in any order.
|
||||
* If you need a specific order, you must call this function multiple times:
|
||||
* this.tsParseModifiers(["public"]);
|
||||
* this.tsParseModifiers(["abstract", "readonly"]);
|
||||
*/
|
||||
tsParseModifiers<T: TsModifier>(
|
||||
allowedModifiers: T[],
|
||||
): { [key: TsModifier]: ?true, __proto__: null } {
|
||||
const modifiers = Object.create(null);
|
||||
|
||||
while (true) {
|
||||
const startPos = this.state.start;
|
||||
const modifier: ?T = this.tsParseModifier(allowedModifiers);
|
||||
|
||||
if (!modifier) break;
|
||||
|
||||
if (Object.hasOwnProperty.call(modifiers, modifier)) {
|
||||
this.raise(startPos, `Duplicate modifier: '${modifier}'`);
|
||||
}
|
||||
modifiers[modifier] = true;
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
tsIsListTerminator(kind: ParsingContext): boolean {
|
||||
switch (kind) {
|
||||
case "EnumMembers":
|
||||
@ -405,7 +431,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return this.eat(tt.name) && this.match(tt.colon);
|
||||
}
|
||||
|
||||
tsTryParseIndexSignature(node: N.TsIndexSignature): ?N.TsIndexSignature {
|
||||
tsTryParseIndexSignature(node: N.Node): ?N.TsIndexSignature {
|
||||
if (
|
||||
!(
|
||||
this.match(tt.bracketL) &&
|
||||
@ -1844,50 +1870,49 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
|
||||
parseClassMemberWithIsStatic(
|
||||
classBody: N.ClassBody,
|
||||
member: any,
|
||||
member: N.ClassMember | N.TsIndexSignature,
|
||||
state: { hadConstructor: boolean },
|
||||
isStatic: boolean,
|
||||
constructorAllowsSuper: boolean,
|
||||
): void {
|
||||
const methodOrProp: N.ClassMethod | N.ClassProperty = member;
|
||||
const prop: N.ClassProperty = member;
|
||||
const propOrIdx: N.ClassProperty | N.TsIndexSignature = member;
|
||||
const modifiers = this.tsParseModifiers([
|
||||
"abstract",
|
||||
"readonly",
|
||||
"declare",
|
||||
]);
|
||||
|
||||
let abstract = false,
|
||||
readonly = false;
|
||||
Object.assign(member, modifiers);
|
||||
|
||||
const mod = this.tsParseModifier(["abstract", "readonly"]);
|
||||
switch (mod) {
|
||||
case "readonly":
|
||||
readonly = true;
|
||||
abstract = !!this.tsParseModifier(["abstract"]);
|
||||
break;
|
||||
case "abstract":
|
||||
abstract = true;
|
||||
readonly = !!this.tsParseModifier(["readonly"]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (abstract) methodOrProp.abstract = true;
|
||||
if (readonly) propOrIdx.readonly = true;
|
||||
|
||||
if (!abstract && !isStatic && !methodOrProp.accessibility) {
|
||||
const idx = this.tsTryParseIndexSignature(member);
|
||||
if (idx) {
|
||||
classBody.body.push(idx);
|
||||
return;
|
||||
|
||||
if (modifiers.abstract) {
|
||||
this.raise(
|
||||
member.start,
|
||||
"Index signatures cannot have the 'abstract' modifier",
|
||||
);
|
||||
}
|
||||
if (isStatic) {
|
||||
this.raise(
|
||||
member.start,
|
||||
"Index signatures cannot have the 'static' modifier",
|
||||
);
|
||||
}
|
||||
if ((member: any).accessibility) {
|
||||
this.raise(
|
||||
member.start,
|
||||
`Index signatures cannot have an accessibility modifier ('${
|
||||
(member: any).accessibility
|
||||
}')`,
|
||||
);
|
||||
}
|
||||
|
||||
if (readonly) {
|
||||
// Must be a property (if not an index signature).
|
||||
methodOrProp.static = isStatic;
|
||||
this.parseClassPropertyName(prop);
|
||||
this.parsePostMemberNameModifiers(methodOrProp);
|
||||
this.pushClassProperty(classBody, prop);
|
||||
return;
|
||||
}
|
||||
|
||||
/*:: invariant(member.type !== "TSIndexSignature") */
|
||||
|
||||
super.parseClassMemberWithIsStatic(
|
||||
classBody,
|
||||
member,
|
||||
@ -1902,6 +1927,20 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
): void {
|
||||
const optional = this.eat(tt.question);
|
||||
if (optional) methodOrProp.optional = true;
|
||||
|
||||
if ((methodOrProp: any).readonly && this.match(tt.parenL)) {
|
||||
this.raise(
|
||||
methodOrProp.start,
|
||||
"Class methods cannot have the 'readonly' modifier",
|
||||
);
|
||||
}
|
||||
|
||||
if ((methodOrProp: any).declare && this.match(tt.parenL)) {
|
||||
this.raise(
|
||||
methodOrProp.start,
|
||||
"Class methods cannot have the 'declare' modifier",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: The reason we do this in `parseExpressionStatement` and not `parseStatement`
|
||||
@ -2048,6 +2087,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
|
||||
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
|
||||
this.parseClassPropertyAnnotation(node);
|
||||
|
||||
if (node.declare && this.match(tt.equal)) {
|
||||
this.raise(
|
||||
this.state.start,
|
||||
"'declare' class fields cannot have an initializer",
|
||||
);
|
||||
}
|
||||
|
||||
return super.parseClassProperty(node);
|
||||
}
|
||||
|
||||
|
||||
@ -743,7 +743,8 @@ export type ClassPrivateMethod = NodeBase &
|
||||
computed: false,
|
||||
};
|
||||
|
||||
export type ClassProperty = ClassMemberBase & {
|
||||
export type ClassProperty = ClassMemberBase &
|
||||
DeclarationBase & {
|
||||
type: "ClassProperty",
|
||||
key: Expression,
|
||||
value: ?Expression, // TODO: Not in spec that this is nullable.
|
||||
|
||||
3
packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/input.ts
vendored
Normal file
3
packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/input.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
declare bar: string = "test";
|
||||
}
|
||||
170
packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/output.json
vendored
Normal file
170
packages/babel-parser/test/fixtures/typescript/class/declare-field-initializer/output.json
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 43,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 43,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassDeclaration",
|
||||
"start": 0,
|
||||
"end": 43,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
},
|
||||
"identifierName": "A"
|
||||
},
|
||||
"name": "A"
|
||||
},
|
||||
"superClass": null,
|
||||
"body": {
|
||||
"type": "ClassBody",
|
||||
"start": 8,
|
||||
"end": 43,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 8
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassProperty",
|
||||
"start": 12,
|
||||
"end": 41,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 31
|
||||
}
|
||||
},
|
||||
"declare": true,
|
||||
"static": false,
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"start": 20,
|
||||
"end": 23,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 13
|
||||
},
|
||||
"identifierName": "bar"
|
||||
},
|
||||
"name": "bar"
|
||||
},
|
||||
"computed": false,
|
||||
"typeAnnotation": {
|
||||
"type": "TSTypeAnnotation",
|
||||
"start": 23,
|
||||
"end": 31,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 13
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 21
|
||||
}
|
||||
},
|
||||
"typeAnnotation": {
|
||||
"type": "TSStringKeyword",
|
||||
"start": 25,
|
||||
"end": 31,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 15
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"type": "StringLiteral",
|
||||
"start": 34,
|
||||
"end": 40,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 24
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 30
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"rawValue": "test",
|
||||
"raw": "\"test\""
|
||||
},
|
||||
"value": "test"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
4
packages/babel-parser/test/fixtures/typescript/class/declare-field/input.ts
vendored
Normal file
4
packages/babel-parser/test/fixtures/typescript/class/declare-field/input.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
declare foo;
|
||||
declare bar: string;
|
||||
}
|
||||
187
packages/babel-parser/test/fixtures/typescript/class/declare-field/output.json
vendored
Normal file
187
packages/babel-parser/test/fixtures/typescript/class/declare-field/output.json
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 49,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 49,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassDeclaration",
|
||||
"start": 0,
|
||||
"end": 49,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
},
|
||||
"identifierName": "A"
|
||||
},
|
||||
"name": "A"
|
||||
},
|
||||
"superClass": null,
|
||||
"body": {
|
||||
"type": "ClassBody",
|
||||
"start": 8,
|
||||
"end": 49,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 8
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassProperty",
|
||||
"start": 12,
|
||||
"end": 24,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 14
|
||||
}
|
||||
},
|
||||
"declare": true,
|
||||
"static": false,
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"start": 20,
|
||||
"end": 23,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 13
|
||||
},
|
||||
"identifierName": "foo"
|
||||
},
|
||||
"name": "foo"
|
||||
},
|
||||
"computed": false,
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"type": "ClassProperty",
|
||||
"start": 27,
|
||||
"end": 47,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 3,
|
||||
"column": 2
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 22
|
||||
}
|
||||
},
|
||||
"declare": true,
|
||||
"static": false,
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"start": 35,
|
||||
"end": 38,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 3,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 13
|
||||
},
|
||||
"identifierName": "bar"
|
||||
},
|
||||
"name": "bar"
|
||||
},
|
||||
"computed": false,
|
||||
"typeAnnotation": {
|
||||
"type": "TSTypeAnnotation",
|
||||
"start": 38,
|
||||
"end": 46,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 3,
|
||||
"column": 13
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 21
|
||||
}
|
||||
},
|
||||
"typeAnnotation": {
|
||||
"type": "TSStringKeyword",
|
||||
"start": 40,
|
||||
"end": 46,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 3,
|
||||
"column": 15
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
3
packages/babel-parser/test/fixtures/typescript/class/declare-method/input.ts
vendored
Normal file
3
packages/babel-parser/test/fixtures/typescript/class/declare-method/input.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
declare foo() {}
|
||||
}
|
||||
145
packages/babel-parser/test/fixtures/typescript/class/declare-method/output.json
vendored
Normal file
145
packages/babel-parser/test/fixtures/typescript/class/declare-method/output.json
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 30,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
"SyntaxError: Class methods cannot have the 'declare' modifier (2:2)"
|
||||
],
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 30,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassDeclaration",
|
||||
"start": 0,
|
||||
"end": 30,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
},
|
||||
"identifierName": "A"
|
||||
},
|
||||
"name": "A"
|
||||
},
|
||||
"superClass": null,
|
||||
"body": {
|
||||
"type": "ClassBody",
|
||||
"start": 8,
|
||||
"end": 30,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 8
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassMethod",
|
||||
"start": 12,
|
||||
"end": 28,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 18
|
||||
}
|
||||
},
|
||||
"declare": true,
|
||||
"static": false,
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"start": 20,
|
||||
"end": 23,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 13
|
||||
},
|
||||
"identifierName": "foo"
|
||||
},
|
||||
"name": "foo"
|
||||
},
|
||||
"computed": false,
|
||||
"kind": "method",
|
||||
"id": null,
|
||||
"generator": false,
|
||||
"async": false,
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "BlockStatement",
|
||||
"start": 26,
|
||||
"end": 28,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 16
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 18
|
||||
}
|
||||
},
|
||||
"body": [],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "Unexpected token, expected \";\" (2:14)"
|
||||
}
|
||||
145
packages/babel-parser/test/fixtures/typescript/class/method-readonly/output.json
vendored
Normal file
145
packages/babel-parser/test/fixtures/typescript/class/method-readonly/output.json
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 31,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
"SyntaxError: Class methods cannot have the 'readonly' modifier (2:4)"
|
||||
],
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 31,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassDeclaration",
|
||||
"start": 0,
|
||||
"end": 31,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
},
|
||||
"identifierName": "C"
|
||||
},
|
||||
"name": "C"
|
||||
},
|
||||
"superClass": null,
|
||||
"body": {
|
||||
"type": "ClassBody",
|
||||
"start": 8,
|
||||
"end": 31,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 8
|
||||
},
|
||||
"end": {
|
||||
"line": 3,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassMethod",
|
||||
"start": 14,
|
||||
"end": 29,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 4
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 19
|
||||
}
|
||||
},
|
||||
"readonly": true,
|
||||
"static": false,
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"start": 23,
|
||||
"end": 24,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 13
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 14
|
||||
},
|
||||
"identifierName": "m"
|
||||
},
|
||||
"name": "m"
|
||||
},
|
||||
"computed": false,
|
||||
"kind": "method",
|
||||
"id": null,
|
||||
"generator": false,
|
||||
"async": false,
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "BlockStatement",
|
||||
"start": 27,
|
||||
"end": 29,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 17
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 19
|
||||
}
|
||||
},
|
||||
"body": [],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassProperty",
|
||||
"type": "ClassPrivateProperty",
|
||||
"start": 12,
|
||||
"end": 24,
|
||||
"loc": {
|
||||
@ -127,7 +127,7 @@
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"type": "ClassProperty",
|
||||
"type": "ClassPrivateProperty",
|
||||
"start": 27,
|
||||
"end": 47,
|
||||
"loc": {
|
||||
|
||||
@ -44,11 +44,94 @@ function registerGlobalType(programScope, name) {
|
||||
}
|
||||
|
||||
export default declare(
|
||||
(api, { jsxPragma = "React", allowNamespaces = false }) => {
|
||||
(
|
||||
api,
|
||||
{
|
||||
jsxPragma = "React",
|
||||
allowNamespaces = false,
|
||||
allowDeclareFields = false,
|
||||
},
|
||||
) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
|
||||
|
||||
const classMemberVisitors = {
|
||||
field(path) {
|
||||
const { node } = path;
|
||||
|
||||
if (!allowDeclareFields && node.declare) {
|
||||
throw path.buildCodeFrameError(
|
||||
`The 'declare' modifier is only allowed when the 'allowDeclareFields' option of ` +
|
||||
`@babel/plugin-transform-typescript or @babel/preset-typescript is enabled.`,
|
||||
);
|
||||
}
|
||||
if (node.definite || node.declare) {
|
||||
if (node.value) {
|
||||
throw path.buildCodeFrameError(
|
||||
`Definietly assigned fields and fields with the 'declare' modifier cannot` +
|
||||
` be initialized here, but only in the constructor`,
|
||||
);
|
||||
}
|
||||
|
||||
path.remove();
|
||||
} else if (!allowDeclareFields && !node.value && !node.decorators) {
|
||||
path.remove();
|
||||
}
|
||||
|
||||
if (node.accessibility) node.accessibility = null;
|
||||
if (node.abstract) node.abstract = null;
|
||||
if (node.readonly) node.readonly = null;
|
||||
if (node.optional) node.optional = null;
|
||||
if (node.typeAnnotation) node.typeAnnotation = null;
|
||||
},
|
||||
method({ node }) {
|
||||
if (node.accessibility) node.accessibility = null;
|
||||
if (node.abstract) node.abstract = null;
|
||||
if (node.optional) node.optional = null;
|
||||
|
||||
// Rest handled by Function visitor
|
||||
},
|
||||
constructor(path, classPath) {
|
||||
// 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 path.node.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 id;
|
||||
if (t.isIdentifier(p)) {
|
||||
id = p;
|
||||
} else if (t.isAssignmentPattern(p) && t.isIdentifier(p.left)) {
|
||||
id = p.left;
|
||||
} else {
|
||||
throw path.buildCodeFrameError(
|
||||
"Parameter properties can not be destructuring patterns.",
|
||||
);
|
||||
}
|
||||
|
||||
return template.statement.ast`this.${id} = ${id}`;
|
||||
});
|
||||
|
||||
injectInitialization(classPath, path, assigns);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
name: "transform-typescript",
|
||||
inherits: syntaxTypeScript,
|
||||
@ -192,27 +275,6 @@ export default declare(
|
||||
if (node.definite) node.definite = null;
|
||||
},
|
||||
|
||||
ClassMethod(path) {
|
||||
const { node } = path;
|
||||
|
||||
if (node.accessibility) node.accessibility = null;
|
||||
if (node.abstract) node.abstract = null;
|
||||
if (node.optional) node.optional = null;
|
||||
|
||||
// Rest handled by Function visitor
|
||||
},
|
||||
|
||||
ClassProperty(path) {
|
||||
const { node } = path;
|
||||
|
||||
if (node.accessibility) node.accessibility = null;
|
||||
if (node.abstract) node.abstract = null;
|
||||
if (node.readonly) node.readonly = null;
|
||||
if (node.optional) node.optional = null;
|
||||
if (node.definite) node.definite = null;
|
||||
if (node.typeAnnotation) node.typeAnnotation = null;
|
||||
},
|
||||
|
||||
TSIndexSignature(path) {
|
||||
path.remove();
|
||||
},
|
||||
@ -238,54 +300,14 @@ export default declare(
|
||||
// class transform would transform the class, causing more specific
|
||||
// visitors to not run.
|
||||
path.get("body.body").forEach(child => {
|
||||
const childNode = child.node;
|
||||
|
||||
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 id;
|
||||
if (t.isIdentifier(p)) {
|
||||
id = p;
|
||||
} else if (
|
||||
t.isAssignmentPattern(p) &&
|
||||
t.isIdentifier(p.left)
|
||||
) {
|
||||
id = p.left;
|
||||
if (child.isClassMethod()) {
|
||||
if (child.node.kind === "constructor") {
|
||||
classMemberVisitors.constructor(child, path);
|
||||
} else {
|
||||
throw path.buildCodeFrameError(
|
||||
"Parameter properties can not be destructuring patterns.",
|
||||
);
|
||||
}
|
||||
|
||||
return template.statement.ast`this.${id} = ${id}`;
|
||||
});
|
||||
|
||||
injectInitialization(path, child, assigns);
|
||||
classMemberVisitors.method(child, path);
|
||||
}
|
||||
} else if (child.isClassProperty()) {
|
||||
childNode.typeAnnotation = null;
|
||||
|
||||
if (!childNode.value && !childNode.decorators) {
|
||||
child.remove();
|
||||
}
|
||||
classMemberVisitors.field(child, path);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
3
packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/input.ts
vendored
Normal file
3
packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/input.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
declare x;
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"plugins": ["transform-typescript"],
|
||||
"throws": "The 'declare' modifier is only allowed when the 'allowDeclareFields' option of @babel/plugin-transform-typescript or @babel/preset-typescript is enabled."
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
x;
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": [["transform-typescript", { "allowDeclareFields": true }]]
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
x;
|
||||
}
|
||||
3
packages/babel-plugin-transform-typescript/test/fixtures/class/declare/input.ts
vendored
Normal file
3
packages/babel-plugin-transform-typescript/test/fixtures/class/declare/input.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
declare x;
|
||||
}
|
||||
3
packages/babel-plugin-transform-typescript/test/fixtures/class/declare/options.json
vendored
Normal file
3
packages/babel-plugin-transform-typescript/test/fixtures/class/declare/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": [["transform-typescript", { "allowDeclareFields": true }]]
|
||||
}
|
||||
1
packages/babel-plugin-transform-typescript/test/fixtures/class/declare/output.js
vendored
Normal file
1
packages/babel-plugin-transform-typescript/test/fixtures/class/declare/output.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
class A {}
|
||||
@ -1,7 +1,8 @@
|
||||
class C {
|
||||
public a?: number;
|
||||
private b: number = 0;
|
||||
readonly c!: number = 1;
|
||||
readonly c: number = 1;
|
||||
@foo d: number;
|
||||
@foo e: number = 3;
|
||||
f!: number;
|
||||
}
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
declare x;
|
||||
y;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"plugins": [
|
||||
"proposal-class-properties",
|
||||
["transform-typescript", { "allowDeclareFields": true }]
|
||||
],
|
||||
"throws": "TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript.\nIf you have already enabled that plugin (or '@babel/preset-typescript'), make sure that it runs before any plugin related to additional class features:\n - @babel/plugin-proposal-class-properties\n - @babel/plugin-proposal-private-methods\n - @babel/plugin-proposal-decorators"
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
declare x;
|
||||
y;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
["transform-typescript", { "allowDeclareFields": true }],
|
||||
"proposal-class-properties"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
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 A {
|
||||
constructor() {
|
||||
_defineProperty(this, "y", void 0);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
class A {
|
||||
x!;
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": [["transform-typescript", { "allowDeclareFields": true }]]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
class A {}
|
||||
@ -4,7 +4,13 @@ import transformTypeScript from "@babel/plugin-transform-typescript";
|
||||
export default declare(
|
||||
(
|
||||
api,
|
||||
{ jsxPragma, allExtensions = false, isTSX = false, allowNamespaces },
|
||||
{
|
||||
jsxPragma,
|
||||
allExtensions = false,
|
||||
isTSX = false,
|
||||
allowNamespaces,
|
||||
allowDeclareFields,
|
||||
},
|
||||
) => {
|
||||
api.assertVersion(7);
|
||||
|
||||
@ -19,13 +25,18 @@ export default declare(
|
||||
throw new Error("isTSX:true requires allExtensions:true");
|
||||
}
|
||||
|
||||
const pluginOptions = isTSX => ({
|
||||
jsxPragma,
|
||||
isTSX,
|
||||
allowNamespaces,
|
||||
allowDeclareFields,
|
||||
});
|
||||
|
||||
return {
|
||||
overrides: allExtensions
|
||||
? [
|
||||
{
|
||||
plugins: [
|
||||
[transformTypeScript, { jsxPragma, isTSX, allowNamespaces }],
|
||||
],
|
||||
plugins: [[transformTypeScript, pluginOptions(isTSX)]],
|
||||
},
|
||||
]
|
||||
: [
|
||||
@ -33,18 +44,13 @@ export default declare(
|
||||
// Only set 'test' if explicitly requested, since it requires that
|
||||
// Babel is being called`
|
||||
test: /\.ts$/,
|
||||
plugins: [[transformTypeScript, { jsxPragma, allowNamespaces }]],
|
||||
plugins: [[transformTypeScript, pluginOptions(false)]],
|
||||
},
|
||||
{
|
||||
// Only set 'test' if explicitly requested, since it requires that
|
||||
// Babel is being called`
|
||||
test: /\.tsx$/,
|
||||
plugins: [
|
||||
[
|
||||
transformTypeScript,
|
||||
{ jsxPragma, isTSX: true, allowNamespaces },
|
||||
],
|
||||
],
|
||||
plugins: [[transformTypeScript, pluginOptions(true)]],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -67,6 +67,10 @@ defineType("ClassProperty", {
|
||||
validate: assertValueType("boolean"),
|
||||
optional: true,
|
||||
},
|
||||
declare: {
|
||||
validate: assertValueType("boolean"),
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user