add support for object literal decorators - fixes #1154

This commit is contained in:
Sebastian McKenzie
2015-04-11 16:30:55 -07:00
parent 2a9777cc20
commit 98c5255b91
22 changed files with 270 additions and 51 deletions

View File

@@ -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")
}

View File

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

View File

@@ -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",

View File

@@ -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) {

View File

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

View File

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

View File

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

View File

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