Update decorators parsing (#7719)
* Update decorators parsing
This commit introduces three changes:
1) Class properties can be decorated
2) Decorators can contain arbitrary expressions, using @(...)
3) The Decorator node type has a new property, "arguments". This
makes it possible do distinguish @dec() and @(dec()), which have
different behaviors because @(dec()) is equivalent to @(dec())().
* Rename Decorator#expression to Decorator#callee
* Add test for @dec()()
This commit is contained in:
@@ -265,33 +265,37 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.next();
|
||||
|
||||
if (this.hasPlugin("decorators2")) {
|
||||
const startPos = this.state.start;
|
||||
const startLoc = this.state.startLoc;
|
||||
let expr = this.parseIdentifier(false);
|
||||
// Every time a decorator class expression is evaluated, a new empty array is pushed onto the stack
|
||||
// So that the decorators of any nested class expressions will be dealt with separately
|
||||
this.state.decoratorStack.push([]);
|
||||
|
||||
while (this.eat(tt.dot)) {
|
||||
const node = this.startNodeAt(startPos, startLoc);
|
||||
node.object = expr;
|
||||
node.property = this.parseIdentifier(true);
|
||||
node.computed = false;
|
||||
expr = this.finishNode(node, "MemberExpression");
|
||||
if (this.eat(tt.parenL)) {
|
||||
node.callee = this.parseExpression();
|
||||
this.expect(tt.parenR);
|
||||
} else {
|
||||
const startPos = this.state.start;
|
||||
const startLoc = this.state.startLoc;
|
||||
let expr = this.parseIdentifier(false);
|
||||
|
||||
while (this.eat(tt.dot)) {
|
||||
const node = this.startNodeAt(startPos, startLoc);
|
||||
node.object = expr;
|
||||
node.property = this.parseIdentifier(true);
|
||||
node.computed = false;
|
||||
expr = this.finishNode(node, "MemberExpression");
|
||||
}
|
||||
|
||||
node.callee = expr;
|
||||
}
|
||||
|
||||
if (this.eat(tt.parenL)) {
|
||||
const node = this.startNodeAt(startPos, startLoc);
|
||||
node.callee = expr;
|
||||
// Every time a decorator class expression is evaluated, a new empty array is pushed onto the stack
|
||||
// So that the decorators of any nested class expressions will be dealt with separately
|
||||
this.state.decoratorStack.push([]);
|
||||
node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
|
||||
this.state.decoratorStack.pop();
|
||||
expr = this.finishNode(node, "CallExpression");
|
||||
this.toReferencedList(expr.arguments);
|
||||
this.toReferencedList(node.arguments);
|
||||
}
|
||||
|
||||
node.expression = expr;
|
||||
this.state.decoratorStack.pop();
|
||||
} else {
|
||||
node.expression = this.parseMaybeAssign();
|
||||
node.callee = this.parseMaybeAssign();
|
||||
}
|
||||
return this.finishNode(node, "Decorator");
|
||||
}
|
||||
@@ -959,14 +963,13 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.parseClassMember(classBody, member, state);
|
||||
|
||||
if (
|
||||
this.hasPlugin("decorators2") &&
|
||||
["method", "get", "set"].indexOf(member.kind) === -1 &&
|
||||
member.kind === "constructor" &&
|
||||
member.decorators &&
|
||||
member.decorators.length > 0
|
||||
) {
|
||||
this.raise(
|
||||
member.start,
|
||||
"Stage 2 decorators may only be used with a class or a class method",
|
||||
"Decorators can't be used with a constructor. Did you mean '@dec class { ... }'?",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +336,7 @@ export type VariableDeclarator = NodeBase & {
|
||||
export type Decorator = NodeBase & {
|
||||
type: "Decorator",
|
||||
expression: Expression,
|
||||
arguments?: Array<Expression | SpreadElement>,
|
||||
};
|
||||
|
||||
export type Directive = NodeBase & {
|
||||
|
||||
Reference in New Issue
Block a user