add support for circular references and hoist all variable declarations

This commit is contained in:
Sebastian McKenzie
2015-01-01 22:30:28 +11:00
parent 27e9f9d616
commit c408432445
12 changed files with 145 additions and 98 deletions

View File

@@ -15,8 +15,13 @@ function SystemFormatter(file) {
util.inherits(SystemFormatter, AMDFormatter);
SystemFormatter.prototype._exportsWildcard = function (objectIdentifier) {
var leftIdentifier = t.identifier("i");
SystemFormatter.prototype._addImportSource = function (node, exportNode) {
node._importSource = exportNode.source && exportNode.source.value;
return node;
};
SystemFormatter.prototype._exportsWildcard = function (objectIdentifier, node) {
var leftIdentifier = this.file.generateUidIdentifier("key");
var valIdentifier = t.memberExpression(objectIdentifier, leftIdentifier, true);
var left = t.variableDeclaration("var", [
@@ -29,11 +34,12 @@ SystemFormatter.prototype._exportsWildcard = function (objectIdentifier) {
this.buildExportCall(leftIdentifier, valIdentifier)
]);
return t.forInStatement(left, right, block);
return this._addImportSource(t.forInStatement(left, right, block), node);
};
SystemFormatter.prototype._exportsAssign = function (id, init) {
return this.buildExportCall(t.literal(id.name), init, true);
SystemFormatter.prototype._exportsAssign = function (id, init, node) {
var call = this.buildExportCall(t.literal(id.name), init, true);
return this._addImportSource(call, node);
};
SystemFormatter.prototype.remapExportAssignment = function (node) {
@@ -49,28 +55,53 @@ SystemFormatter.prototype.buildExportCall = function (id, init, isStatement) {
}
};
SystemFormatter.prototype.buildRunnerSetters = function () {
return t.arrayExpression(_.map(this.ids, function (uid) {
var moduleIdentifier = t.identifier("m");
SystemFormatter.prototype.importSpecifier = function (specifier, node, nodes) {
AMDFormatter.prototype.importSpecifier.apply(this, arguments);
this._addImportSource(_.last(nodes), node);
};
return t.functionExpression(null, [moduleIdentifier], t.blockStatement([
t.assignmentExpression("=", uid, moduleIdentifier)
]));
SystemFormatter.prototype.buildRunnerSetters = function (block, hoistDeclarators) {
return t.arrayExpression(_.map(this.ids, function (uid, source) {
var nodes = [];
traverse(block, {
enter: function (node) {
if (node._importSource === source) {
if (t.isVariableDeclaration(node)) {
_.each(node.declarations, function (declar) {
hoistDeclarators.push(t.variableDeclarator(declar.id));
nodes.push(t.expressionStatement(
t.assignmentExpression("=", declar.id, declar.init)
));
});
} else {
nodes.push(node);
}
this.remove();
}
}
});
return t.functionExpression(null, [uid], t.blockStatement(nodes));
}));
};
SystemFormatter.prototype.transform = function (ast) {
var program = ast.program;
var hoistDeclarators = [];
var moduleName = this.getModuleName();
var moduleNameLiteral = t.literal(moduleName);
var block = t.blockStatement(program.body);
var runner = util.template("system", {
MODULE_NAME: moduleNameLiteral,
MODULE_DEPENDENCIES: t.arrayExpression(this.buildDependencyLiterals()),
EXPORT_IDENTIFIER: this.exportIdentifier,
SETTERS: this.buildRunnerSetters(),
EXECUTE: t.functionExpression(null, [], t.blockStatement(program.body))
SETTERS: this.buildRunnerSetters(block, hoistDeclarators),
EXECUTE: t.functionExpression(null, [], block)
}, true);
var handlerBody = runner.expression.arguments[2].body.body;
@@ -78,8 +109,55 @@ SystemFormatter.prototype.transform = function (ast) {
var returnStatement = handlerBody.pop();
// hoist up all variable declarations
traverse(block, {
enter: function (node, parent, scope) {
if (t.isFunction(node)) {
// nothing inside is accessible
return this.stop();
}
if (t.isVariableDeclaration(node)) {
if (node.kind !== "var" && !t.isProgram(parent)) { // let, const
// can't be accessed
return;
}
var nodes = [];
_.each(node.declarations, function (declar) {
hoistDeclarators.push(t.variableDeclarator(declar.id));
if (declar.init) {
// no initializer so we can just hoist it as-is
var assign = t.expressionStatement(t.assignmentExpression("=", declar.id, declar.init));
nodes.push(assign);
}
});
// for (var i in test)
// for (var i = 0;;)
if (t.isFor(parent)) {
if (parent.left === node) {
return node.declarations[0].id;
}
if (parent.init === node) {
return t.toSequenceExpression(nodes, scope);
}
}
return nodes;
}
}
});
if (hoistDeclarators.length) {
var hoistDeclar = t.variableDeclaration("var", hoistDeclarators);
hoistDeclar._blockHoist = true;
handlerBody.unshift(hoistDeclar);
}
// hoist up function declarations for circular references
traverse(program, {
traverse(block, {
enter: function (node) {
if (t.isFunction(node)) this.stop();
@@ -90,12 +168,6 @@ SystemFormatter.prototype.transform = function (ast) {
}
});
if (!_.isEmpty(this.ids)) {
handlerBody.push(t.variableDeclaration("var", _.map(this.ids, function (uid) {
return t.variableDeclarator(uid);
})));
}
handlerBody.push(returnStatement);
program.body = [runner];

View File

@@ -1,4 +1,5 @@
System.register([], function (_export) {
var Foo;
_export("default", foo);
function foo() {}
@@ -19,7 +20,7 @@ System.register([], function (_export) {
_export("default", function () {});
var Foo = function Foo() {};
Foo = function Foo() {};
_export("default", Foo);
}

View File

@@ -1,12 +1,10 @@
System.register(["foo"], function (_export) {
var _foo;
return {
setters: [function (m) {
_foo = m
for (var i in _foo) {
_export(i, _foo[i])
setters: [function (_foo) {
for (var _key in _foo) {
_export(_key, _foo[_key])
}
_export("foo", _foo.foo);
_export("foo", _foo.foo);
@@ -20,7 +18,6 @@ System.register(["foo"], function (_export) {
_export("default", _foo.foo);
_export("bar", _foo.bar);
}],
execute: function () {
"use strict";

View File

@@ -1,4 +1,5 @@
System.register([], function (_export) {
var foo, foo2, foo3, foo4, foo5, foo6, foo8;
_export("foo7", foo7);
function foo7() {}
@@ -7,13 +8,13 @@ System.register([], function (_export) {
execute: function () {
"use strict";
var foo = _export("foo", 1);
var foo2 = _export("foo2", function () {});
var foo3 = _export("foo3", undefined);
var foo4 = _export("foo4", 2);
var foo5 = _export("foo5", undefined);
var foo6 = _export("foo6", 3);
var foo8 = function foo8() {};
foo = _export("foo", 1);
foo2 = _export("foo2", function () {});
foo3 = _export("foo3", undefined);
foo4 = _export("foo4", 2);
foo5 = _export("foo5", undefined);
foo6 = _export("foo6", 3);
foo8 = function foo8() {};
_export("foo8", foo8);
}

View File

@@ -1,24 +1,20 @@
System.register(["./evens"], function (_export) {
var p;
var isEven, p, isOdd;
_export("nextOdd", nextOdd);
function nextOdd(n) {
return _export("p", p = isEven(n) ? n + 1 : n + 2);
}
var _evens;
return {
setters: [function (m) {
_evens = m
setters: [function (_evens) {
isEven = _evens.isEven;
}],
execute: function () {
"use strict";
var isEven = _evens.isEven;
_export("p", p = 5);
var isOdd = _export("isOdd", (function (isEven) {
p = _export("p", 5);
isOdd = _export("isOdd", (function (isEven) {
return function (n) {
return !isEven(n);
};

View File

@@ -1,14 +1,12 @@
System.register([], function (_export) {
var _foo;
System.register(["foo"], function (_export) {
var foo, foo;
return {
setters: [function (m) {
_foo = m
setters: [function (_foo) {
foo = _foo["default"];
foo = _foo["default"];
}],
execute: function () {
"use strict";
var foo = _foo["default"];
var foo = _foo["default"];
}
};
});

View File

@@ -1,13 +1,11 @@
System.register(["foo"], function (_export) {
var _foo;
var foo;
return {
setters: [function (m) {
_foo = m
setters: [function (_foo) {
foo = _foo;
}],
execute: function () {
"use strict";
var foo = _foo;
}
};
});
});

View File

@@ -1,14 +1,12 @@
System.register(["foo"], function (_export) {
var _foo;
var foo, xyz;
return {
setters: [function (m) {
_foo = m
setters: [function (_foo) {
foo = _foo["default"];
xyz = _foo.baz;
}],
execute: function () {
"use strict";
var foo = _foo["default"];
var xyz = _foo.baz;
}
};
});

View File

@@ -1,18 +1,16 @@
System.register(["foo"], function (_export) {
var _foo;
var bar, bar, baz, baz, baz, xyz;
return {
setters: [function (m) {
_foo = m
setters: [function (_foo) {
bar = _foo.bar;
bar = _foo.bar;
baz = _foo.baz;
baz = _foo.bar;
baz = _foo.bar;
xyz = _foo.xyz;
}],
execute: function () {
"use strict";
var bar = _foo.bar;
var bar = _foo.bar;
var baz = _foo.baz;
var baz = _foo.bar;
var baz = _foo.bar;
var xyz = _foo.xyz;
}
};
});
});

View File

@@ -1,15 +1,8 @@
System.register(["foo", "foo-bar", "./directory/foo-bar"], function (_export) {
var _foo, _fooBar, _directoryFooBar;
return {
setters: [function (m) {
_foo = m
}, function (m) {
_fooBar = m
}, function (m) {
_directoryFooBar = m
}],
setters: [function (_foo) {}, function (_fooBar) {}, function (_directoryFooBar) {}],
execute: function () {
"use strict";
}
};
});
});

View File

@@ -1,24 +1,18 @@
System.register(["foo", "foo-bar", "./directory/foo-bar"], function (_export) {
var _foo, _fooBar, _directoryFooBar;
var foo, foo, bar, bar, test;
return {
setters: [function (m) {
_foo = m
}, function (m) {
_fooBar = m
}, function (m) {
_directoryFooBar = m
}],
setters: [function (_foo) {
foo = _foo["default"];
foo = _foo;
bar = _foo.bar;
bar = _foo.foo;
}, function (_fooBar) {}, function (_directoryFooBar) {}],
execute: function () {
"use strict";
var foo = _foo["default"];
var foo = _foo;
var bar = _foo.bar;
var bar = _foo.foo;
_export("test", test);
var test = _export("test", 5);
test = _export("test", 5);
_export("default", test);
}
};

View File

@@ -1,10 +1,11 @@
System.register([], function (_export) {
var test;
return {
setters: [],
execute: function () {
"use strict";
var test = _export("test", 2);
test = _export("test", 2);
_export("test", test = 5);
_export("test", test += 1);