Add the decoratorsAutoAccessors parser plugin (#13681)

Co-authored-by: Huáng Jùnliàng <jlhwung@gmail.com>
Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
Chris Garrett 2021-10-04 11:08:14 -04:00 committed by Nicolò Ribaudo
parent bee3e35851
commit 579ab3fc81
50 changed files with 1095 additions and 5 deletions

View File

@ -115,6 +115,49 @@ export function ClassProperty(this: Printer, node: t.ClassProperty) {
this.semicolon(); this.semicolon();
} }
export function ClassAccessorProperty(
this: Printer,
node: t.ClassAccessorProperty,
) {
this.printJoin(node.decorators, node);
// catch up to property key, avoid line break
// between member modifiers and the property key.
this.source("end", node.key.loc);
this.tsPrintClassMemberModifiers(node, /* isField */ true);
this.word("accessor");
this.printInnerComments(node);
this.space();
if (node.computed) {
this.token("[");
this.print(node.key, node);
this.token("]");
} else {
this._variance(node);
this.print(node.key, node);
}
// TS
if (node.optional) {
this.token("?");
}
if (node.definite) {
this.token("!");
}
this.print(node.typeAnnotation, node);
if (node.value) {
this.space();
this.token("=");
this.space();
this.print(node.value, node);
}
this.semicolon();
}
export function ClassPrivateProperty( export function ClassPrivateProperty(
this: Printer, this: Printer,
node: t.ClassPrivateProperty, node: t.ClassPrivateProperty,

View File

@ -1213,6 +1213,18 @@ interface ClassPrivateProperty <: Node {
} }
``` ```
## ClassAccessorProperty
```js
interface ClassAccessorProperty <: Node {
type: "ClassAccessorProperty";
key: Expression | PrivateName;
value: Expression;
static: boolean;
computed: boolean;
}
```
## StaticBlock ## StaticBlock
```js ```js

View File

@ -1518,8 +1518,9 @@ export default class StatementParser extends ExpressionParser {
) { ) {
const publicMethod: $FlowSubtype<N.ClassMethod> = member; const publicMethod: $FlowSubtype<N.ClassMethod> = member;
const privateMethod: $FlowSubtype<N.ClassPrivateMethod> = member; const privateMethod: $FlowSubtype<N.ClassPrivateMethod> = member;
const publicProp: $FlowSubtype<N.ClassMethod> = member; const publicProp: $FlowSubtype<N.ClassProperty> = member;
const privateProp: $FlowSubtype<N.ClassPrivateMethod> = member; const privateProp: $FlowSubtype<N.ClassPrivateProperty> = member;
const accessorProp: $FlowSubtype<N.ClassAccessorProperty> = member;
const method: typeof publicMethod | typeof privateMethod = publicMethod; const method: typeof publicMethod | typeof privateMethod = publicMethod;
const publicMember: typeof publicMethod | typeof publicProp = publicMethod; const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
@ -1674,6 +1675,18 @@ export default class StatementParser extends ExpressionParser {
} }
this.checkGetterSetterParams(publicMethod); this.checkGetterSetterParams(publicMethod);
} else if (
isContextual &&
key.name === "accessor" &&
!this.isLineTerminator()
) {
this.expectPlugin("decoratorAutoAccessors");
this.resetPreviousNodeTrailingComments(key);
// The so-called parsed name would have been "accessor": get the real name.
const isPrivate = this.match(tt.privateName);
this.parseClassElementName(publicProp);
this.pushClassAccessorProperty(classBody, accessorProp, isPrivate);
} else if (this.isLineTerminator()) { } else if (this.isLineTerminator()) {
// an uninitialized class property (due to ASI, since we don't otherwise recognize the next token) // an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
if (isPrivate) { if (isPrivate) {
@ -1759,6 +1772,34 @@ export default class StatementParser extends ExpressionParser {
); );
} }
pushClassAccessorProperty(
classBody: N.ClassBody,
prop: N.ClassAccessorProperty,
isPrivate: boolean,
) {
if (!isPrivate && !prop.computed) {
// Not private, so not node is not a PrivateName and we can safely cast
const key = (prop.key: N.Expression);
if (key.name === "constructor" || key.value === "constructor") {
// Non-computed field, which is either an identifier named "constructor"
// or a string literal named "constructor"
this.raise(prop.key.start, Errors.ConstructorClassField);
}
}
const node = this.parseClassAccessorProperty(prop);
classBody.body.push(node);
if (isPrivate) {
this.classScope.declarePrivateName(
this.getPrivateNameSV(node.key),
CLASS_ELEMENT_OTHER,
node.key.start,
);
}
}
pushClassMethod( pushClassMethod(
classBody: N.ClassBody, classBody: N.ClassBody,
method: N.ClassMethod, method: N.ClassMethod,
@ -1843,8 +1884,18 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "ClassProperty"); return this.finishNode(node, "ClassProperty");
} }
parseClassAccessorProperty(
node: N.ClassAccessorProperty,
): N.ClassAccessorProperty {
this.parseInitializer(node);
this.semicolon();
return this.finishNode(node, "ClassAccessorProperty");
}
// https://tc39.es/ecma262/#prod-Initializer // https://tc39.es/ecma262/#prod-Initializer
parseInitializer(node: N.ClassProperty | N.ClassPrivateProperty): void { parseInitializer(
node: N.ClassProperty | N.ClassPrivateProperty | N.ClassAccessorProperty,
): void {
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER); this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
this.expressionScope.enter(newExpressionScope()); this.expressionScope.enter(newExpressionScope());
this.prodParam.enter(PARAM); this.prodParam.enter(PARAM);

View File

@ -782,7 +782,8 @@ export type ClassMember =
| ClassMethod | ClassMethod
| ClassPrivateMethod | ClassPrivateMethod
| ClassProperty | ClassProperty
| ClassPrivateProperty; | ClassPrivateProperty
| ClassAccessorProperty;
export type MethodLike = export type MethodLike =
| ObjectMethod | ObjectMethod
@ -855,6 +856,20 @@ export type ClassPrivateProperty = NodeBase & {
variance?: ?FlowVariance, variance?: ?FlowVariance,
}; };
export type ClassAccessorProperty = ClassMemberBase &
DeclarationBase & {
type: "ClassAccessorProperty",
key: Expression | PrivateName,
value: ?Expression,
typeAnnotation?: ?TypeAnnotationBase, // TODO: Not in spec
variance?: ?FlowVariance, // TODO: Not in spec
// TypeScript only: (TODO: Not in spec)
readonly?: true,
definite?: true,
};
export type OptClassDeclaration = ClassBase & export type OptClassDeclaration = ClassBase &
DeclarationBase & DeclarationBase &
HasDecorators & { HasDecorators & {

View File

@ -0,0 +1,3 @@
class Foo {
accessor bar;
}

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: \"decoratorAutoAccessors\". (2:11)",
"plugins": []
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor constructor;
}

View File

@ -0,0 +1,44 @@
{
"type": "File",
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Classes may not have a field named 'constructor'. (2:11)"
],
"program": {
"type": "Program",
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":37,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":35,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":23}},
"static": false,
"key": {
"type": "Identifier",
"start":23,"end":34,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":22},"identifierName":"constructor"},
"name": "constructor"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
class Foo {
accessor
bar;
}

View File

@ -0,0 +1,53 @@
{
"type": "File",
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"program": {
"type": "Program",
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":31,"loc":{"start":{"line":1,"column":10},"end":{"line":4,"column":1}},
"body": [
{
"type": "ClassProperty",
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10}},
"static": false,
"key": {
"type": "Identifier",
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10},"identifierName":"accessor"},
"name": "accessor"
},
"computed": false,
"value": null
},
{
"type": "ClassProperty",
"start":25,"end":29,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":6}},
"static": false,
"key": {
"type": "Identifier",
"start":25,"end":28,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":5},"identifierName":"bar"},
"name": "bar"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor bar;
}

View File

@ -0,0 +1,41 @@
{
"type": "File",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":29,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":27,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":15}},
"static": false,
"key": {
"type": "Identifier",
"start":23,"end":26,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":14},"identifierName":"bar"},
"name": "bar"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor ["constructor"];
}

View File

@ -0,0 +1,45 @@
{
"type": "File",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":41,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":39,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":27}},
"static": false,
"key": {
"type": "StringLiteral",
"start":24,"end":37,"loc":{"start":{"line":2,"column":12},"end":{"line":2,"column":25}},
"extra": {
"rawValue": "constructor",
"raw": "\"constructor\""
},
"value": "constructor"
},
"computed": true,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
class Foo {
accessor
["bar"];
}

View File

@ -0,0 +1,57 @@
{
"type": "File",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"program": {
"type": "Program",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":35,"loc":{"start":{"line":1,"column":10},"end":{"line":4,"column":1}},
"body": [
{
"type": "ClassProperty",
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10}},
"static": false,
"key": {
"type": "Identifier",
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10},"identifierName":"accessor"},
"name": "accessor"
},
"computed": false,
"value": null
},
{
"type": "ClassProperty",
"start":25,"end":33,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":10}},
"static": false,
"computed": true,
"key": {
"type": "StringLiteral",
"start":26,"end":31,"loc":{"start":{"line":3,"column":3},"end":{"line":3,"column":8}},
"extra": {
"rawValue": "bar",
"raw": "\"bar\""
},
"value": "bar"
},
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor ["bar"];
}

View File

@ -0,0 +1,45 @@
{
"type": "File",
"start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":33,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":31,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":19}},
"static": false,
"key": {
"type": "StringLiteral",
"start":24,"end":29,"loc":{"start":{"line":2,"column":12},"end":{"line":2,"column":17}},
"extra": {
"rawValue": "bar",
"raw": "\"bar\""
},
"value": "bar"
},
"computed": true,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
static accessor ["bar"];
}

View File

@ -0,0 +1,45 @@
{
"type": "File",
"start":0,"end":40,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":40,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":40,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":40,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":38,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":26}},
"static": true,
"key": {
"type": "StringLiteral",
"start":31,"end":36,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":24}},
"extra": {
"rawValue": "bar",
"raw": "\"bar\""
},
"value": "bar"
},
"computed": true,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor = 123;
}

View File

@ -0,0 +1,49 @@
{
"type": "File",
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"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":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":31,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassProperty",
"start":14,"end":29,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":17}},
"static": false,
"key": {
"type": "Identifier",
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10},"identifierName":"accessor"},
"name": "accessor"
},
"computed": false,
"value": {
"type": "NumericLiteral",
"start":25,"end":28,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":16}},
"extra": {
"rawValue": 123,
"raw": "123"
},
"value": 123
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor static bar;
}

View File

@ -0,0 +1,56 @@
{
"type": "File",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Missing semicolon. (2:17)"
],
"program": {
"type": "Program",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":36,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":29,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":17}},
"static": false,
"key": {
"type": "Identifier",
"start":23,"end":29,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":17},"identifierName":"static"},
"name": "static"
},
"computed": false,
"value": null
},
{
"type": "ClassProperty",
"start":30,"end":34,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":22}},
"static": false,
"key": {
"type": "Identifier",
"start":30,"end":33,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":21},"identifierName":"bar"},
"name": "bar"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor static bar;
}

View File

@ -0,0 +1,56 @@
{
"type": "File",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Missing semicolon. (2:17)"
],
"program": {
"type": "Program",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":36,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":29,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":17}},
"static": false,
"key": {
"type": "Identifier",
"start":23,"end":29,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":17},"identifierName":"static"},
"name": "static"
},
"computed": false,
"value": null
},
{
"type": "ClassProperty",
"start":30,"end":34,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":22}},
"static": false,
"key": {
"type": "Identifier",
"start":30,"end":33,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":21},"identifierName":"bar"},
"name": "bar"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor() {}
}

View File

@ -0,0 +1,51 @@
{
"type": "File",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":29,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassMethod",
"start":14,"end":27,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":15}},
"static": false,
"key": {
"type": "Identifier",
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10},"identifierName":"accessor"},
"name": "accessor"
},
"computed": false,
"kind": "method",
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":25,"end":27,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":15}},
"body": [],
"directives": []
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,5 @@
class Foo {
accessor bar() {
}
}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:14)"
}

