New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decorators 2 Transform [WIP] #6107

Closed
wants to merge 23 commits into
base: master
from
Commits
Jump to file or symbol
Failed to load files and symbols.
+184 −0
Diff settings

Always

Just for now

Viewing a subset of changes. View all

WIP decorator transform initial commit

  • Loading branch information...
peey committed Jul 21, 2017
commit 9904b61f6e1b75f15208f1eb0e1c02076ccba1ea
@@ -0,0 +1,20 @@
{
"name": "babel-plugin-transform-decorators-2",
"version": "7.0.0-alpha.15",
"author": "Peeyush Kushwaha <peeyush.p97@gmail.com>",
"license": "MIT",
"description": "Transformer for stage-2 decorators",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-decorators-2",
"main": "lib/index.js",
"keywords": [
"babel",
"babel-plugin",
"decorators"
],
"dependencies": {
"babel-plugin-syntax-decorators-2": "7.0.0-alpha.15"
},
"devDependencies": {
"babel-helper-plugin-test-runner": "7.0.0-alpha.15"
}
}
@@ -0,0 +1,125 @@
import syntaxDecorators2 from "babel-plugin-syntax-decorators-2";
//TODO: will have to check for dot (.) access to reserved keywords in decorator members
export default function({ types: t }) {
// goes over the methods of current class and prepares decorators
// expects path of a ClassExpression
function takeMethodDecorators(path) {
const body = path.get("body.body");
// a collection of [decoratedKey, decorators] or [decoratedKey, decorators, true] for static properties
const entries = [];
for (const method of body) {
const { node } = method;
if (!method.isClassMethod()) continue;
if (!node.decorators || !method.node.decorators.length) continue;
const decorators = method.node.decorators
.map(d => d.expression)
.reverse(); // reverse for correct evaluation order
node.decorators = []; //TODO: should we remove from path? method.get("decorators") doesn't work
const entry = [];
if (node.computed) {
// if it is computed, we need an identifier to refer to it later
// so we can avoid evaluating the expression twice
if (t.isAssignmentExpression(node.key)) {
entry.push(node.key.left);
} else {
const parent = path.findParent(p => p.parentPath.isBlock());
const ref = method.scope.generateUidIdentifier("key");
parent.insertBefore(
t.variableDeclaration("let", [t.variableDeclarator(ref)]),
);
const replacement = t.sequenceExpression([
t.assignmentExpression("=", ref, node.key),
]);
//method.get("key").replaceWith(); FIXME: this should work
node.key = replacement;
entry.push(ref);
}
} else {
entry.push(t.stringLiteral(node.key.name));
}
entry.push(t.arrayExpression(decorators));
if (node.static) {
entry.push(t.booleanLiteral(true));
}
entries.push(t.arrayExpression(entry));
}
return t.arrayExpression(entries);
}
// expects path of a ClassExpression
function takeClassDecorators(path) {
// reverse for correct decorator evaluation order
const decorators = path.node.decorators.map(d => d.expression).reverse();
path.node.decorators = [];
return t.arrayExpression(decorators);
}
return {
inherits: syntaxDecorators2,
visitor: {
// export default is a special case since it expects and expression rather than a declaration
ExportDefaultDeclaration(path) {
const classPath = path.get("declaration");
if (!classPath.isClassDeclaration()) return;

This comment has been minimized.

@nicolo-ribaudo

nicolo-ribaudo Aug 16, 2017

Member

I think we can also return if the class doesn't have any decorator?

if (classPath.node.id) {
const ref = classPath.node.id;
path.insertBefore(
t.variableDeclaration("let", [t.variableDeclarator(ref)]),
);
classPath.replaceWith(
t.assignmentExpression("=", ref, t.toExpression(classPath.node)),
);
} else {
classPath.replaceWith(t.toExpression(classPath));
}
},
// replace declaration with a let declaration
ClassDeclaration(path) {
const { node } = path;

This comment has been minimized.

@nicolo-ribaudo

nicolo-ribaudo Aug 16, 2017

Member

I think we can also return if the class doesn't have any decorator?

Also here

const ref = node.id || path.scope.generateUidIdentifier("class");
path.replaceWith(
t.variableDeclaration("let", [
t.variableDeclarator(ref, t.toExpression(node)),
]),
);
},
ClassExpression(path) {
const methodDecorators = takeMethodDecorators(path);
const classDecorators = takeClassDecorators(path);
if (
methodDecorators.elements.length == 0 &&
classDecorators.elements.length == 0
) {
return;

This comment has been minimized.

@littledan

littledan Aug 30, 2017

When will this be true when hasDecorators is already true?

}
path.replaceWith(
t.callExpression(t.identifier("decorate"), [
path.node,
methodDecorators,
classDecorators,
]),
);
},
ClassMethod(path) {
if (path.get("decorators")) {
path.get("decorators").forEach(p => p.remove());
path.get("body").unshiftContainer("body", t.identifier("yo"));
}
},
},
};
}
@@ -0,0 +1,3 @@
{
"plugins": ["transform-decorators-2"]
}
@@ -0,0 +1,7 @@
let decorate = () => void 0, dec, bar, foo = {bar: () => void 0}, baz, decorator; //just to supress ReferenceErrors
@decorator class Bizz {
@dec m1() {};
@bar @foo.bar(baz) m2() {};
@dec static [3 + 7] () {};
}
@@ -0,0 +1,26 @@
let decorate = () => void 0,
dec,
bar,
foo = {
bar: () => void 0
},
baz,
decorator; //just to supress ReferenceErrors
let _key;
let Bizz = decorate(class Bizz {
m1() {
yo
}
m2() {
yo
}
static [(_key = 3 + 7)]() {
yo
}
}, [["m1", [dec]], ["m2", [foo.bar(baz), bar]], [_key, [dec], true]], [decorator]);
@@ -0,0 +1,3 @@
import runner from "babel-helper-plugin-test-runner";
runner(__dirname);
ProTip! Use n and p to navigate between commits in a pull request.