Skip to content

Commit

Permalink
Adds the ClassAccessorProperty and ClassPrivateAccessorProperty node …
Browse files Browse the repository at this point in the history
…types

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
  • Loading branch information
pzuraq committed Aug 21, 2021
1 parent 10640b2 commit fa5d956
Show file tree
Hide file tree
Showing 26 changed files with 616 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
72 changes: 70 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,24 @@ export default class StatementParser extends ExpressionParser {
}

this.checkGetterSetterParams(publicMethod);
} else if (
isContextual &&
key.name === "accessor" &&
this.hasPlugin("decorators") &&
this.getPluginOption("decorators", "version") === "modern" &&
!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 +1672,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 +1779,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 fa5d956

Please sign in to comment.