add support for object literal decorators - fixes #1154
This commit is contained in:
@@ -516,10 +516,15 @@ pp.parseObj = function(isPattern, refShorthandDefaultPos) {
|
||||
if (this.afterTrailingComma(tt.braceR)) break
|
||||
} else first = false
|
||||
|
||||
while (this.type === tt.at) {
|
||||
this.decorators.push(this.parseDecorator())
|
||||
}
|
||||
|
||||
let prop = this.startNode(), isGenerator = false, isAsync = false, start
|
||||
if (this.options.features["es7.objectRestSpread"] && this.type === tt.ellipsis) {
|
||||
prop = this.parseSpread()
|
||||
prop.type = "SpreadProperty"
|
||||
this.takeDecorators(prop)
|
||||
node.properties.push(prop)
|
||||
continue
|
||||
}
|
||||
@@ -545,8 +550,12 @@ pp.parseObj = function(isPattern, refShorthandDefaultPos) {
|
||||
}
|
||||
this.parseObjPropValue(prop, start, isGenerator, isAsync, isPattern, refShorthandDefaultPos);
|
||||
this.checkPropClash(prop, propHash)
|
||||
this.takeDecorators(prop)
|
||||
node.properties.push(this.finishNode(prop, "Property"))
|
||||
}
|
||||
if (this.decorators.length) {
|
||||
this.raise(this.start, "You have trailing decorators with no property");
|
||||
}
|
||||
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
|
||||
}
|
||||
|
||||
|
||||
@@ -475,7 +475,7 @@ pp.parseClass = function(node, isStatement) {
|
||||
if (this.eat(tt.semi)) continue
|
||||
if (this.type === tt.at) {
|
||||
this.decorators.push(this.parseDecorator())
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
var method = this.startNode()
|
||||
this.takeDecorators(method)
|
||||
|
||||
@@ -59,6 +59,7 @@ export default class File {
|
||||
"defaults",
|
||||
"create-class",
|
||||
"create-decorated-class",
|
||||
"create-decorated-object",
|
||||
"tagged-template-literal",
|
||||
"tagged-template-literal-loose",
|
||||
"interop-require",
|
||||
|
||||
@@ -4,17 +4,43 @@ import each from "lodash/collection/each";
|
||||
import has from "lodash/object/has";
|
||||
import * as t from "../../types";
|
||||
|
||||
export function push(mutatorMap, key, kind, computed, value) {
|
||||
var alias = t.toKeyAlias({ computed }, key);
|
||||
export function push(mutatorMap, node, kind, file) {
|
||||
var alias = t.toKeyAlias(node);
|
||||
|
||||
//
|
||||
|
||||
var map = {};
|
||||
if (has(mutatorMap, alias)) map = mutatorMap[alias];
|
||||
mutatorMap[alias] = map;
|
||||
|
||||
map._key = key;
|
||||
if (computed) map._computed = true;
|
||||
//
|
||||
|
||||
map[kind] = value;
|
||||
map._inherits ||= [];
|
||||
map._inherits.push(node);
|
||||
|
||||
map._key = node.key;
|
||||
|
||||
if (node.computed) {
|
||||
map._computed = true;
|
||||
}
|
||||
|
||||
if (node.decorators) {
|
||||
var decorators = map.decorators ||= t.arrayExpression([]);
|
||||
decorators.elements = decorators.elements.concat(node.decorators.map(dec => dec.expression));
|
||||
}
|
||||
|
||||
if (map.value || map.initializer) {
|
||||
throw file.errorWithNode(node, "Key conflict with sibling node");
|
||||
}
|
||||
|
||||
if (node.kind === "init") kind = "value";
|
||||
if (node.kind === "get") kind = "get";
|
||||
if (node.kind === "set") kind = "set";
|
||||
|
||||
t.inheritsComments(node.value, node);
|
||||
map[kind] = node.value;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
export function hasComputed(mutatorMap) {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
(function (descriptors) {
|
||||
var target = {};
|
||||
|
||||
for (var i = 0; i < descriptors.length; i ++) {
|
||||
var descriptor = descriptors[i];
|
||||
var decorators = descriptor.decorators;
|
||||
var key = descriptor.key;
|
||||
|
||||
// don't want to expose these to userland since i know people will rely on them
|
||||
// and think it's spec behaviour
|
||||
delete descriptor.key;
|
||||
delete descriptor.decorators;
|
||||
|
||||
descriptor.enumerable = true;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
|
||||
if (decorators) {
|
||||
for (var f = 0; f < decorators.length; f++) {
|
||||
var decorator = decorators[f];
|
||||
if (typeof decorator === "function") {
|
||||
descriptor = decorator(target, key, descriptor) || descriptor;
|
||||
} else {
|
||||
throw new TypeError("The decorator for method " + descriptor.key + " is of the invalid type " + typeof decorator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(target, key, descriptor);
|
||||
}
|
||||
|
||||
return target;
|
||||
})
|
||||
@@ -5,14 +5,14 @@ export function check(node) {
|
||||
return t.isProperty(node) && (node.kind === "get" || node.kind === "set");
|
||||
}
|
||||
|
||||
export function ObjectExpression(node) {
|
||||
export function ObjectExpression(node, parent, scope, file) {
|
||||
var mutatorMap = {};
|
||||
var hasAny = false;
|
||||
|
||||
node.properties = node.properties.filter(function (prop) {
|
||||
if (prop.kind === "get" || prop.kind === "set") {
|
||||
hasAny = true;
|
||||
defineMap.push(mutatorMap, prop.key, prop.kind, prop.computed, prop.value);
|
||||
defineMap.push(mutatorMap, prop, prop.kind, file);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -243,44 +243,15 @@ class ClassTransformer {
|
||||
mutatorMap = this.instanceMutatorMap;
|
||||
}
|
||||
|
||||
var alias = t.toKeyAlias(node);
|
||||
|
||||
//
|
||||
|
||||
var map = {};
|
||||
if (has(mutatorMap, alias)) map = mutatorMap[alias];
|
||||
mutatorMap[alias] = map;
|
||||
|
||||
//
|
||||
|
||||
map._inherits ||= [];
|
||||
map._inherits.push(node);
|
||||
|
||||
map._key = node.key;
|
||||
var map = defineMap.push(mutatorMap, node, kind, this.file);
|
||||
|
||||
if (enumerable) {
|
||||
map.enumerable = t.literal(true)
|
||||
}
|
||||
|
||||
if (node.computed) {
|
||||
map._computed = true;
|
||||
}
|
||||
|
||||
if (node.decorators) {
|
||||
if (map.decorators) {
|
||||
this.hasDecorators = true;
|
||||
var decorators = map.decorators ||= t.arrayExpression([]);
|
||||
decorators.elements = decorators.elements.concat(node.decorators.map(dec => dec.expression));
|
||||
}
|
||||
|
||||
if (map.value || map.initializer) {
|
||||
throw this.file.errorWithNode(node, "Key conflict with sibling node");
|
||||
}
|
||||
|
||||
if (node.kind === "get") kind = "get";
|
||||
if (node.kind === "set") kind = "set";
|
||||
|
||||
t.inheritsComments(node.value, node);
|
||||
map[kind] = node.value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,33 @@
|
||||
import * as defineMap from "../../helpers/define-map";
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var metadata = {
|
||||
optional: true,
|
||||
stage: 1
|
||||
};
|
||||
|
||||
export function check(node) {
|
||||
return !!node.decorators;
|
||||
}
|
||||
|
||||
export function ObjectExpression(node, parent, scope, file) {
|
||||
var hasDecorators = false;
|
||||
for (var i = 0; i < node.properties.length; i++) {
|
||||
var prop = node.properties[i];
|
||||
if (prop.decorators) {
|
||||
hasDecorators = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasDecorators) return;
|
||||
|
||||
var mutatorMap = {};
|
||||
|
||||
for (var i = 0; i < node.properties.length; i++) {
|
||||
defineMap.push(mutatorMap, node.properties[i], null, file);
|
||||
}
|
||||
|
||||
var obj = defineMap.toClassObject(mutatorMap);
|
||||
obj = defineMap.toComputedObjectFromClass(obj);
|
||||
return t.callExpression(file.addHelper("create-decorated-object"), [obj]);
|
||||
}
|
||||
|
||||
@@ -2787,6 +2787,66 @@ test('class Foo { @bar static foo = "bar"; }', {
|
||||
features: { "es7.classProperties": true, "es7.decorators": true }
|
||||
});
|
||||
|
||||
test("var obj = { @foo bar: 'wow' };", {
|
||||
"start": 0,
|
||||
"body": [{
|
||||
"start": 0,
|
||||
"declarations": [{
|
||||
"start": 4,
|
||||
"id": {
|
||||
"start": 4,
|
||||
"name": "obj",
|
||||
"type": "Identifier",
|
||||
"end": 7
|
||||
},
|
||||
"init": {
|
||||
"start": 10,
|
||||
"properties": [{
|
||||
"start": 17,
|
||||
"key": {
|
||||
"start": 17,
|
||||
"name": "bar",
|
||||
"type": "Identifier",
|
||||
"end": 20
|
||||
},
|
||||
"value": {
|
||||
"start": 22,
|
||||
"value": "wow",
|
||||
"raw": "'wow'",
|
||||
"type": "Literal",
|
||||
"end": 27
|
||||
},
|
||||
"kind": "init",
|
||||
"decorators": [{
|
||||
"start": 12,
|
||||
"expression": {
|
||||
"start": 13,
|
||||
"name": "foo",
|
||||
"type": "Identifier",
|
||||
"end": 16
|
||||
},
|
||||
"type": "Decorator",
|
||||
"end": 16
|
||||
}],
|
||||
"type": "Property",
|
||||
"end": 27
|
||||
}],
|
||||
"type": "ObjectExpression",
|
||||
"end": 29
|
||||
},
|
||||
"type": "VariableDeclarator",
|
||||
"end": 29
|
||||
}],
|
||||
"kind": "var",
|
||||
"type": "VariableDeclaration",
|
||||
"end": 30
|
||||
}],
|
||||
"type": "Program",
|
||||
"end": 30
|
||||
}, {
|
||||
features: { "es7.decorators": true }
|
||||
});
|
||||
|
||||
// ES7 export extensions - https://github.com/leebyron/ecmascript-more-export-from
|
||||
|
||||
test('export foo, { bar } from "bar";', {
|
||||
|
||||
@@ -10,11 +10,11 @@ var Foo = (function () {
|
||||
|
||||
babelHelpers.createDecoratedClass(Foo, [{
|
||||
key: "foo",
|
||||
enumerable: true,
|
||||
decorators: [bar],
|
||||
initializer: function () {
|
||||
return "Bar";
|
||||
}
|
||||
},
|
||||
enumerable: true
|
||||
}], null, _instanceInitializers);
|
||||
return Foo;
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -9,12 +9,12 @@ var Foo = (function () {
|
||||
|
||||
babelHelpers.createDecoratedClass(Foo, null, [{
|
||||
key: "foo",
|
||||
enumerable: true,
|
||||
decorators: [bar],
|
||||
initializer: function () {
|
||||
return "Bar";
|
||||
}
|
||||
},
|
||||
enumerable: true
|
||||
}], null, _staticInitializers);
|
||||
Foo.foo = _staticInitializers.foo.call(Foo);
|
||||
return Foo;
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -10,9 +10,9 @@ var Foo = (function () {
|
||||
|
||||
babelHelpers.createDecoratedClass(Foo, [{
|
||||
key: "foo",
|
||||
enumerable: true,
|
||||
decorators: [bar],
|
||||
initializer: function () {}
|
||||
initializer: function () {},
|
||||
enumerable: true
|
||||
}], null, _instanceInitializers);
|
||||
return Foo;
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -9,10 +9,10 @@ var Foo = (function () {
|
||||
|
||||
babelHelpers.createDecoratedClass(Foo, null, [{
|
||||
key: "foo",
|
||||
enumerable: true,
|
||||
decorators: [bar],
|
||||
initializer: function () {}
|
||||
initializer: function () {},
|
||||
enumerable: true
|
||||
}], null, _staticInitializers);
|
||||
Foo.foo = _staticInitializers.foo.call(Foo);
|
||||
return Foo;
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
var autobind = function (target, name, descriptor) {
|
||||
var fn = descriptor.value;
|
||||
delete descriptor.value;
|
||||
delete descriptor.writable;
|
||||
descriptor.get = function () {
|
||||
return fn.bind(this);
|
||||
};
|
||||
};
|
||||
|
||||
var person = {
|
||||
first: "Sebastian",
|
||||
last: "McKenzie",
|
||||
|
||||
@autobind
|
||||
getName() {
|
||||
return `${this.first} ${this.last}`;
|
||||
}
|
||||
}
|
||||
|
||||
assert.equal(person.getName.call(null), "Sebastian McKenzie")
|
||||
@@ -0,0 +1,11 @@
|
||||
var obj = {
|
||||
@foo
|
||||
get foo() {
|
||||
|
||||
},
|
||||
|
||||
@foo
|
||||
set foo() {
|
||||
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
var obj = babelHelpers.createDecoratedObject([{
|
||||
key: "foo",
|
||||
decorators: [foo, foo],
|
||||
get: function get() {},
|
||||
set: function set() {}
|
||||
}]);
|
||||
@@ -0,0 +1,6 @@
|
||||
var obj = {
|
||||
@foo
|
||||
get foo() {
|
||||
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var obj = babelHelpers.createDecoratedObject([{
|
||||
key: "foo",
|
||||
decorators: [foo],
|
||||
get: function get() {}
|
||||
}]);
|
||||
@@ -0,0 +1,6 @@
|
||||
var obj = {
|
||||
@foo
|
||||
set foo() {
|
||||
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var obj = babelHelpers.createDecoratedObject([{
|
||||
key: "foo",
|
||||
decorators: [foo],
|
||||
set: function set() {}
|
||||
}]);
|
||||
@@ -0,0 +1,11 @@
|
||||
var obj = {
|
||||
@foo
|
||||
bar() {
|
||||
|
||||
},
|
||||
|
||||
@bar
|
||||
foo: "lol",
|
||||
|
||||
yes: "wow"
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
var obj = babelHelpers.createDecoratedObject([{
|
||||
key: "bar",
|
||||
decorators: [foo],
|
||||
value: function value() {}
|
||||
}, {
|
||||
key: "foo",
|
||||
decorators: [bar],
|
||||
value: "lol"
|
||||
}, {
|
||||
key: "yes",
|
||||
value: "wow"
|
||||
}]);
|
||||
Reference in New Issue
Block a user