diff --git a/packages/babel-parser/ast/spec.md b/packages/babel-parser/ast/spec.md index 0e005fd2295e..a6fb48d138cf 100644 --- a/packages/babel-parser/ast/spec.md +++ b/packages/babel-parser/ast/spec.md @@ -1235,6 +1235,7 @@ interface ClassProperty <: Node { value: Expression; static: boolean; computed: boolean; + accessor: boolean; } ``` diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 8634676ca4e4..3a2e294fbfbe 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1373,6 +1373,14 @@ export default class StatementParser extends ExpressionParser { prop.computed = false; prop.key = key; prop.static = false; + + if ( + this.hasPlugin("decorators") && + this.getPluginOption("decorators", "version") === "modern" + ) { + prop.accessor = false; + } + classBody.body.push(this.parseClassProperty(prop)); return true; } @@ -1409,10 +1417,11 @@ export default class StatementParser extends ExpressionParser { ) { const publicMethod: $FlowSubtype = member; const privateMethod: $FlowSubtype = member; - const publicProp: $FlowSubtype = member; - const privateProp: $FlowSubtype = member; + const publicProp: $FlowSubtype = member; + const privateProp: $FlowSubtype = member; const method: typeof publicMethod | typeof privateMethod = publicMethod; + const prop: typeof publicProp | typeof privateProp = publicProp; const publicMember: typeof publicMethod | typeof publicProp = publicMethod; member.static = isStatic; @@ -1486,6 +1495,13 @@ export default class StatementParser extends ExpressionParser { allowsDirectSuper, ); } else if (this.isClassProperty()) { + if ( + this.hasPlugin("decorators") && + this.getPluginOption("decorators", "version") === "modern" + ) { + prop.accessor = false; + } + if (isPrivate) { this.pushClassPrivateProperty(classBody, privateProp); } else { @@ -1563,6 +1579,26 @@ 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); + + prop.accessor = true; + + if (isPrivate) { + this.pushClassPrivateProperty(classBody, privateProp); + } else { + this.pushClassProperty(classBody, publicProp); + } } else if (this.isLineTerminator()) { // an uninitialized class property (due to ASI, since we don't otherwise recognize the next token) if (isPrivate) { diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index cef0987a6864..7e2f9b3a2a86 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -813,6 +813,7 @@ export type ClassProperty = ClassMemberBase & type: "ClassProperty", key: Expression, value: ?Expression, // TODO: Not in spec that this is nullable. + accessor?: boolean, // Decorators proposal typeAnnotation?: ?TypeAnnotationBase, // TODO: Not in spec variance?: ?FlowVariance, // TODO: Not in spec @@ -828,6 +829,7 @@ export type ClassPrivateProperty = NodeBase & { value: ?Expression, // TODO: Not in spec that this is nullable. static: boolean, computed: false, + accessor?: boolean, // Decorators proposal // Flow and Typescript typeAnnotation?: ?TypeAnnotationBase, diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/basic-accessor/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/basic-accessor/input.js new file mode 100644 index 000000000000..735a7a78e304 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/basic-accessor/input.js @@ -0,0 +1,3 @@ +class Foo { + accessor bar; +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/basic-accessor/output.json b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/basic-accessor/output.json new file mode 100644 index 000000000000..f4b57591a939 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/basic-accessor/output.json @@ -0,0 +1,42 @@ +{ + "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": "ClassProperty", + "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, + "accessor": true, + "value": null + } + ] + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/field-named-accessor/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/field-named-accessor/input.js new file mode 100644 index 000000000000..fba4a91f8875 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/field-named-accessor/input.js @@ -0,0 +1,3 @@ +class Foo { + accessor = 123; +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/field-named-accessor/output.json b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/field-named-accessor/output.json new file mode 100644 index 000000000000..9f9598f566fb --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/field-named-accessor/output.json @@ -0,0 +1,50 @@ +{ + "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, + "accessor": 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": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/method-named-accessor/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/method-named-accessor/input.js new file mode 100644 index 000000000000..d4092463f89a --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/method-named-accessor/input.js @@ -0,0 +1,3 @@ +class Foo { + accessor() {} +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/method-named-accessor/output.json b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/method-named-accessor/output.json new file mode 100644 index 000000000000..b55c19130b12 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/method-named-accessor/output.json @@ -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": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/not-allowed-on-methods/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/not-allowed-on-methods/input.js new file mode 100644 index 000000000000..4e41f3511da0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/not-allowed-on-methods/input.js @@ -0,0 +1,5 @@ +class Foo { + accessor bar() { + + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/not-allowed-on-methods/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/not-allowed-on-methods/options.json new file mode 100644 index 000000000000..a1c98245e1ed --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/not-allowed-on-methods/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (2:14)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/private-accessor/input.js b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/private-accessor/input.js new file mode 100644 index 000000000000..9127772fc36b --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/private-accessor/input.js @@ -0,0 +1,3 @@ +class Foo { + accessor #bar; +} diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/private-accessor/output.json b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/private-accessor/output.json new file mode 100644 index 000000000000..e740f04334c6 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/accessor/private-accessor/output.json @@ -0,0 +1,46 @@ +{ + "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": "ClassPrivateProperty", + "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, + "accessor": true, + "value": null + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/decorators-modern/options.json b/packages/babel-parser/test/fixtures/experimental/decorators-modern/options.json new file mode 100644 index 000000000000..20edf62c0edf --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/decorators-modern/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["decorators", { "decoratorsBeforeExport": false, "version": "modern" }]] +}