diff --git a/packages/babylon/src/parser/statement.js b/packages/babylon/src/parser/statement.js index 8d802f5c93..82bd72dcd4 100644 --- a/packages/babylon/src/parser/statement.js +++ b/packages/babylon/src/parser/statement.js @@ -225,6 +225,10 @@ export default class StatementParser extends ExpressionParser { } } + canHaveLeadingDecorator(): boolean { + return this.match(tt._class); + } + parseDecorators(allowExport?: boolean): void { if (this.hasPlugin("decorators2")) { allowExport = false; @@ -250,7 +254,7 @@ export default class StatementParser extends ExpressionParser { } } - if (!this.match(tt._class)) { + if (!this.canHaveLeadingDecorator()) { this.raise( this.state.start, "Leading decorators must be attached to a class declaration", diff --git a/packages/babylon/src/plugins/typescript.js b/packages/babylon/src/plugins/typescript.js index c623dbb554..f1473c5d72 100644 --- a/packages/babylon/src/plugins/typescript.js +++ b/packages/babylon/src/plugins/typescript.js @@ -1489,11 +1489,14 @@ export default (superClass: Class): Class => } } + isAbstractClass(): boolean { + return ( + this.isContextual("abstract") && this.lookahead().type === tt._class + ); + } + parseExportDefaultExpression(): N.Expression | N.Declaration { - if ( - this.isContextual("abstract") && - this.lookahead().type === tt._class - ) { + if (this.isAbstractClass()) { const cls = this.startNode(); this.next(); // Skip "abstract" this.parseClass(cls, true, true); @@ -2084,4 +2087,9 @@ export default (superClass: Class): Class => shouldParseAsyncArrow(): boolean { return this.match(tt.colon) || super.shouldParseAsyncArrow(); } + + canHaveLeadingDecorator() { + // Avoid unnecessary lookahead in checking for abstract class unless needed! + return super.canHaveLeadingDecorator() || this.isAbstractClass(); + } }; diff --git a/packages/babylon/test/fixtures/typescript/decorators/abstract-class/input.js b/packages/babylon/test/fixtures/typescript/decorators/abstract-class/input.js new file mode 100644 index 0000000000..ef03c08991 --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/decorators/abstract-class/input.js @@ -0,0 +1,6 @@ +@decorate() +abstract class Test extends Object { + constructor() { + super(); + } +} diff --git a/packages/babylon/test/fixtures/typescript/decorators/abstract-class/options.json b/packages/babylon/test/fixtures/typescript/decorators/abstract-class/options.json new file mode 100644 index 0000000000..f6d2d63c2d --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/decorators/abstract-class/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["decorators", "typescript"] +} diff --git a/packages/babylon/test/fixtures/typescript/decorators/abstract-class/output.json b/packages/babylon/test/fixtures/typescript/decorators/abstract-class/output.json new file mode 100644 index 0000000000..f0c716d0db --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/decorators/abstract-class/output.json @@ -0,0 +1,254 @@ +{ + "type": "File", + "start": 0, + "end": 85, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 85, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 1 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 85, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 1 + } + }, + "abstract": true, + "decorators": [ + { + "type": "Decorator", + "start": 0, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "callee": { + "type": "CallExpression", + "start": 1, + "end": 11, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 11 + } + }, + "callee": { + "type": "Identifier", + "start": 1, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + }, + "identifierName": "decorate" + }, + "name": "decorate" + }, + "arguments": [] + } + } + ], + "id": { + "type": "Identifier", + "start": 27, + "end": 31, + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 19 + }, + "identifierName": "Test" + }, + "name": "Test" + }, + "superClass": { + "type": "Identifier", + "start": 40, + "end": 46, + "loc": { + "start": { + "line": 2, + "column": 28 + }, + "end": { + "line": 2, + "column": 34 + }, + "identifierName": "Object" + }, + "name": "Object" + }, + "body": { + "type": "ClassBody", + "start": 47, + "end": 85, + "loc": { + "start": { + "line": 2, + "column": 35 + }, + "end": { + "line": 6, + "column": 1 + } + }, + "body": [ + { + "type": "ClassMethod", + "start": 51, + "end": 83, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 5, + "column": 3 + } + }, + "static": false, + "key": { + "type": "Identifier", + "start": 51, + "end": 62, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 13 + }, + "identifierName": "constructor" + }, + "name": "constructor" + }, + "computed": false, + "kind": "constructor", + "id": null, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 65, + "end": 83, + "loc": { + "start": { + "line": 3, + "column": 16 + }, + "end": { + "line": 5, + "column": 3 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 71, + "end": 79, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 12 + } + }, + "expression": { + "type": "CallExpression", + "start": 71, + "end": 78, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 11 + } + }, + "callee": { + "type": "Super", + "start": 71, + "end": 76, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 9 + } + } + }, + "arguments": [] + } + } + ], + "directives": [] + } + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file