diff --git a/lib/6to5/file.js b/lib/6to5/file.js index 0611abd28a..002c6b0e9a 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -123,6 +123,17 @@ File.prototype.generate = function () { }; File.prototype.generateUid = function (name) { + // replace all non-valid identifiers with dashes + name = name.replace(/[^a-zA-Z0-9]/g, "-"); + + // remove all dashes and numbers from start of name + name = name.replace(/^[-0-9]+/, ""); + + // camel case + name = name.replace(/[-_\s]+(.)?/g, function(match, c){ + return c ? c.toUpperCase() : ""; + }); + var uids = this.uids; var i = uids[name] || 1; diff --git a/lib/6to5/modules/amd.js b/lib/6to5/modules/amd.js index e69de29bb2..6ffb6235ee 100644 --- a/lib/6to5/modules/amd.js +++ b/lib/6to5/modules/amd.js @@ -0,0 +1,106 @@ +module.exports = AMDFormatter; + +var CommonJSFormatter = require("./common"); +var util = require("../util"); +var b = require("ast-types").builders; +var _ = require("lodash"); + +function AMDFormatter(file) { + this.file = file; + this.ids = {}; +} + +AMDFormatter.prototype.transform = function (ast) { + var program = ast.program; + var body = program.body; + + // build an array of module names + + var names = []; + _.each(this.ids, function (id, name) { + names.push(b.literal(name)); + }); + names = b.arrayExpression(names); + + // add exports + + body.unshift(b.variableDeclaration("var", [ + b.variableDeclarator(b.identifier("exports"), b.objectExpression([])) + ])); + body.push(b.returnStatement(b.identifier("exports"))); + + // build up define container + + var container = b.functionExpression(null, _.values(this.ids), b.blockStatement(body)); + var call = b.callExpression(b.identifier("define"), [names, container]); + + program.body = [b.expressionStatement(call)]; +}; + +AMDFormatter.prototype._push = function (node) { + var id = node.source.value; + var ids = this.ids; + + if (ids[id]) { + return ids[id]; + } else { + return this.ids[id] = b.identifier(this.file.generateUid(id)); + } +}; + +AMDFormatter.prototype.import = function (node, nodes) { + this._push(node); +}; + +AMDFormatter.prototype.importSpecifier = function (specifier, node, nodes) { + var key = util.getSpecifierName(specifier); + var id = specifier.id; + + // import foo from "foo"; + if (specifier.default) { + id = b.identifier("default"); + } + + var ref; + + if (specifier.type === "ImportBatchSpecifier") { + // import * as bar from "foo"; + ref = this._push(node); + } else { + // import foo from "foo"; + ref = b.memberExpression(this._push(node), id, false); + } + + nodes.push(b.variableDeclaration("var", [ + b.variableDeclarator(key, ref) + ])); +}; + +AMDFormatter.prototype.export = CommonJSFormatter.prototype.export; + +AMDFormatter.prototype.exportSpecifier = function (specifier, node, nodes) { + var variableName = util.getSpecifierName(specifier); + + if (node.source) { + var object = this._push(node); + if (specifier.type === "ExportBatchSpecifier") { + // export * from "foo"; + nodes.push(util.template("exports-wildcard", { + OBJECT: object + }, true)); + } else { + // export { foo } from "test"; + nodes.push(util.template("exports-assign-key", { + VARIABLE_NAME: variableName.name, + OBJECT: object, + KEY: specifier.id + }, true)); + } + } else { + // export { foo }; + nodes.push(util.template("exports-assign", { + VALUE: specifier.id, + KEY: variableName + }, true)); + } +}; diff --git a/lib/6to5/modules/common.js b/lib/6to5/modules/common.js index 6008d02a18..768eebea65 100644 --- a/lib/6to5/modules/common.js +++ b/lib/6to5/modules/common.js @@ -1,25 +1,21 @@ -module.exports = CommonJSModuleFormatter; +module.exports = CommonJSFormatter; var util = require("../util"); var b = require("recast").types.builders; -var getSpecifierName = function (specifier) { - return specifier.name || specifier.id; -}; - -function CommonJSModuleFormatter(file) { +function CommonJSFormatter(file) { this.file = file; } -CommonJSModuleFormatter.prototype.import = function (node, nodes) { +CommonJSFormatter.prototype.import = function (node, nodes) { // import "foo"; nodes.push(util.template("require", { MODULE_NAME: node.source.raw }, true)); }; -CommonJSModuleFormatter.prototype.importSpecifier = function (specifier, node, nodes) { - var variableName = getSpecifierName(specifier); +CommonJSFormatter.prototype.importSpecifier = function (specifier, node, nodes) { + var variableName = util.getSpecifierName(specifier); // import foo from "foo"; if (specifier.default) { @@ -32,13 +28,13 @@ CommonJSModuleFormatter.prototype.importSpecifier = function (specifier, node, n if (specifier.type !== "ImportBatchSpecifier") templateName += "-key"; nodes.push(util.template(templateName, { - VARIABLE_NAME: variableName.name, + VARIABLE_NAME: variableName, MODULE_NAME: node.source.raw, KEY: specifier.id })); }; -CommonJSModuleFormatter.prototype.export = function (node, nodes) { +CommonJSFormatter.prototype.export = function (node, nodes) { var declar = node.declaration; if (node.default) { @@ -68,20 +64,21 @@ CommonJSModuleFormatter.prototype.export = function (node, nodes) { } }; -CommonJSModuleFormatter.prototype.exportSpecifier = function (specifier, node, nodes) { - var variableName = getSpecifierName(specifier); +CommonJSFormatter.prototype.exportSpecifier = function (specifier, node, nodes) { + var variableName = util.getSpecifierName(specifier); if (node.source) { + var object = b.callExpression(b.identifier("require"), [node.source]); if (specifier.type === "ExportBatchSpecifier") { // export * from "foo"; nodes.push(util.template("exports-wildcard", { - MODULE_NAME: node.source.raw + OBJECT: object }, true)); } else { // export { foo } from "test"; - nodes.push(util.template("exports-require-assign-key", { + nodes.push(util.template("exports-assign-key", { VARIABLE_NAME: variableName.name, - MODULE_NAME: node.source.raw, + OBJECT: object, KEY: specifier.id }, true)); } diff --git a/lib/6to5/templates/array-concat.js b/lib/6to5/templates/array-concat.js deleted file mode 100644 index 0ee1aa04d8..0000000000 --- a/lib/6to5/templates/array-concat.js +++ /dev/null @@ -1 +0,0 @@ -[].concat(ARGUMENT); diff --git a/lib/6to5/templates/exports-assign-key.js b/lib/6to5/templates/exports-assign-key.js new file mode 100644 index 0000000000..4a101bbbdc --- /dev/null +++ b/lib/6to5/templates/exports-assign-key.js @@ -0,0 +1 @@ +exports.VARIABLE_NAME = OBJECT.KEY; diff --git a/lib/6to5/templates/exports-require-assign-key.js b/lib/6to5/templates/exports-require-assign-key.js deleted file mode 100644 index 43de6ff6ff..0000000000 --- a/lib/6to5/templates/exports-require-assign-key.js +++ /dev/null @@ -1 +0,0 @@ -exports.VARIABLE_NAME = require(MODULE_NAME).KEY; diff --git a/lib/6to5/templates/exports-wildcard.js b/lib/6to5/templates/exports-wildcard.js index 364ad8c20d..26e5b73c7d 100644 --- a/lib/6to5/templates/exports-wildcard.js +++ b/lib/6to5/templates/exports-wildcard.js @@ -2,4 +2,4 @@ for (var i in obj) { exports[i] = obj[i]; } -}(require(MODULE_NAME))); +})(OBJECT); diff --git a/lib/6to5/modules/umd.js b/lib/6to5/templates/umd.js similarity index 100% rename from lib/6to5/modules/umd.js rename to lib/6to5/templates/umd.js diff --git a/lib/6to5/transform.js b/lib/6to5/transform.js index 1fdae6e52f..24d78fdd29 100644 --- a/lib/6to5/transform.js +++ b/lib/6to5/transform.js @@ -99,12 +99,14 @@ transform.transformers = { _aliasFunctions: require("./transformers/_alias-functions"), _blockHoist: require("./transformers/_block-hoist"), _declarations: require("./transformers/_declarations"), + _moduleFormatter: require("./transformers/_module-formatter"), useStrict: require("./transformers/use-strict") }; transform.moduleFormatters = { - common: require("./modules/common") + common: require("./modules/common"), + amd: require("./modules/amd") }; _.each(transform.transformers, function (transformer, key) { diff --git a/lib/6to5/transformer.js b/lib/6to5/transformer.js index 801c84b545..5420d84c4d 100644 --- a/lib/6to5/transformer.js +++ b/lib/6to5/transformer.js @@ -61,10 +61,6 @@ Transformer.prototype.transform = function (file) { if (transformer.ast && transformer.ast.exit) { transformer.ast.exit(ast, file); } - - if (file.moduleFormatter.transform) { - file.moduleFormatter.transform(ast); - } }; Transformer.prototype.canRun = function (file) { diff --git a/lib/6to5/transformers/_module-formatter.js b/lib/6to5/transformers/_module-formatter.js new file mode 100644 index 0000000000..fb7c7d4a90 --- /dev/null +++ b/lib/6to5/transformers/_module-formatter.js @@ -0,0 +1,11 @@ +var transform = require("../transform"); + +exports.ast = { + exit: function (ast, file) { + if (!transform.transformers.modules.canRun(file)) return; + + if (file.moduleFormatter.transform) { + file.moduleFormatter.transform(ast); + } + } +}; diff --git a/lib/6to5/util.js b/lib/6to5/util.js index 2855d8f0b5..c52ebe02d1 100644 --- a/lib/6to5/util.js +++ b/lib/6to5/util.js @@ -25,6 +25,10 @@ exports.list = function (val) { return val ? val.split(",") : []; }; +exports.getSpecifierName = function (specifier) { + return specifier.name || specifier.id; +}; + exports.ensureExpressionType = function (node) { node.type = { FunctionDeclaration: "FunctionExpression", diff --git a/package.json b/package.json index 90f7dbdf86..f0f8ece787 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "chokidar": "^0.9.0", "source-map-support": "^0.2.7", "esutils": "^1.1.4", - "acorn": "^0.9.0" + "acorn": "^0.9.0", + "ast-types": "^0.5.3" }, "devDependencies": { "es6-transpiler": "0.7.17", diff --git a/test/fixtures/syntax/modules-amd/exports-default/actual.js b/test/fixtures/syntax/modules-amd/exports-default/actual.js new file mode 100644 index 0000000000..d7c96201d6 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-default/actual.js @@ -0,0 +1,8 @@ +export default 42; +export default {}; +export default []; +export default foo; +export default function () {} +export default class {} +export default function foo () {} +export default class foo {} diff --git a/test/fixtures/syntax/modules-amd/exports-default/expected.js b/test/fixtures/syntax/modules-amd/exports-default/expected.js new file mode 100644 index 0000000000..2ea13e2d07 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-default/expected.js @@ -0,0 +1,25 @@ +"use strict"; + +define([], function () { + var exports = {}; + + exports.default = 42; + exports.default = {}; + exports.default = []; + exports.default = foo; + exports.default = function() {}; + + exports.default = function() { + var _class = function() {}; + return _class; + }(); + + exports.default = function foo() {}; + + exports.default = function() { + var foo = function foo() {}; + return foo; + }(); + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/exports-from/actual.js b/test/fixtures/syntax/modules-amd/exports-from/actual.js new file mode 100644 index 0000000000..60857f6542 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-from/actual.js @@ -0,0 +1,6 @@ +export * from "foo"; +export {foo} from "foo"; +export {foo, bar} from "foo"; +export {foo as bar} from "foo"; +export {foo as default} from "foo"; +export {foo as default, bar} from "foo"; diff --git a/test/fixtures/syntax/modules-amd/exports-from/expected.js b/test/fixtures/syntax/modules-amd/exports-from/expected.js new file mode 100644 index 0000000000..43cb26b524 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-from/expected.js @@ -0,0 +1,21 @@ +"use strict"; + +define(["foo"], function (_foo) { + var exports = {}; + + (function(obj) { + for (var i in obj) { + exports[i] = obj[i]; + } + })(_foo); + + exports.foo = _foo.foo; + exports.foo = _foo.foo; + exports.bar = _foo.bar; + exports.bar = _foo.foo; + exports.default = _foo.foo; + exports.default = _foo.foo; + exports.bar = _foo.bar; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/exports-named/actual.js b/test/fixtures/syntax/modules-amd/exports-named/actual.js new file mode 100644 index 0000000000..8515ace759 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-named/actual.js @@ -0,0 +1,5 @@ +export {foo}; +export {foo, bar}; +export {foo as bar}; +export {foo as default}; +export {foo as default, bar}; diff --git a/test/fixtures/syntax/modules-amd/exports-named/expected.js b/test/fixtures/syntax/modules-amd/exports-named/expected.js new file mode 100644 index 0000000000..9f77ddb2ea --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-named/expected.js @@ -0,0 +1,15 @@ +"use strict"; + +define([], function () { + var exports = {}; + + exports.foo = foo; + exports.foo = foo; + exports.bar = bar; + exports.bar = foo; + exports.default = foo; + exports.default = foo; + exports.bar = bar; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/exports-variable/actual.js b/test/fixtures/syntax/modules-amd/exports-variable/actual.js new file mode 100644 index 0000000000..b4629cc731 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-variable/actual.js @@ -0,0 +1,8 @@ +export var foo = 1; +export var foo2 = function () {}; +export var foo3; +export let foo4 = 2; +export let foo5; +export const foo6 = 3; +export function foo7 () {} +export class foo8 {} diff --git a/test/fixtures/syntax/modules-amd/exports-variable/expected.js b/test/fixtures/syntax/modules-amd/exports-variable/expected.js new file mode 100644 index 0000000000..0cd8c44945 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/exports-variable/expected.js @@ -0,0 +1,29 @@ +"use strict"; + +define([], function () { + var exports = {}; + + exports.foo7 = foo7; + var foo = 1; + exports.foo = foo; + var foo2 = function() {}; + exports.foo2 = foo2; + var foo3; + exports.foo3 = foo3; + var _foo4 = 2; + exports.foo4 = _foo4; + var _foo5; + exports.foo5 = _foo5; + var _foo6 = 3; + exports.foo6 = _foo6; + function foo7() {} + + var foo8 = function() { + var foo8 = function foo8() {}; + return foo8; + }(); + + exports.foo8 = foo8; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/hoist-function-exports/actual.js b/test/fixtures/syntax/modules-amd/hoist-function-exports/actual.js new file mode 100644 index 0000000000..aa0f9366ae --- /dev/null +++ b/test/fixtures/syntax/modules-amd/hoist-function-exports/actual.js @@ -0,0 +1,11 @@ +import { isEven } from "./evens"; + +export function nextOdd(n) { + return isEven(n) ? n + 1 : n + 2; +} + +export var isOdd = (function(isEven) { + return function(n) { + return !isEven(n); + }; +})(isEven); diff --git a/test/fixtures/syntax/modules-amd/hoist-function-exports/expected.js b/test/fixtures/syntax/modules-amd/hoist-function-exports/expected.js new file mode 100644 index 0000000000..d2b62de3ca --- /dev/null +++ b/test/fixtures/syntax/modules-amd/hoist-function-exports/expected.js @@ -0,0 +1,22 @@ +"use strict"; + +define(["./evens"], function (_evens) { + var exports = {}; + + exports.nextOdd = nextOdd; + var isEven = _evens.isEven; + + function nextOdd(n) { + return (isEven(n) ? n + 1 : n + 2); + } + + var isOdd = function(isEven) { + return function(n) { + return !isEven(n); + }; + }(isEven); + + exports.isOdd = isOdd; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/imports-default/actual.js b/test/fixtures/syntax/modules-amd/imports-default/actual.js new file mode 100644 index 0000000000..e67418654c --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-default/actual.js @@ -0,0 +1,2 @@ +import foo from "foo"; +import {default as foo} from "foo"; diff --git a/test/fixtures/syntax/modules-amd/imports-default/expected.js b/test/fixtures/syntax/modules-amd/imports-default/expected.js new file mode 100644 index 0000000000..ccd9af5ef1 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-default/expected.js @@ -0,0 +1,10 @@ +"use strict"; + +define(["foo"], function (_foo) { + var exports = {}; + + var foo = _foo.default; + var foo = _foo.default; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/imports-glob/actual.js b/test/fixtures/syntax/modules-amd/imports-glob/actual.js new file mode 100644 index 0000000000..e55c077500 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-glob/actual.js @@ -0,0 +1 @@ +import * as foo from "foo"; diff --git a/test/fixtures/syntax/modules-amd/imports-glob/expected.js b/test/fixtures/syntax/modules-amd/imports-glob/expected.js new file mode 100644 index 0000000000..63a8ce4b6a --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-glob/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +define(["foo"], function (_foo) { + var exports = {}; + + var foo = _foo; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/imports-mixing/actual.js b/test/fixtures/syntax/modules-amd/imports-mixing/actual.js new file mode 100644 index 0000000000..ef78c95b1c --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-mixing/actual.js @@ -0,0 +1 @@ +import foo, {baz as xyz} from "foo"; diff --git a/test/fixtures/syntax/modules-amd/imports-mixing/expected.js b/test/fixtures/syntax/modules-amd/imports-mixing/expected.js new file mode 100644 index 0000000000..a6674d76a3 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-mixing/expected.js @@ -0,0 +1,10 @@ +"use strict"; + +define(["foo"], function (_foo) { + var exports = {}; + + var foo = _foo.default; + var xyz = _foo.baz; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/imports-named/actual.js b/test/fixtures/syntax/modules-amd/imports-named/actual.js new file mode 100644 index 0000000000..83a766c62d --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-named/actual.js @@ -0,0 +1,4 @@ +import {bar} from "foo"; +import {bar, baz} from "foo"; +import {bar as baz} from "foo"; +import {bar as baz, xyz} from "foo"; diff --git a/test/fixtures/syntax/modules-amd/imports-named/expected.js b/test/fixtures/syntax/modules-amd/imports-named/expected.js new file mode 100644 index 0000000000..3d0d613ab1 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports-named/expected.js @@ -0,0 +1,14 @@ +"use strict"; + +define(["foo"], function (_foo) { + var exports = {}; + + var bar = _foo.bar; + var bar = _foo.bar; + var baz = _foo.baz; + var baz = _foo.bar; + var baz = _foo.bar; + var xyz = _foo.xyz; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/imports/actual.js b/test/fixtures/syntax/modules-amd/imports/actual.js new file mode 100644 index 0000000000..222b6885ac --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports/actual.js @@ -0,0 +1,3 @@ +import "foo"; +import "foo-bar"; +import "./directory/foo-bar"; diff --git a/test/fixtures/syntax/modules-amd/imports/expected.js b/test/fixtures/syntax/modules-amd/imports/expected.js new file mode 100644 index 0000000000..a5cc6001ba --- /dev/null +++ b/test/fixtures/syntax/modules-amd/imports/expected.js @@ -0,0 +1,7 @@ +"use strict"; + +define(["foo", "foo-bar", "./directory/foo-bar"], function (_foo, _fooBar, _directoryFooBar) { + var exports = {}; + + return exports; +}); diff --git a/test/fixtures/syntax/modules-amd/overview/actual.js b/test/fixtures/syntax/modules-amd/overview/actual.js new file mode 100644 index 0000000000..a77d4d5dfa --- /dev/null +++ b/test/fixtures/syntax/modules-amd/overview/actual.js @@ -0,0 +1,12 @@ +import "foo"; +import "foo-bar"; +import "./directory/foo-bar"; +import foo from "foo"; +import * as foo from "foo"; +import {bar} from "foo"; +import {foo as bar} from "foo"; + +export {test}; +export var test = 5; + +export default test; diff --git a/test/fixtures/syntax/modules-amd/overview/expected.js b/test/fixtures/syntax/modules-amd/overview/expected.js new file mode 100644 index 0000000000..a3100b8401 --- /dev/null +++ b/test/fixtures/syntax/modules-amd/overview/expected.js @@ -0,0 +1,16 @@ +"use strict"; + +define(["foo", "foo-bar", "./directory/foo-bar"], function (_foo, _fooBar, _directoryFooBar) { + var exports = {}; + + var foo = _foo.default; + var foo = _foo; + var bar = _foo.bar; + var bar = _foo.foo; + exports.test = test; + var test = 5; + exports.test = test; + exports.default = test; + + return exports; +});