Skip to content

Commit f893b33

Browse files
pzuraqJLHwungnicolo-ribaudo
committed
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>
1 parent 10fe2f3 commit f893b33

50 files changed

Lines changed: 1095 additions & 5 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/babel-generator/src/generators/classes.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,49 @@ export function ClassProperty(this: Printer, node: t.ClassProperty) {
115115
this.semicolon();
116116
}
117117

118+
export function ClassAccessorProperty(
119+
this: Printer,
120+
node: t.ClassAccessorProperty,
121+
) {
122+
this.printJoin(node.decorators, node);
123+
124+
// catch up to property key, avoid line break
125+
// between member modifiers and the property key.
126+
this.source("end", node.key.loc);
127+
128+
this.tsPrintClassMemberModifiers(node, /* isField */ true);
129+
130+
this.word("accessor");
131+
this.printInnerComments(node);
132+
this.space();
133+
134+
if (node.computed) {
135+
this.token("[");
136+
this.print(node.key, node);
137+
this.token("]");
138+
} else {
139+
this._variance(node);
140+
this.print(node.key, node);
141+
}
142+
143+
// TS
144+
if (node.optional) {
145+
this.token("?");
146+
}
147+
if (node.definite) {
148+
this.token("!");
149+
}
150+
151+
this.print(node.typeAnnotation, node);
152+
if (node.value) {
153+
this.space();
154+
this.token("=");
155+
this.space();
156+
this.print(node.value, node);
157+
}
158+
this.semicolon();
159+
}
160+
118161
export function ClassPrivateProperty(
119162
this: Printer,
120163
node: t.ClassPrivateProperty,

packages/babel-parser/ast/spec.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,18 @@ interface ClassPrivateProperty <: Node {
12131213
}
12141214
```
12151215

1216+
## ClassAccessorProperty
1217+
1218+
```js
1219+
interface ClassAccessorProperty <: Node {
1220+
type: "ClassAccessorProperty";
1221+
key: Expression | PrivateName;
1222+
value: Expression;
1223+
static: boolean;
1224+
computed: boolean;
1225+
}
1226+
```
1227+
12161228
## StaticBlock
12171229

12181230
```js

packages/babel-parser/src/parser/statement.js

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,8 +1529,9 @@ export default class StatementParser extends ExpressionParser {
15291529
) {
15301530
const publicMethod: $FlowSubtype<N.ClassMethod> = member;
15311531
const privateMethod: $FlowSubtype<N.ClassPrivateMethod> = member;
1532-
const publicProp: $FlowSubtype<N.ClassMethod> = member;
1533-
const privateProp: $FlowSubtype<N.ClassPrivateMethod> = member;
1532+
const publicProp: $FlowSubtype<N.ClassProperty> = member;
1533+
const privateProp: $FlowSubtype<N.ClassPrivateProperty> = member;
1534+
const accessorProp: $FlowSubtype<N.ClassAccessorProperty> = member;
15341535

15351536
const method: typeof publicMethod | typeof privateMethod = publicMethod;
15361537
const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
@@ -1687,6 +1688,18 @@ export default class StatementParser extends ExpressionParser {
16871688
}
16881689

16891690
this.checkGetterSetterParams(publicMethod);
1691+
} else if (
1692+
isContextual &&
1693+
key.name === "accessor" &&
1694+
!this.isLineTerminator()
1695+
) {
1696+
this.expectPlugin("decoratorAutoAccessors");
1697+
this.resetPreviousNodeTrailingComments(key);
1698+
1699+
// The so-called parsed name would have been "accessor": get the real name.
1700+
const isPrivate = this.match(tt.privateName);
1701+
this.parseClassElementName(publicProp);
1702+
this.pushClassAccessorProperty(classBody, accessorProp, isPrivate);
16901703
} else if (this.isLineTerminator()) {
16911704
// an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
16921705
if (isPrivate) {
@@ -1774,6 +1787,34 @@ export default class StatementParser extends ExpressionParser {
17741787
);
17751788
}
17761789

1790+
pushClassAccessorProperty(
1791+
classBody: N.ClassBody,
1792+
prop: N.ClassAccessorProperty,
1793+
isPrivate: boolean,
1794+
) {
1795+
if (!isPrivate && !prop.computed) {
1796+
// Not private, so not node is not a PrivateName and we can safely cast
1797+
const key = (prop.key: N.Expression);
1798+
1799+
if (key.name === "constructor" || key.value === "constructor") {
1800+
// Non-computed field, which is either an identifier named "constructor"
1801+
// or a string literal named "constructor"
1802+
this.raise(Errors.ConstructorClassField, { node: key });
1803+
}
1804+
}
1805+
1806+
const node = this.parseClassAccessorProperty(prop);
1807+
classBody.body.push(node);
1808+
1809+
if (isPrivate) {
1810+
this.classScope.declarePrivateName(
1811+
this.getPrivateNameSV(node.key),
1812+
CLASS_ELEMENT_OTHER,
1813+
node.key.loc.start,
1814+
);
1815+
}
1816+
}
1817+
17771818
pushClassMethod(
17781819
classBody: N.ClassBody,
17791820
method: N.ClassMethod,
@@ -1858,8 +1899,18 @@ export default class StatementParser extends ExpressionParser {
18581899
return this.finishNode(node, "ClassProperty");
18591900
}
18601901

1902+
parseClassAccessorProperty(
1903+
node: N.ClassAccessorProperty,
1904+
): N.ClassAccessorProperty {
1905+
this.parseInitializer(node);
1906+
this.semicolon();
1907+
return this.finishNode(node, "ClassAccessorProperty");
1908+
}
1909+
18611910
// https://tc39.es/ecma262/#prod-Initializer
1862-
parseInitializer(node: N.ClassProperty | N.ClassPrivateProperty): void {
1911+
parseInitializer(
1912+
node: N.ClassProperty | N.ClassPrivateProperty | N.ClassAccessorProperty,
1913+
): void {
18631914
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
18641915
this.expressionScope.enter(newExpressionScope());
18651916
this.prodParam.enter(PARAM);

packages/babel-parser/src/types.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,8 @@ export type ClassMember =
782782
| ClassMethod
783783
| ClassPrivateMethod
784784
| ClassProperty
785-
| ClassPrivateProperty;
785+
| ClassPrivateProperty
786+
| ClassAccessorProperty;
786787

787788
export type MethodLike =
788789
| ObjectMethod
@@ -855,6 +856,20 @@ export type ClassPrivateProperty = NodeBase & {
855856
variance?: ?FlowVariance,
856857
};
857858

859+
export type ClassAccessorProperty = ClassMemberBase &
860+
DeclarationBase & {
861+
type: "ClassAccessorProperty",
862+
key: Expression | PrivateName,
863+
value: ?Expression,
864+
865+
typeAnnotation?: ?TypeAnnotationBase, // TODO: Not in spec
866+
variance?: ?FlowVariance, // TODO: Not in spec
867+
868+
// TypeScript only: (TODO: Not in spec)
869+
readonly?: true,
870+
definite?: true,
871+
};
872+
858873
export type OptClassDeclaration = ClassBase &
859874
DeclarationBase &
860875
HasDecorators & {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo {
2+
accessor bar;
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"throws": "This experimental syntax requires enabling the parser plugin: \"decoratorAutoAccessors\". (2:11)",
3+
"plugins": []
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo {
2+
accessor constructor;
3+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
4+
"errors": [
5+
"SyntaxError: Classes may not have a field named 'constructor'. (2:11)"
6+
],
7+
"program": {
8+
"type": "Program",
9+
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
10+
"sourceType": "script",
11+
"interpreter": null,
12+
"body": [
13+
{
14+
"type": "ClassDeclaration",
15+
"start":0,"end":37,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
16+
"id": {
17+
"type": "Identifier",
18+
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
19+
"name": "Foo"
20+
},
21+
"superClass": null,
22+
"body": {
23+
"type": "ClassBody",
24+
"start":10,"end":37,"loc":{"start":{"line":1,"column":10},"end":{"line":3,"column":1}},
25+
"body": [
26+
{
27+
"type": "ClassAccessorProperty",
28+
"start":14,"end":35,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":23}},
29+
"static": false,
30+
"key": {
31+
"type": "Identifier",
32+
"start":23,"end":34,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":22},"identifierName":"constructor"},
33+
"name": "constructor"
34+
},
35+
"computed": false,
36+
"value": null
37+
}
38+
]
39+
}
40+
}
41+
],
42+
"directives": []
43+
}
44+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Foo {
2+
accessor
3+
bar;
4+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
4+
"program": {
5+
"type": "Program",
6+
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
7+
"sourceType": "script",
8+
"interpreter": null,
9+
"body": [
10+
{
11+
"type": "ClassDeclaration",
12+
"start":0,"end":31,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":1}},
13+
"id": {
14+
"type": "Identifier",
15+
"start":6,"end":9,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":9},"identifierName":"Foo"},
16+
"name": "Foo"
17+
},
18+
"superClass": null,
19+
"body": {
20+
"type": "ClassBody",
21+
"start":10,"end":31,"loc":{"start":{"line":1,"column":10},"end":{"line":4,"column":1}},
22+
"body": [
23+
{
24+
"type": "ClassProperty",
25+
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10}},
26+
"static": false,
27+
"key": {
28+
"type": "Identifier",
29+
"start":14,"end":22,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10},"identifierName":"accessor"},
30+
"name": "accessor"
31+
},
32+
"computed": false,
33+
"value": null
34+
},
35+
{
36+
"type": "ClassProperty",
37+
"start":25,"end":29,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":6}},
38+
"static": false,
39+
"key": {
40+
"type": "Identifier",
41+
"start":25,"end":28,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":5},"identifierName":"bar"},
42+
"name": "bar"
43+
},
44+
"computed": false,
45+
"value": null
46+
}
47+
]
48+
}
49+
}
50+
],
51+
"directives": []
52+
}
53+
}

0 commit comments

Comments
 (0)