View File

@ -0,0 +1,5 @@
{
"plugins": [
"decoratorAutoAccessors"
]
}

View File

@ -0,0 +1,3 @@
class Foo {
accessor #bar;
}

View File

@ -0,0 +1,45 @@
{
"type": "File",
"start":0,"end":30,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":30,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"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":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":30,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":28,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":16}},
"static": false,
"key": {
"type": "PrivateName",
"start":23,"end":27,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":15}},
"id": {
"type": "Identifier",
"start":24,"end":27,"loc":{"start":{"line":2,"column":12},"end":{"line":2,"column":15},"identifierName":"bar"},
"name": "bar"
}
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
static accessor #bar;
}

View File

@ -0,0 +1,45 @@
{
"type": "File",
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":37,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":35,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":23}},
"static": true,
"key": {
"type": "PrivateName",
"start":30,"end":34,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":22}},
"id": {
"type": "Identifier",
"start":31,"end":34,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":22},"identifierName":"bar"},
"name": "bar"
}
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
class Foo {
static accessor
bar;
}

View File

@ -0,0 +1,53 @@
{
"type": "File",
"start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"program": {
"type": "Program",
"start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":38,"loc":{"start":{"line":1,"column":10},"end":{"line":4,"column":1}},
"body": [
{
"type": "ClassProperty",
"start":14,"end":29,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":17}},
"static": true,
"key": {
"type": "Identifier",
"start":21,"end":29,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":17},"identifierName":"accessor"},
"name": "accessor"
},
"computed": false,
"value": null
},
{
"type": "ClassProperty",
"start":32,"end":36,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":6}},
"static": false,
"key": {
"type": "Identifier",
"start":32,"end":35,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":5},"identifierName":"bar"},
"name": "bar"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
class Foo {
static accessor bar;
}

View File

@ -0,0 +1,41 @@
{
"type": "File",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"program": {
"type": "Program",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
"name": "Foo"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start":10,"end":36,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
"body": [
{
"type": "ClassAccessorProperty",
"start":14,"end":34,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":22}},
"static": true,
"key": {
"type": "Identifier",
"start":30,"end":33,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":21},"identifierName":"bar"},
"name": "bar"
},
"computed": false,
"value": null
}
]
}
}
],
"directives": []
}
}

View File

@ -6,6 +6,7 @@ import * as t from "@babel/types";
import NodePath from "../index"; import NodePath from "../index";
export interface NodePathAssetions { export interface NodePathAssetions {
assertAccessor(opts?: object): asserts this is NodePath<t.Accessor>;
assertAnyTypeAnnotation( assertAnyTypeAnnotation(
opts?: object, opts?: object,
): asserts this is NodePath<t.AnyTypeAnnotation>; ): asserts this is NodePath<t.AnyTypeAnnotation>;
@ -61,6 +62,9 @@ export interface NodePathAssetions {
): asserts this is NodePath<t.CallExpression>; ): asserts this is NodePath<t.CallExpression>;
assertCatchClause(opts?: object): asserts this is NodePath<t.CatchClause>; assertCatchClause(opts?: object): asserts this is NodePath<t.CatchClause>;
assertClass(opts?: object): asserts this is NodePath<t.Class>; assertClass(opts?: object): asserts this is NodePath<t.Class>;
assertClassAccessorProperty(
opts?: object,
): asserts this is NodePath<t.ClassAccessorProperty>;
assertClassBody(opts?: object): asserts this is NodePath<t.ClassBody>; assertClassBody(opts?: object): asserts this is NodePath<t.ClassBody>;
assertClassDeclaration( assertClassDeclaration(
opts?: object, opts?: object,

View File

@ -7,6 +7,7 @@ import NodePath from "../index";
import type { VirtualTypeAliases } from "./virtual-types"; import type { VirtualTypeAliases } from "./virtual-types";
export interface NodePathValidators { export interface NodePathValidators {
isAccessor(opts?: object): this is NodePath<t.Accessor>;
isAnyTypeAnnotation(opts?: object): this is NodePath<t.AnyTypeAnnotation>; isAnyTypeAnnotation(opts?: object): this is NodePath<t.AnyTypeAnnotation>;
isArgumentPlaceholder(opts?: object): this is NodePath<t.ArgumentPlaceholder>; isArgumentPlaceholder(opts?: object): this is NodePath<t.ArgumentPlaceholder>;
isArrayExpression(opts?: object): this is NodePath<t.ArrayExpression>; isArrayExpression(opts?: object): this is NodePath<t.ArrayExpression>;
@ -38,6 +39,9 @@ export interface NodePathValidators {
isCallExpression(opts?: object): this is NodePath<t.CallExpression>; isCallExpression(opts?: object): this is NodePath<t.CallExpression>;
isCatchClause(opts?: object): this is NodePath<t.CatchClause>; isCatchClause(opts?: object): this is NodePath<t.CatchClause>;
isClass(opts?: object): this is NodePath<t.Class>; isClass(opts?: object): this is NodePath<t.Class>;
isClassAccessorProperty(
opts?: object,
): this is NodePath<t.ClassAccessorProperty>;
isClassBody(opts?: object): this is NodePath<t.ClassBody>; isClassBody(opts?: object): this is NodePath<t.ClassBody>;
isClassDeclaration(opts?: object): this is NodePath<t.ClassDeclaration>; isClassDeclaration(opts?: object): this is NodePath<t.ClassDeclaration>;
isClassExpression(opts?: object): this is NodePath<t.ClassExpression>; isClassExpression(opts?: object): this is NodePath<t.ClassExpression>;

View File

@ -506,6 +506,12 @@ export function assertClassProperty(
): asserts node is t.ClassProperty { ): asserts node is t.ClassProperty {
assert("ClassProperty", node, opts); assert("ClassProperty", node, opts);
} }
export function assertClassAccessorProperty(
node: object | null | undefined,
opts?: object | null,
): asserts node is t.ClassAccessorProperty {
assert("ClassAccessorProperty", node, opts);
}
export function assertClassPrivateProperty( export function assertClassPrivateProperty(
node: object | null | undefined, node: object | null | undefined,
opts?: object | null, opts?: object | null,
@ -1694,6 +1700,12 @@ export function assertModuleSpecifier(
): asserts node is t.ModuleSpecifier { ): asserts node is t.ModuleSpecifier {
assert("ModuleSpecifier", node, opts); assert("ModuleSpecifier", node, opts);
} }
export function assertAccessor(
node: object | null | undefined,
opts?: object | null,
): asserts node is t.Accessor {
assert("Accessor", node, opts);
}
export function assertPrivate( export function assertPrivate(
node: object | null | undefined, node: object | null | undefined,
opts?: object | null, opts?: object | null,

View File

@ -65,6 +65,7 @@ export type Node =
| BreakStatement | BreakStatement
| CallExpression | CallExpression
| CatchClause | CatchClause
| ClassAccessorProperty
| ClassBody | ClassBody
| ClassDeclaration | ClassDeclaration
| ClassExpression | ClassExpression
@ -988,6 +989,24 @@ export interface ClassProperty extends BaseNode {
variance?: Variance | null; variance?: Variance | null;
} }
export interface ClassAccessorProperty extends BaseNode {
type: "ClassAccessorProperty";
key: Identifier | StringLiteral | NumericLiteral | Expression | PrivateName;
value?: Expression | null;
typeAnnotation?: TypeAnnotation | TSTypeAnnotation | Noop | null;
decorators?: Array<Decorator> | null;
computed?: boolean;
static?: boolean;
abstract?: boolean | null;
accessibility?: "public" | "private" | "protected" | null;
declare?: boolean | null;
definite?: boolean | null;
optional?: boolean | null;
override?: boolean;
readonly?: boolean | null;
variance?: Variance | null;
}
export interface ClassPrivateProperty extends BaseNode { export interface ClassPrivateProperty extends BaseNode {
type: "ClassPrivateProperty"; type: "ClassPrivateProperty";
key: PrivateName; key: PrivateName;
@ -2094,6 +2113,7 @@ export type Standardized =
| OptionalMemberExpression | OptionalMemberExpression
| OptionalCallExpression | OptionalCallExpression
| ClassProperty | ClassProperty
| ClassAccessorProperty
| ClassPrivateProperty | ClassPrivateProperty
| ClassPrivateMethod | ClassPrivateMethod
| PrivateName | PrivateName
@ -2366,7 +2386,11 @@ export type UserWhitespacable =
| ObjectTypeSpreadProperty; | ObjectTypeSpreadProperty;
export type Method = ObjectMethod | ClassMethod | ClassPrivateMethod; export type Method = ObjectMethod | ClassMethod | ClassPrivateMethod;
export type ObjectMember = ObjectMethod | ObjectProperty; export type ObjectMember = ObjectMethod | ObjectProperty;
export type Property = ObjectProperty | ClassProperty | ClassPrivateProperty; export type Property =
| ObjectProperty
| ClassProperty
| ClassAccessorProperty
| ClassPrivateProperty;
export type UnaryLike = UnaryExpression | SpreadElement; export type UnaryLike = UnaryExpression | SpreadElement;
export type Pattern = AssignmentPattern | ArrayPattern | ObjectPattern; export type Pattern = AssignmentPattern | ArrayPattern | ObjectPattern;
export type Class = ClassExpression | ClassDeclaration; export type Class = ClassExpression | ClassDeclaration;
@ -2386,6 +2410,7 @@ export type ModuleSpecifier =
| ImportSpecifier | ImportSpecifier
| ExportNamespaceSpecifier | ExportNamespaceSpecifier
| ExportDefaultSpecifier; | ExportDefaultSpecifier;
export type Accessor = ClassAccessorProperty;
export type Private = ClassPrivateProperty | ClassPrivateMethod | PrivateName; export type Private = ClassPrivateProperty | ClassPrivateMethod | PrivateName;
export type Flow = export type Flow =
| AnyTypeAnnotation | AnyTypeAnnotation
@ -2691,6 +2716,7 @@ export interface Aliases {
ModuleDeclaration: ModuleDeclaration; ModuleDeclaration: ModuleDeclaration;
ExportDeclaration: ExportDeclaration; ExportDeclaration: ExportDeclaration;
ModuleSpecifier: ModuleSpecifier; ModuleSpecifier: ModuleSpecifier;
Accessor: Accessor;
Private: Private; Private: Private;
Flow: Flow; Flow: Flow;
FlowType: FlowType; FlowType: FlowType;

View File

@ -525,6 +525,21 @@ export function classProperty(
): t.ClassProperty { ): t.ClassProperty {
return builder.apply("ClassProperty", arguments); return builder.apply("ClassProperty", arguments);
} }
export function classAccessorProperty(
key:
| t.Identifier
| t.StringLiteral
| t.NumericLiteral
| t.Expression
| t.PrivateName,
value?: t.Expression | null,
typeAnnotation?: t.TypeAnnotation | t.TSTypeAnnotation | t.Noop | null,
decorators?: Array<t.Decorator> | null,
computed?: boolean,
_static?: boolean,
): t.ClassAccessorProperty {
return builder.apply("ClassAccessorProperty", arguments);
}
export function classPrivateProperty( export function classPrivateProperty(
key: t.PrivateName, key: t.PrivateName,
value: t.Expression | null | undefined, value: t.Expression | null | undefined,

View File

@ -91,6 +91,7 @@ export {
optionalMemberExpression as OptionalMemberExpression, optionalMemberExpression as OptionalMemberExpression,
optionalCallExpression as OptionalCallExpression, optionalCallExpression as OptionalCallExpression,
classProperty as ClassProperty, classProperty as ClassProperty,
classAccessorProperty as ClassAccessorProperty,
classPrivateProperty as ClassPrivateProperty, classPrivateProperty as ClassPrivateProperty,
classPrivateMethod as ClassPrivateMethod, classPrivateMethod as ClassPrivateMethod,
privateName as PrivateName, privateName as PrivateName,

View File

@ -39,6 +39,7 @@ export const CLASS_TYPES = FLIPPED_ALIAS_KEYS["Class"];
export const MODULEDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["ModuleDeclaration"]; export const MODULEDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["ModuleDeclaration"];
export const EXPORTDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["ExportDeclaration"]; export const EXPORTDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["ExportDeclaration"];
export const MODULESPECIFIER_TYPES = FLIPPED_ALIAS_KEYS["ModuleSpecifier"]; export const MODULESPECIFIER_TYPES = FLIPPED_ALIAS_KEYS["ModuleSpecifier"];
export const ACCESSOR_TYPES = FLIPPED_ALIAS_KEYS["Accessor"];
export const PRIVATE_TYPES = FLIPPED_ALIAS_KEYS["Private"]; export const PRIVATE_TYPES = FLIPPED_ALIAS_KEYS["Private"];
export const FLOW_TYPES = FLIPPED_ALIAS_KEYS["Flow"]; export const FLOW_TYPES = FLIPPED_ALIAS_KEYS["Flow"];
export const FLOWTYPE_TYPES = FLIPPED_ALIAS_KEYS["FlowType"]; export const FLOWTYPE_TYPES = FLIPPED_ALIAS_KEYS["FlowType"];

View File

@ -8,6 +8,7 @@ export default function toComputedKey(
| t.ObjectProperty | t.ObjectProperty
| t.ClassMethod | t.ClassMethod
| t.ClassProperty | t.ClassProperty
| t.ClassAccessorProperty
| t.MemberExpression | t.MemberExpression
| t.OptionalMemberExpression, | t.OptionalMemberExpression,
// @ts-expect-error todo(flow->ts): maybe check the type of node before accessing .key and .property // @ts-expect-error todo(flow->ts): maybe check the type of node before accessing .key and .property

View File

@ -2126,6 +2126,80 @@ defineType("ClassProperty", {
}, },
}); });
defineType("ClassAccessorProperty", {
visitor: ["key", "value", "typeAnnotation", "decorators"],
builder: [
"key",
"value",
"typeAnnotation",
"decorators",
"computed",
"static",
],
aliases: ["Property", "Accessor"],
fields: {
...classMethodOrPropertyCommon,
key: {
validate: chain(
(function () {
const normal = assertNodeType(
"Identifier",
"StringLiteral",
"NumericLiteral",
"PrivateName",
);
const computed = assertNodeType("Expression");
return function (node: any, key: string, val: any) {
const validator = node.computed ? computed : normal;
validator(node, key, val);
};
})(),
assertNodeType(
"Identifier",
"StringLiteral",
"NumericLiteral",
"Expression",
"PrivateName",
),
),
},
value: {
validate: assertNodeType("Expression"),
optional: true,
},
definite: {
validate: assertValueType("boolean"),
optional: true,
},
typeAnnotation: {
validate: process.env.BABEL_8_BREAKING
? assertNodeType("TypeAnnotation", "TSTypeAnnotation")
: assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"),
optional: true,
},
decorators: {
validate: chain(
assertValueType("array"),
assertEach(assertNodeType("Decorator")),
),
optional: true,
},
readonly: {
validate: assertValueType("boolean"),
optional: true,
},
declare: {
validate: assertValueType("boolean"),
optional: true,
},
variance: {
validate: assertNodeType("Variance"),
optional: true,
},
},
});
defineType("ClassPrivateProperty", { defineType("ClassPrivateProperty", {
visitor: ["key", "value", "decorators", "typeAnnotation"], visitor: ["key", "value", "decorators", "typeAnnotation"],
builder: ["key", "value", "decorators", "static"], builder: ["key", "value", "decorators", "static"],

View File

@ -1399,6 +1399,23 @@ export function isClassProperty(
return false; return false;
} }
export function isClassAccessorProperty(
node: object | null | undefined,
opts?: object | null,
): node is t.ClassAccessorProperty {
if (!node) return false;
const nodeType = (node as t.Node).type;
if (nodeType === "ClassAccessorProperty") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isClassPrivateProperty( export function isClassPrivateProperty(
node: object | null | undefined, node: object | null | undefined,
opts?: object | null, opts?: object | null,
@ -4277,6 +4294,7 @@ export function isStandardized(
"OptionalMemberExpression" === nodeType || "OptionalMemberExpression" === nodeType ||
"OptionalCallExpression" === nodeType || "OptionalCallExpression" === nodeType ||
"ClassProperty" === nodeType || "ClassProperty" === nodeType ||
"ClassAccessorProperty" === nodeType ||
"ClassPrivateProperty" === nodeType || "ClassPrivateProperty" === nodeType ||
"ClassPrivateMethod" === nodeType || "ClassPrivateMethod" === nodeType ||
"PrivateName" === nodeType || "PrivateName" === nodeType ||
@ -5052,6 +5070,7 @@ export function isProperty(
if ( if (
"ObjectProperty" === nodeType || "ObjectProperty" === nodeType ||
"ClassProperty" === nodeType || "ClassProperty" === nodeType ||
"ClassAccessorProperty" === nodeType ||
"ClassPrivateProperty" === nodeType "ClassPrivateProperty" === nodeType
) { ) {
if (typeof opts === "undefined") { if (typeof opts === "undefined") {
@ -5187,6 +5206,23 @@ export function isModuleSpecifier(
return false; return false;
} }
export function isAccessor(
node: object | null | undefined,
opts?: object | null,
): node is t.Accessor {
if (!node) return false;
const nodeType = (node as t.Node).type;
if ("ClassAccessorProperty" === nodeType) {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isPrivate( export function isPrivate(
node: object | null | undefined, node: object | null | undefined,
opts?: object | null, opts?: object | null,

View File

@ -63,6 +63,7 @@ export default function isReferenced(
// yes: class { [NODE] = value; } // yes: class { [NODE] = value; }
// yes: class { key = NODE; } // yes: class { key = NODE; }
case "ClassProperty": case "ClassProperty":
case "ClassAccessorProperty":
if (parent.key === node) { if (parent.key === node) {
return !!parent.computed; return !!parent.computed;
} }