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 ClassAccessorProperty and ClassPrivateAccessorProperty
node types.
  • Loading branch information
pzuraq committed Aug 21, 2021
1 parent 10640b2 commit fbe455a
Show file tree
Hide file tree
Showing 26 changed files with 618 additions and 2 deletions.
23 changes: 23 additions & 0 deletions packages/babel-parser/ast/spec.md
Expand Up @@ -1212,6 +1212,29 @@ interface ClassPrivateProperty <: Node {
}
```

## ClassAccessorProperty

```js
interface ClassAccessorProperty <: Node {
type: "ClassAccessorProperty";
key: Expression;
value: Expression;
static: boolean;
computed: boolean;
}
```

## ClassPrivateAccessorProperty

```js
interface ClassPrivateAccessorProperty <: Node {
type: "ClassPrivateAccessorProperty";
key: PrivateName;
value: Expression;
static: boolean;
}
```

## StaticBlock

```js
Expand Down
71 changes: 69 additions & 2 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -1379,6 +1379,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 @@ -1415,8 +1416,11 @@ 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 publicAccessorProp: $FlowSubtype<N.ClassAccessorProperty> = member;
const privateAccessorProp: $FlowSubtype<N.ClassPrivateAccessorProperty> =
member;
const method: typeof publicMethod | typeof privateMethod = publicMethod;
const publicMember: typeof publicMethod | typeof publicProp = publicMethod;
Expand Down Expand Up @@ -1569,6 +1573,23 @@ 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 "get/set": get the real name.
const isPrivate = this.match(tt.privateName);
this.parseClassElementName(publicProp);

if (isPrivate) {
this.pushClassPrivateAccessorProperty(classBody, privateAccessorProp);
} else {
this.pushClassAccessorProperty(classBody, publicAccessorProp);
}
} 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 @@ -1650,6 +1671,36 @@ export default class StatementParser extends ExpressionParser {
);
}

pushClassAccessorProperty(
classBody: N.ClassBody,
prop: N.ClassAccessorProperty,
) {
if (
!prop.computed &&
(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);
}

classBody.body.push(this.parseClassAccessorProperty(prop));
}

pushClassPrivateAccessorProperty(
classBody: N.ClassBody,
prop: N.ClassPrivateAccessorProperty,
) {
const node = this.parseClassPrivateAccessorProperty(prop);
classBody.body.push(node);

this.classScope.declarePrivateName(
this.getPrivateNameSV(node.key),
CLASS_ELEMENT_OTHER,
node.key.start,
);
}

pushClassMethod(
classBody: N.ClassBody,
method: N.ClassMethod,
Expand Down Expand Up @@ -1727,6 +1778,22 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "ClassProperty");
}

parseClassPrivateAccessorProperty(
node: N.ClassPrivateAccessorProperty,
): N.ClassPrivateAccessorProperty {
this.parseInitializer(node);
this.semicolon();
return this.finishNode(node, "ClassPrivateAccessorProperty");
}

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 {
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
Expand Down
31 changes: 31 additions & 0 deletions packages/babel-parser/src/types.js
Expand Up @@ -849,6 +849,37 @@ export type ClassPrivateProperty = NodeBase & {
override?: true,
};

export type ClassAccessorProperty = ClassMemberBase &
DeclarationBase & {
type: "ClassAccesorProperty",
key: Expression,
init?: 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 ClassPrivateAccessorProperty = NodeBase & {
type: "ClassPrivateAccessorProperty",
key: PrivateName,
init?: Expression, // TODO: Not in spec that this is nullable.
static: boolean,
computed: false,

// Flow and Typescript
typeAnnotation?: ?TypeAnnotationBase,

// TypeScript only
optional?: true,
definite?: true,
readonly?: true,
override?: 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,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,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": []
}
}
@@ -0,0 +1,3 @@
class Foo {
accessor ["bar"];
}
@@ -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": []
}
}
@@ -0,0 +1,3 @@
class Foo {
static accessor ["bar"];
}

0 comments on commit fbe455a

Please sign in to comment.