Computed class properties (#4500)

* 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)

* doc: Update babel-types with ClassProperty.computed

* chore(package): update babylon to v6.11.0

* babel-types: move ClassProperty.computed to be last builder arg
This commit is contained in:
Moti Zilberman 2016-09-26 18:46:00 +03:00 committed by Daniel Tschinder
parent a81a0d0f84
commit 03d772c2ec
14 changed files with 76 additions and 10 deletions

View File

@ -33,7 +33,7 @@
"babel-register": "^6.14.0", "babel-register": "^6.14.0",
"babel-traverse": "^6.14.0", "babel-traverse": "^6.14.0",
"babel-types": "^6.14.0", "babel-types": "^6.14.0",
"babylon": "^6.9.0", "babylon": "^6.11.0",
"convert-source-map": "^1.1.0", "convert-source-map": "^1.1.0",
"debug": "^2.1.1", "debug": "^2.1.1",
"json5": "^0.4.0", "json5": "^0.4.0",

View File

@ -21,6 +21,6 @@
}, },
"devDependencies": { "devDependencies": {
"babel-helper-fixtures": "^6.9.0", "babel-helper-fixtures": "^6.9.0",
"babylon": "^6.9.0" "babylon": "^6.11.0"
} }
} }

View File

@ -55,7 +55,13 @@ export function ClassProperty(node: Object) {
this.word("static"); this.word("static");
this.space(); 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); this.print(node.typeAnnotation, node);
if (node.value) { if (node.value) {
this.space(); this.space();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -170,6 +170,7 @@ suites.forEach(function (testSuite) {
"exportExtensions", "exportExtensions",
"functionBind", "functionBind",
"classConstructorCall", "classConstructorCall",
"classProperties",
], ],
strictMode: false, strictMode: false,
sourceType: "module", sourceType: "module",

View File

@ -58,14 +58,15 @@ export default function ({ types: t }) {
if (!propNode.value) continue; if (!propNode.value) continue;
let isStatic = propNode.static; let isStatic = propNode.static;
let isComputed = propNode.computed || t.isLiteral(prop.key);
if (isStatic) { if (isStatic) {
nodes.push(t.expressionStatement( nodes.push(t.expressionStatement(
t.assignmentExpression("=", t.memberExpression(ref, propNode.key), propNode.value) t.assignmentExpression("=", t.memberExpression(ref, propNode.key, isComputed), propNode.value)
)); ));
} else { } else {
instanceBody.push(t.expressionStatement( 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)
)); ));
} }
} }

View File

@ -0,0 +1,3 @@
class Foo {
[bar] = "foo";
}

View File

@ -0,0 +1,4 @@
var Foo = function Foo() {
babelHelpers.classCallCheck(this, Foo);
this[bar] = "foo";
};

View File

@ -8,7 +8,7 @@
"repository": "https://github.com/babel/babel/tree/master/packages/babel-template", "repository": "https://github.com/babel/babel/tree/master/packages/babel-template",
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
"babylon": "^6.9.0", "babylon": "^6.11.0",
"babel-traverse": "^6.15.0", "babel-traverse": "^6.15.0",
"babel-types": "^6.15.0", "babel-types": "^6.15.0",
"babel-runtime": "^6.9.0", "babel-runtime": "^6.9.0",

View File

@ -12,7 +12,7 @@
"babel-messages": "^6.8.0", "babel-messages": "^6.8.0",
"babel-runtime": "^6.9.0", "babel-runtime": "^6.9.0",
"babel-types": "^6.15.0", "babel-types": "^6.15.0",
"babylon": "^6.9.0", "babylon": "^6.11.0",
"debug": "^2.2.0", "debug": "^2.2.0",
"globals": "^8.3.0", "globals": "^8.3.0",
"invariant": "^2.2.0", "invariant": "^2.2.0",

View File

@ -217,7 +217,7 @@ Aliases: `Function`, `Scopable`, `BlockParent`, `FunctionParent`, `Method`
- `returnType` (default: `null`) - `returnType` (default: `null`)
- `typeParameters` (default: `null`) - `typeParameters` (default: `null`)
### t.classProperty(key, value, typeAnnotation, decorators) ### t.classProperty(key, value, typeAnnotation, decorators, computed)
See also `t.isClassProperty(node, opts)` and `t.assertClassProperty(node, opts)`. See also `t.isClassProperty(node, opts)` and `t.assertClassProperty(node, opts)`.
@ -227,6 +227,7 @@ Aliases: `Flow`, `Property`
- `value` (required) - `value` (required)
- `typeAnnotation` (required) - `typeAnnotation` (required)
- `decorators` (required) - `decorators` (required)
- `computed`: `boolean` (default: `false`)
### t.conditionalExpression(test, consequent, alternate) ### t.conditionalExpression(test, consequent, alternate)

View File

@ -1,4 +1,6 @@
import defineType from "./index"; import defineType, {
assertValueType
} from "./index";
defineType("AnyTypeAnnotation", { defineType("AnyTypeAnnotation", {
aliases: ["Flow", "FlowBaseAnnotation"], aliases: ["Flow", "FlowBaseAnnotation"],
@ -42,8 +44,13 @@ defineType("ClassImplements", {
defineType("ClassProperty", { defineType("ClassProperty", {
visitor: ["key", "value", "typeAnnotation", "decorators"], visitor: ["key", "value", "typeAnnotation", "decorators"],
builder: ["key", "value", "typeAnnotation", "decorators", "computed"],
aliases: ["Flow", "Property"], aliases: ["Flow", "Property"],
fields: { fields: {
computed: {
validate: assertValueType("boolean"),
default: false
}
// todo // todo
} }
}); });

View File

@ -100,9 +100,14 @@ export function isReferenced(node: Object, parent: Object): boolean {
return parent.name !== node; return parent.name !== node;
// no: class { NODE = value; } // no: class { NODE = value; }
// yes: class { [NODE] = value; }
// yes: class { key = NODE; } // yes: class { key = NODE; }
case "ClassProperty": 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 NODE from "foo";
// no: import * as NODE from "foo"; // no: import * as NODE from "foo";