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 dc5f419fa9
commit 3f3ce5f668
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();
}
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(
this: Printer,
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
```js

View File

@ -1518,8 +1518,9 @@ export default class StatementParser extends ExpressionParser {
) {
const publicMethod: $FlowSubtype<N.ClassMethod> = member;
const privateMethod: $FlowSubtype<N.ClassPrivateMethod> = member;
const publicProp: $FlowSubtype<N.ClassMethod> = member;
const privateProp: $FlowSubtype<N.ClassPrivateMethod> = member;
const publicProp: $FlowSubtype<N.ClassProperty> = member;
const privateProp: $FlowSubtype<N.ClassPrivateProperty> = member;
const accessorProp: $FlowSubtype<N.ClassAccessorProperty> = member;
const method: typeof publicMethod | typeof privateMethod = publicMethod;
const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
@ -1674,6 +1675,18 @@ export default class StatementParser extends ExpressionParser {
}
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()) {
// an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
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(
classBody: N.ClassBody,
method: N.ClassMethod,
@ -1843,8 +1884,18 @@ export default class StatementParser extends ExpressionParser {
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
parseInitializer(node: N.ClassProperty | N.ClassPrivateProperty): void {
parseInitializer(
node: N.ClassProperty | N.ClassPrivateProperty | N.ClassAccessorProperty,
): void {
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
this.expressionScope.enter(newExpressionScope());
this.prodParam.enter(PARAM);

View File

@ -782,7 +782,8 @@ export type ClassMember =
| ClassMethod
| ClassPrivateMethod
| ClassProperty
| ClassPrivateProperty;
| ClassPrivateProperty
| ClassAccessorProperty;
export type MethodLike =
| ObjectMethod
@ -855,6 +856,20 @@ export type ClassPrivateProperty = NodeBase & {
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 &
DeclarationBase &
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";
export interface NodePathAssetions {
assertAccessor(opts?: object): asserts this is NodePath<t.Accessor>;
assertAnyTypeAnnotation(
opts?: object,
): asserts this is NodePath<t.AnyTypeAnnotation>;
@ -61,6 +62,9 @@ export interface NodePathAssetions {
): asserts this is NodePath<t.CallExpression>;
assertCatchClause(opts?: object): asserts this is NodePath<t.CatchClause>;
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>;
assertClassDeclaration(
opts?: object,

View File

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

View File

@ -506,6 +506,12 @@ export function assertClassProperty(
): asserts node is t.ClassProperty {
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(
node: object | null | undefined,
opts?: object | null,
@ -1694,6 +1700,12 @@ export function assertModuleSpecifier(
): asserts node is t.ModuleSpecifier {
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(
node: object | null | undefined,
opts?: object | null,

View File

@ -65,6 +65,7 @@ export type Node =
| BreakStatement
| CallExpression
| CatchClause
| ClassAccessorProperty
| ClassBody
| ClassDeclaration
| ClassExpression
@ -988,6 +989,24 @@ export interface ClassProperty extends BaseNode {
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 {
type: "ClassPrivateProperty";
key: PrivateName;
@ -2094,6 +2113,7 @@ export type Standardized =
| OptionalMemberExpression
| OptionalCallExpression
| ClassProperty
| ClassAccessorProperty
| ClassPrivateProperty
| ClassPrivateMethod
| PrivateName
@ -2366,7 +2386,11 @@ export type UserWhitespacable =
| ObjectTypeSpreadProperty;
export type Method = ObjectMethod | ClassMethod | ClassPrivateMethod;
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 Pattern = AssignmentPattern | ArrayPattern | ObjectPattern;
export type Class = ClassExpression | ClassDeclaration;
@ -2386,6 +2410,7 @@ export type ModuleSpecifier =
| ImportSpecifier
| ExportNamespaceSpecifier
| ExportDefaultSpecifier;
export type Accessor = ClassAccessorProperty;
export type Private = ClassPrivateProperty | ClassPrivateMethod | PrivateName;
export type Flow =
| AnyTypeAnnotation
@ -2691,6 +2716,7 @@ export interface Aliases {
ModuleDeclaration: ModuleDeclaration;
ExportDeclaration: ExportDeclaration;
ModuleSpecifier: ModuleSpecifier;
Accessor: Accessor;
Private: Private;
Flow: Flow;
FlowType: FlowType;

View File

@ -525,6 +525,21 @@ export function classProperty(
): t.ClassProperty {
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(
key: t.PrivateName,
value: t.Expression | null | undefined,

View File

@ -91,6 +91,7 @@ export {
optionalMemberExpression as OptionalMemberExpression,
optionalCallExpression as OptionalCallExpression,
classProperty as ClassProperty,
classAccessorProperty as ClassAccessorProperty,
classPrivateProperty as ClassPrivateProperty,
classPrivateMethod as ClassPrivateMethod,
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 EXPORTDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["ExportDeclaration"];
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 FLOW_TYPES = FLIPPED_ALIAS_KEYS["Flow"];
export const FLOWTYPE_TYPES = FLIPPED_ALIAS_KEYS["FlowType"];

View File

@ -8,6 +8,7 @@ export default function toComputedKey(
| t.ObjectProperty
| t.ClassMethod
| t.ClassProperty
| t.ClassAccessorProperty
| t.MemberExpression
| t.OptionalMemberExpression,
// @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", {
visitor: ["key", "value", "decorators", "typeAnnotation"],
builder: ["key", "value", "decorators", "static"],

View File

@ -1399,6 +1399,23 @@ export function isClassProperty(
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(
node: object | null | undefined,
opts?: object | null,
@ -4277,6 +4294,7 @@ export function isStandardized(
"OptionalMemberExpression" === nodeType ||
"OptionalCallExpression" === nodeType ||
"ClassProperty" === nodeType ||
"ClassAccessorProperty" === nodeType ||
"ClassPrivateProperty" === nodeType ||
"ClassPrivateMethod" === nodeType ||
"PrivateName" === nodeType ||
@ -5052,6 +5070,7 @@ export function isProperty(
if (
"ObjectProperty" === nodeType ||
"ClassProperty" === nodeType ||
"ClassAccessorProperty" === nodeType ||
"ClassPrivateProperty" === nodeType
) {
if (typeof opts === "undefined") {
@ -5187,6 +5206,23 @@ export function isModuleSpecifier(
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(
node: object | null | undefined,
opts?: object | null,

View File

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