From d1150f66b4f55f705228b0c01e8eb5df19c78abb Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Sun, 11 Sep 2016 17:16:00 +0300 Subject: [PATCH] Support computed class property names (#4499) ** Depends on babel/babylon#121 ** * `babel-types`: Add `computed` field to `ClassProperty` * `babel-plugin-transform-class-properties`: handle computed property names correctly * `babel-generator`: add tests for class properties (computed/literal, static/instance) --- .../babel-generator/src/generators/classes.js | 8 +++++++- .../types/ClassBody-ClassProperty/actual.js | 19 +++++++++++++++++++ .../types/ClassBody-ClassProperty/expected.js | 19 +++++++++++++++++++ packages/babel-generator/test/index.js | 1 + .../src/index.js | 5 +++-- .../general/instance-computed/actual.js | 3 +++ .../general/instance-computed/expected.js | 4 ++++ packages/babel-types/src/definitions/flow.js | 9 ++++++++- packages/babel-types/src/validators.js | 7 ++++++- 9 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/actual.js create mode 100644 packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/expected.js create mode 100644 packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/actual.js create mode 100644 packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/expected.js diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index 841473324aff..db3151f51693 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -55,7 +55,13 @@ export function ClassProperty(node: Object) { this.word("static"); this.space(); } - this.print(node.key, node); + if (node.computed) { + this.token("["); + this.print(node.key, node); + this.token("]"); + } else { + this.print(node.key, node); + } this.print(node.typeAnnotation, node); if (node.value) { this.space(); diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/actual.js b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/actual.js new file mode 100644 index 000000000000..25cd363323cd --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/actual.js @@ -0,0 +1,19 @@ +class Foo { + foo; + foo = 1; + "foo"; + "foo" = 1; + ["foo"]; + ["foo"] = 1; + ["f" + "oo"]; + ["f" + "oo"] = 1; + + static foo; + static foo = 1; + static "foo"; + static "foo" = 1; + static ["foo"]; + static ["foo"] = 1; + static ["f" + "oo"]; + static ["f" + "oo"] = 1; +} diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/expected.js b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/expected.js new file mode 100644 index 000000000000..25cd363323cd --- /dev/null +++ b/packages/babel-generator/test/fixtures/types/ClassBody-ClassProperty/expected.js @@ -0,0 +1,19 @@ +class Foo { + foo; + foo = 1; + "foo"; + "foo" = 1; + ["foo"]; + ["foo"] = 1; + ["f" + "oo"]; + ["f" + "oo"] = 1; + + static foo; + static foo = 1; + static "foo"; + static "foo" = 1; + static ["foo"]; + static ["foo"] = 1; + static ["f" + "oo"]; + static ["f" + "oo"] = 1; +} diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index 6d5fbeda3b15..450de50ae1d9 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -170,6 +170,7 @@ suites.forEach(function (testSuite) { "exportExtensions", "functionBind", "classConstructorCall", + "classProperties", ], strictMode: false, sourceType: "module", diff --git a/packages/babel-plugin-transform-class-properties/src/index.js b/packages/babel-plugin-transform-class-properties/src/index.js index ed0feea20481..5744b14afc90 100644 --- a/packages/babel-plugin-transform-class-properties/src/index.js +++ b/packages/babel-plugin-transform-class-properties/src/index.js @@ -58,14 +58,15 @@ export default function ({ types: t }) { if (!propNode.value) continue; let isStatic = propNode.static; + let isComputed = propNode.computed || t.isLiteral(prop.key); if (isStatic) { nodes.push(t.expressionStatement( - t.assignmentExpression("=", t.memberExpression(ref, propNode.key), propNode.value) + t.assignmentExpression("=", t.memberExpression(ref, propNode.key, isComputed), propNode.value) )); } else { instanceBody.push(t.expressionStatement( - t.assignmentExpression("=", t.memberExpression(t.thisExpression(), propNode.key), propNode.value) + t.assignmentExpression("=", t.memberExpression(t.thisExpression(), propNode.key, isComputed), propNode.value) )); } } diff --git a/packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/actual.js b/packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/actual.js new file mode 100644 index 000000000000..5ad5e1f5618d --- /dev/null +++ b/packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/actual.js @@ -0,0 +1,3 @@ +class Foo { + [bar] = "foo"; +} diff --git a/packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/expected.js b/packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/expected.js new file mode 100644 index 000000000000..913f3dde4603 --- /dev/null +++ b/packages/babel-plugin-transform-class-properties/test/fixtures/general/instance-computed/expected.js @@ -0,0 +1,4 @@ +var Foo = function Foo() { + babelHelpers.classCallCheck(this, Foo); + this[bar] = "foo"; +}; diff --git a/packages/babel-types/src/definitions/flow.js b/packages/babel-types/src/definitions/flow.js index ca0b99864171..73b7f423a21d 100644 --- a/packages/babel-types/src/definitions/flow.js +++ b/packages/babel-types/src/definitions/flow.js @@ -1,4 +1,6 @@ -import defineType from "./index"; +import defineType, { + assertValueType +} from "./index"; defineType("AnyTypeAnnotation", { aliases: ["Flow", "FlowBaseAnnotation"], @@ -42,8 +44,13 @@ defineType("ClassImplements", { defineType("ClassProperty", { visitor: ["key", "value", "typeAnnotation", "decorators"], + builder: ["key", "value", "computed", "typeAnnotation", "decorators"], aliases: ["Flow", "Property"], fields: { + computed: { + validate: assertValueType("boolean"), + default: false + } // todo } }); diff --git a/packages/babel-types/src/validators.js b/packages/babel-types/src/validators.js index 720579d87ee4..46b2c81fba8a 100644 --- a/packages/babel-types/src/validators.js +++ b/packages/babel-types/src/validators.js @@ -100,9 +100,14 @@ export function isReferenced(node: Object, parent: Object): boolean { return parent.name !== node; // no: class { NODE = value; } + // yes: class { [NODE] = value; } // yes: class { key = NODE; } case "ClassProperty": - return parent.value === node; + if (parent.key === node) { + return parent.computed; + } else { + return parent.value === node; + } // no: import NODE from "foo"; // no: import * as NODE from "foo";