Skip to content

Commit

Permalink
Fix handling of comments with decorators before export
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Oct 10, 2022
1 parent c9ddd3c commit 792f629
Show file tree
Hide file tree
Showing 19 changed files with 609 additions and 7 deletions.
@@ -0,0 +1,2 @@
/* 1 */ export /* 2 */ @dec1 /* 3 */ @dec2
/* 4 */ class /* 5 */ C /* 6 */ { /* 7 */ } /* 8 */
@@ -0,0 +1,4 @@
{
"plugins": [["decorators", { "decoratorsBeforeExport": false }]],
"decoratorsBeforeExport": false
}
@@ -0,0 +1,3 @@
/* 1 */export /* 2 */@dec1
/* 3 */@dec2
/* 4 */class /* 5 */C /* 6 */ {/* 7 */} /* 8 */
@@ -0,0 +1,2 @@
/* 1 */ @dec1 /* 2 */ @dec2 /* 3 */
export /* 4 */ class /* 5 */ C /* 6 */ { /* 7 */ } /* 8 */
@@ -0,0 +1,4 @@
{
"plugins": [["decorators", { "decoratorsBeforeExport": true }]],
"decoratorsBeforeExport": true
}
@@ -0,0 +1,3 @@
/* 1 */@dec1
/* 2 */@dec2
/* 3 */export /* 4 */class /* 5 */C /* 6 */ {/* 7 */} /* 8 */
@@ -0,0 +1,2 @@
/* 1 */ @dec1 /* 2 */ @dec2 /* 3 */
export /* 4 */ class /* 5 */ C /* 6 */ { /* 7 */ } /* 8 */
@@ -0,0 +1,4 @@
{
"plugins": ["decorators-legacy"],
"decoratorsBeforeExport": true
}
@@ -0,0 +1,3 @@
/* 1 */@dec1
/* 2 */@dec2
/* 3 */export /* 4 */class /* 5 */C /* 6 */ {/* 7 */} /* 8 */
67 changes: 60 additions & 7 deletions packages/babel-parser/src/parser/statement.ts
Expand Up @@ -522,13 +522,49 @@ export default abstract class StatementParser extends ExpressionParser {
}
}

decoratorsEnabledBeforeExport(): boolean {
if (this.hasPlugin("decorators-legacy")) return true;
return (
this.hasPlugin("decorators") &&
!!this.getPluginOption("decorators", "decoratorsBeforeExport")
);
}

takeDecorators(node: N.HasDecorators): void {
const decorators =
this.state.decoratorStack[this.state.decoratorStack.length - 1];
if (decorators.length) {
node.decorators = decorators;
this.resetStartLocationFromNode(node, decorators[0]);
this.state.decoratorStack[this.state.decoratorStack.length - 1] = [];

// In case of decorators followed by `export`, we manually mark the
// comments between `export` and `class` as leading comments of the
// class declaration node.
// This is needed because the class location range overlaps with
// `export`, and thus they would me marked as inner comments of
// the class declaration.
// @dec export /* comment */ class Foo {}
// See [parseDecorators] for the hanling of comments before decorators
if (this.decoratorsEnabledBeforeExport()) {
const {
start,
lastTokStart,
lastTokEndLoc: { index: lastTokEnd },
} = this.state;

if (this.input.slice(lastTokStart, lastTokEnd) === "export") {
const { commentStack } = this.state;
for (let i = commentStack.length - 1; i >= 0; i--) {
const commentWS = commentStack[i];
if (commentWS.start >= lastTokEnd && commentWS.end <= start) {
commentWS.trailingNode = node as N.Node;
} else if (commentWS.end < lastTokEnd) {
break;
}
}
}
}
}
}

Expand All @@ -537,8 +573,9 @@ export default abstract class StatementParser extends ExpressionParser {
}

