From 6a73f391991d969ea4002512e82f8153be9c65ba Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 21 Dec 2017 07:13:31 -0800 Subject: [PATCH] Support parsing `export default abstract class {}` (#7075) --- .../typescript/class-abstract/actual.js | 3 +- .../typescript/class-abstract/expected.js | 5 +- .../typescript/class-abstract/options.json | 2 +- .../class/export-default-abstract/actual.js | 1 + .../class/export-default-abstract/expected.js | 1 + packages/babylon/src/parser/statement.js | 42 ++-- packages/babylon/src/plugins/typescript.js | 14 ++ .../class/abstract-false-positive/actual.js | 2 + .../abstract-false-positive/expected.json | 103 +++++++++ .../typescript/class/abstract/actual.js | 5 +- .../typescript/class/abstract/expected.json | 207 +++++++++++++----- 11 files changed, 309 insertions(+), 76 deletions(-) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/actual.js create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/expected.js create mode 100644 packages/babylon/test/fixtures/typescript/class/abstract-false-positive/actual.js create mode 100644 packages/babylon/test/fixtures/typescript/class/abstract-false-positive/expected.json diff --git a/packages/babel-generator/test/fixtures/typescript/class-abstract/actual.js b/packages/babel-generator/test/fixtures/typescript/class-abstract/actual.js index 97bbd960bc..8f41a8b71f 100644 --- a/packages/babel-generator/test/fixtures/typescript/class-abstract/actual.js +++ b/packages/babel-generator/test/fixtures/typescript/class-abstract/actual.js @@ -2,5 +2,6 @@ abstract class C {} declare abstract class C {} export abstract class C {} // `export abstract class { }` is not valid. -// `export default abstract class C { }` is not valid. +export default abstract class { } +export default abstract class C { } // `abstract class` is not valid as an expression. diff --git a/packages/babel-generator/test/fixtures/typescript/class-abstract/expected.js b/packages/babel-generator/test/fixtures/typescript/class-abstract/expected.js index 63db4cfa6c..860e03a34d 100644 --- a/packages/babel-generator/test/fixtures/typescript/class-abstract/expected.js +++ b/packages/babel-generator/test/fixtures/typescript/class-abstract/expected.js @@ -3,5 +3,6 @@ abstract class C {} declare abstract class C {} export abstract class C {} // `export abstract class { }` is not valid. -// `export default abstract class C { }` is not valid. -// `abstract class` is not valid as an expression. \ No newline at end of file + +export default abstract class {} +export default abstract class C {} // `abstract class` is not valid as an expression. \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/typescript/class-abstract/options.json b/packages/babel-generator/test/fixtures/typescript/class-abstract/options.json index 9f3a0c2c0f..fe9bffaa5e 100644 --- a/packages/babel-generator/test/fixtures/typescript/class-abstract/options.json +++ b/packages/babel-generator/test/fixtures/typescript/class-abstract/options.json @@ -1,4 +1,4 @@ { "sourceType": "module", - "plugins": ["typescript", "classProperties"] + "plugins": ["typescript"] } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/actual.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/actual.js new file mode 100644 index 0000000000..d84e173be3 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/actual.js @@ -0,0 +1 @@ +export default abstract class {} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/expected.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/expected.js new file mode 100644 index 0000000000..a6e68e9838 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/export-default-abstract/expected.js @@ -0,0 +1 @@ +export default class {} diff --git a/packages/babylon/src/parser/statement.js b/packages/babylon/src/parser/statement.js index eb9465598a..39f112595d 100644 --- a/packages/babylon/src/parser/statement.js +++ b/packages/babylon/src/parser/statement.js @@ -1328,26 +1328,7 @@ export default class StatementParser extends ExpressionParser { this.parseExportFrom(node, true); } else if (this.eat(tt._default)) { // export default ... - let expr = this.startNode(); - let needsSemi = false; - if (this.eat(tt._function)) { - expr = this.parseFunction(expr, true, false, false, true); - } else if ( - this.isContextual("async") && - this.lookahead().type === tt._function - ) { - // async function declaration - this.eatContextual("async"); - this.eat(tt._function); - expr = this.parseFunction(expr, true, false, true, true); - } else if (this.match(tt._class)) { - expr = this.parseClass(expr, true, true); - } else { - needsSemi = true; - expr = this.parseMaybeAssign(); - } - node.declaration = expr; - if (needsSemi) this.semicolon(); + node.declaration = this.parseExportDefaultExpression(); this.checkExport(node, true, true); return this.finishNode(node, "ExportDefaultDeclaration"); } else if (this.shouldParseExportDeclaration()) { @@ -1373,6 +1354,27 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "ExportNamedDeclaration"); } + parseExportDefaultExpression(): N.Expression | N.Declaration { + const expr = this.startNode(); + if (this.eat(tt._function)) { + return this.parseFunction(expr, true, false, false, true); + } else if ( + this.isContextual("async") && + this.lookahead().type === tt._function + ) { + // async function declaration + this.eatContextual("async"); + this.eat(tt._function); + return this.parseFunction(expr, true, false, true, true); + } else if (this.match(tt._class)) { + return this.parseClass(expr, true, true); + } else { + const res = this.parseMaybeAssign(); + this.semicolon(); + return res; + } + } + // eslint-disable-next-line no-unused-vars parseExportDeclaration(node: N.ExportNamedDeclaration): ?N.Declaration { return this.parseStatement(true); diff --git a/packages/babylon/src/plugins/typescript.js b/packages/babylon/src/plugins/typescript.js index cd9e7fc353..586681e49c 100644 --- a/packages/babylon/src/plugins/typescript.js +++ b/packages/babylon/src/plugins/typescript.js @@ -1426,6 +1426,20 @@ export default (superClass: Class): Class => } } + parseExportDefaultExpression(): N.Expression | N.Declaration { + if ( + this.isContextual("abstract") && + this.lookahead().type === tt._class + ) { + const cls = this.startNode(); + this.next(); // Skip "abstract" + this.parseClass(cls, true, true); + cls.abstract = true; + return cls; + } + return super.parseExportDefaultExpression(); + } + parseStatementContent( declaration: boolean, topLevel: ?boolean, diff --git a/packages/babylon/test/fixtures/typescript/class/abstract-false-positive/actual.js b/packages/babylon/test/fixtures/typescript/class/abstract-false-positive/actual.js new file mode 100644 index 0000000000..c75018145f --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/class/abstract-false-positive/actual.js @@ -0,0 +1,2 @@ +// Exports an identifier, doesn't try parsing `abstract class` +export default abstract; diff --git a/packages/babylon/test/fixtures/typescript/class/abstract-false-positive/expected.json b/packages/babylon/test/fixtures/typescript/class/abstract-false-positive/expected.json new file mode 100644 index 0000000000..087b254519 --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/class/abstract-false-positive/expected.json @@ -0,0 +1,103 @@ +{ + "type": "File", + "start": 0, + "end": 87, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 24 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 87, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 24 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExportDefaultDeclaration", + "start": 63, + "end": 87, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 24 + } + }, + "declaration": { + "type": "Identifier", + "start": 78, + "end": 86, + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 23 + }, + "identifierName": "abstract" + }, + "name": "abstract", + "leadingComments": null + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " Exports an identifier, doesn't try parsing `abstract class`", + "start": 0, + "end": 62, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 62 + } + } + } + ] + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " Exports an identifier, doesn't try parsing `abstract class`", + "start": 0, + "end": 62, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 62 + } + } + } + ] +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/typescript/class/abstract/actual.js b/packages/babylon/test/fixtures/typescript/class/abstract/actual.js index 97bbd960bc..c0a5ad4b67 100644 --- a/packages/babylon/test/fixtures/typescript/class/abstract/actual.js +++ b/packages/babylon/test/fixtures/typescript/class/abstract/actual.js @@ -1,6 +1,7 @@ abstract class C {} declare abstract class C {} export abstract class C {} -// `export abstract class { }` is not valid. -// `export default abstract class C { }` is not valid. +// `export abstract class {}` is not valid TypeScript. +export default abstract class { } +export default abstract class C { } // `abstract class` is not valid as an expression. diff --git a/packages/babylon/test/fixtures/typescript/class/abstract/expected.json b/packages/babylon/test/fixtures/typescript/class/abstract/expected.json index 0c084659fb..081ab1355e 100644 --- a/packages/babylon/test/fixtures/typescript/class/abstract/expected.json +++ b/packages/babylon/test/fixtures/typescript/class/abstract/expected.json @@ -1,28 +1,28 @@ { "type": "File", "start": 0, - "end": 225, + "end": 250, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 6, + "line": 7, "column": 50 } }, "program": { "type": "Program", "start": 0, - "end": 225, + "end": 250, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 6, + "line": 7, "column": 50 } }, @@ -201,9 +201,9 @@ "trailingComments": [ { "type": "CommentLine", - "value": " `export abstract class { }` is not valid.", + "value": " `export abstract class {}` is not valid TypeScript.", "start": 75, - "end": 119, + "end": 129, "loc": { "start": { "line": 4, @@ -211,38 +211,161 @@ }, "end": { "line": 4, - "column": 44 - } - } - }, - { - "type": "CommentLine", - "value": " `export default abstract class C { }` is not valid.", - "start": 120, - "end": 174, - "loc": { - "start": { - "line": 5, - "column": 0 - }, - "end": { - "line": 5, "column": 54 } } + } + ] + }, + { + "type": "ExportDefaultDeclaration", + "start": 130, + "end": 163, + "loc": { + "start": { + "line": 5, + "column": 0 }, - { - "type": "CommentLine", - "value": " `abstract class` is not valid as an expression.", - "start": 175, - "end": 225, + "end": { + "line": 5, + "column": 33 + } + }, + "declaration": { + "type": "ClassDeclaration", + "start": 145, + "end": 163, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 33 + } + }, + "id": null, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 160, + "end": 163, "loc": { "start": { - "line": 6, + "line": 5, + "column": 30 + }, + "end": { + "line": 5, + "column": 33 + } + }, + "body": [], + "leadingComments": null + }, + "leadingComments": null, + "abstract": true + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " `export abstract class {}` is not valid TypeScript.", + "start": 75, + "end": 129, + "loc": { + "start": { + "line": 4, "column": 0 }, "end": { + "line": 4, + "column": 54 + } + } + } + ] + }, + { + "type": "ExportDefaultDeclaration", + "start": 164, + "end": 199, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 35 + } + }, + "declaration": { + "type": "ClassDeclaration", + "start": 179, + "end": 199, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 35 + } + }, + "id": { + "type": "Identifier", + "start": 194, + "end": 195, + "loc": { + "start": { "line": 6, + "column": 30 + }, + "end": { + "line": 6, + "column": 31 + }, + "identifierName": "C" + }, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 196, + "end": 199, + "loc": { + "start": { + "line": 6, + "column": 32 + }, + "end": { + "line": 6, + "column": 35 + } + }, + "body": [], + "leadingComments": null, + "trailingComments": null + }, + "trailingComments": null, + "abstract": true + }, + "trailingComments": [ + { + "type": "CommentLine", + "value": " `abstract class` is not valid as an expression.", + "start": 200, + "end": 250, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, "column": 50 } } @@ -255,9 +378,9 @@ "comments": [ { "type": "CommentLine", - "value": " `export abstract class { }` is not valid.", + "value": " `export abstract class {}` is not valid TypeScript.", "start": 75, - "end": 119, + "end": 129, "loc": { "start": { "line": 4, @@ -265,22 +388,6 @@ }, "end": { "line": 4, - "column": 44 - } - } - }, - { - "type": "CommentLine", - "value": " `export default abstract class C { }` is not valid.", - "start": 120, - "end": 174, - "loc": { - "start": { - "line": 5, - "column": 0 - }, - "end": { - "line": 5, "column": 54 } } @@ -288,15 +395,15 @@ { "type": "CommentLine", "value": " `abstract class` is not valid as an expression.", - "start": 175, - "end": 225, + "start": 200, + "end": 250, "loc": { "start": { - "line": 6, + "line": 7, "column": 0 }, "end": { - "line": 6, + "line": 7, "column": 50 } }