Skip to content

Commit

Permalink
Adds the simpleAutoAccessors parser plugin
Browse files Browse the repository at this point in the history
This is the first part of the implementation of the latest version of
the decorators proposal: https://github.com/tc39/proposal-decorators

The keyword is outlined here in the README: https://github.com/tc39/proposal-decorators#class-auto-accessors

It adds a `simpleAutoAccessors` plugin which can be used to parse the
`accessor` keyword into the ClassAccessorProperty node.
  • Loading branch information
pzuraq committed Sep 27, 2021
1 parent 14572e1 commit 35c26c1
Show file tree
Hide file tree
Showing 49 changed files with 1,087 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/babel-compat-data/data/plugins.json
Expand Up @@ -379,7 +379,7 @@
"chrome": "46",
"opera": "33",
"edge": "14",
"firefox": "45",
"firefox": "41",
"safari": "10",
"node": "5",
"ios": "10",
Expand Down
42 changes: 42 additions & 0 deletions packages/babel-generator/src/generators/classes.ts
Expand Up @@ -114,6 +114,48 @@ 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.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,
Expand Down
12 changes: 12 additions & 0 deletions packages/babel-parser/ast/spec.md
Expand Up @@ -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
Expand Down
57 changes: 54 additions & 3 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -1394,6 +1394,7 @@ export default class StatementParser extends ExpressionParser {
prop.computed = false;
prop.key = key;
prop.static = false;
classBody.body.push(this.parseClassProperty(prop));
return true;
}
Expand Down Expand Up @@ -1430,8 +1431,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;
Expand Down Expand Up @@ -1585,6 +1587,18 @@ export default class StatementParser extends ExpressionParser {
}

this.checkGetterSetterParams(publicMethod);
} else if (
isContextual &&
key.name === "accessor" &&
this.hasPlugin("simpleAutoAccessors") &&
!this.isLineTerminator()
) {
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) {
Expand Down Expand Up @@ -1666,6 +1680,33 @@ export default class StatementParser extends ExpressionParser {
);
}

pushClassAccessorProperty(
classBody: N.ClassBody,
prop: N.ClassAccessorProperty,
isPrivate: boolean,
) {
if (
!prop.computed &&
prop.key.type !== "PrivateName" &&
(prop.key.name === "constructor" || prop.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,
Expand Down Expand Up @@ -1743,8 +1784,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/proposal-class-fields/#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);
Expand Down
17 changes: 16 additions & 1 deletion packages/babel-parser/src/types.js
Expand Up @@ -782,7 +782,8 @@ export type ClassMember =
| ClassMethod
| ClassPrivateMethod
| ClassProperty
| ClassPrivateProperty;
| ClassPrivateProperty
| ClassAccessorProperty;
export type MethodLike =
| ObjectMethod
Expand Down Expand Up @@ -849,6 +850,20 @@ export type ClassPrivateProperty = NodeBase & {
override?: true,
};

export type ClassAccessorProperty = ClassMemberBase &
DeclarationBase & {
type: "ClassAccessorProperty",
key: Expression | PrivateName,
value: ?Expression, // TODO: Not in spec that this is nullable.

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 & {
Expand Down
@@ -0,0 +1,3 @@
class Foo {
accessor constructor;
}
@@ -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": []
}
}
@@ -0,0 +1,4 @@
class Foo {
accessor
bar;
}
@@ -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": []
}
}
@@ -0,0 +1,3 @@
class Foo {
accessor bar;
}
@@ -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": []
}
}
@@ -0,0 +1,3 @@
class Foo {
accessor ["constructor"];
}

0 comments on commit 35c26c1

Please sign in to comment.