parseDecorators(this: Parser, allowExport?: boolean): void {
const currentContextDecorators =
this.state.decoratorStack[this.state.decoratorStack.length - 1];
const { decoratorStack } = this.state;
const currentContextDecorators = decoratorStack[decoratorStack.length - 1];

while (this.match(tt.at)) {
const decorator = this.parseDecorator();
currentContextDecorators.push(decorator);
Expand All @@ -549,12 +586,27 @@ export default abstract class StatementParser extends ExpressionParser {
this.unexpected();
}

if (
this.hasPlugin("decorators") &&
!this.getPluginOption("decorators", "decoratorsBeforeExport")
) {
if (!this.decoratorsEnabledBeforeExport()) {
this.raise(Errors.DecoratorExportClass, { at: this.state.startLoc });
}

// In case of comments followed by a decorator followed by `export`,
// we manually attach them to the decorator node rather than to the
// class declaration node. This is because the class location range
// overlaps with `export`, and this causes confusing comments behavior:
// /* comment */ @dec export class Foo {}
// See [takeDecorators] for the hanling of comments after `export`
const firstDecorator = decoratorStack[decoratorStack.length - 1][0];
const { commentStack } = this.state;
for (let i = commentStack.length - 1; i >= 0; i--) {
const commentWS = commentStack[i];
if (commentWS.trailingNode === firstDecorator) {
this.finalizeComment(commentWS);
commentStack.splice(i, 1);
} else if (commentWS.end < firstDecorator.start) {
break;
}
}
} else if (!this.canHaveLeadingDecorator()) {
throw this.raise(Errors.UnexpectedLeadingDecorator, {
at: this.state.startLoc,
Expand Down Expand Up @@ -1472,9 +1524,10 @@ export default abstract class StatementParser extends ExpressionParser {
isStatement: /* T === ClassDeclaration */ boolean,
optionalId?: boolean,
): T {
this.next();
this.takeDecorators(node);

this.next(); // 'class'

// A class definition is always strict mode code.
const oldStrict = this.state.strict;
this.state.strict = true;
Expand Down
@@ -0,0 +1,2 @@
/* 1 */ export /* 2 */ @dec1 /* 3 */ @dec2
/* 4 */ class /* 5 */ C /* 6 */ { /* 7 */ } /* 8 */
@@ -0,0 +1,3 @@
{
"plugins": [["decorators", { "decoratorsBeforeExport": false }]]
}
@@ -0,0 +1,169 @@
{
"type": "File",
"start":0,"end":94,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":51,"index":94}},
"errors": [
"SyntaxError: 'import' and 'export' may appear only with 'sourceType: \"module\"' (1:8)"
],
"program": {
"type": "Program",
"start":0,"end":94,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":51,"index":94}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExportNamedDeclaration",
"start":8,"end":86,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":2,"column":43,"index":86}},
"specifiers": [],
"source": null,
"declaration": {
"type": "ClassDeclaration",
"start":23,"end":86,"loc":{"start":{"line":1,"column":23,"index":23},"end":{"line":2,"column":43,"index":86}},
"decorators": [
{
"type": "Decorator",
"start":23,"end":28,"loc":{"start":{"line":1,"column":23,"index":23},"end":{"line":1,"column":28,"index":28}},
"expression": {
"type": "Identifier",
"start":24,"end":28,"loc":{"start":{"line":1,"column":24,"index":24},"end":{"line":1,"column":28,"index":28},"identifierName":"dec1"},
"name": "dec1"
},
"trailingComments": [
{
"type": "CommentBlock",
"value": " 3 ",
"start":29,"end":36,"loc":{"start":{"line":1,"column":29,"index":29},"end":{"line":1,"column":36,"index":36}}
}
]
},
{
"type": "Decorator",
"start":37,"end":42,"loc":{"start":{"line":1,"column":37,"index":37},"end":{"line":1,"column":42,"index":42}},
"expression": {
"type": "Identifier",
"start":38,"end":42,"loc":{"start":{"line":1,"column":38,"index":38},"end":{"line":1,"column":42,"index":42},"identifierName":"dec2"},
"name": "dec2"
},
"trailingComments": [
{
"type": "CommentBlock",
"value": " 4 ",
"start":43,"end":50,"loc":{"start":{"line":2,"column":0,"index":43},"end":{"line":2,"column":7,"index":50}}
}
],
"leadingComments": [
{
"type": "CommentBlock",
"value": " 3 ",
"start":29,"end":36,"loc":{"start":{"line":1,"column":29,"index":29},"end":{"line":1,"column":36,"index":36}}
}
]
}
],
"id": {
"type": "Identifier",
"start":65,"end":66,"loc":{"start":{"line":2,"column":22,"index":65},"end":{"line":2,"column":23,"index":66},"identifierName":"C"},
"name": "C",
"trailingComments": [
{
"type": "CommentBlock",
"value": " 6 ",
"start":67,"end":74,"loc":{"start":{"line":2,"column":24,"index":67},"end":{"line":2,"column":31,"index":74}}
}
],
"leadingComments": [
{
"type": "CommentBlock",
"value": " 5 ",
"start":57,"end":64,"loc":{"start":{"line":2,"column":14,"index":57},"end":{"line":2,"column":21,"index":64}}
}
]
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":75,"end":86,"loc":{"start":{"line":2,"column":32,"index":75},"end":{"line":2,"column":43,"index":86}},
"body": [],
"innerComments": [
{
"type": "CommentBlock",
"value": " 7 ",
"start":77,"end":84,"loc":{"start":{"line":2,"column":34,"index":77},"end":{"line":2,"column":41,"index":84}}
}
],
"leadingComments": [
{
"type": "CommentBlock",
"value": " 6 ",
"start":67,"end":74,"loc":{"start":{"line":2,"column":24,"index":67},"end":{"line":2,"column":31,"index":74}}
}
]
},
"leadingComments": [
{
"type": "CommentBlock",
"value": " 2 ",
"start":15,"end":22,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":22,"index":22}}
}
]
},
"trailingComments": [
{
"type": "CommentBlock",
"value": " 8 ",
"start":87,"end":94,"loc":{"start":{"line":2,"column":44,"index":87},"end":{"line":2,"column":51,"index":94}}
}
],
"leadingComments": [
{
"type": "CommentBlock",
"value": " 1 ",
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}
}
]
}
],
"directives": []
},
"comments": [
{
"type": "CommentBlock",
"value": " 1 ",
"start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}
},
{
"type": "CommentBlock",
"value": " 2 ",
"start":15,"end":22,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":22,"index":22}}
},
{
"type": "CommentBlock",
"value": " 3 ",
"start":29,"end":36,"loc":{"start":{"line":1,"column":29,"index":29},"end":{"line":1,"column":36,"index":36}}
},
{
"type": "CommentBlock",
"value": " 4 ",
"start":43,"end":50,"loc":{"start":{"line":2,"column":0,"index":43},"end":{"line":2,"column":7,"index":50}}
},
{
"type": "CommentBlock",
"value": " 5 ",
"start":57,"end":64,"loc":{"start":{"line":2,"column":14,"index":57},"end":{"line":2,"column":21,"index":64}}
},
{
"type": "CommentBlock",
"value": " 6 ",
"start":67,"end":74,"loc":{"start":{"line":2,"column":24,"index":67},"end":{"line":2,"column":31,"index":74}}
},
{
"type": "CommentBlock",
"value": " 7 ",
"start":77,"end":84,"loc":{"start":{"line":2,"column":34,"index":77},"end":{"line":2,"column":41,"index":84}}
},
{
"type": "CommentBlock",
"value": " 8 ",
"start":87,"end":94,"loc":{"start":{"line":2,"column":44,"index":87},"end":{"line":2,"column":51,"index":94}}
}
]
}
@@ -0,0 +1,2 @@
/* 1 */ @dec1 /* 2 */ @dec2 /* 3 */
export /* 4 */ class /* 5 */ C /* 6 */ { /* 7 */ } /* 8 */
@@ -0,0 +1,3 @@
{
"plugins": [["decorators", { "decoratorsBeforeExport": true }]]
}

0 comments on commit 792f629

Please sign in to comment.