From a1adca6b658aaf8de513ba8312693e29428c3e18 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 19 Oct 2014 12:11:12 +1100 Subject: [PATCH] add support for custom module formatters - #82 --- bin/6to5/index.js | 25 +++-- lib/6to5/file.js | 13 ++- lib/6to5/modules/amd.js | 0 lib/6to5/modules/common.js | 96 +++++++++++++++++ lib/6to5/modules/umd.js | 0 lib/6to5/transform.js | 4 + lib/6to5/transformer.js | 24 +++-- lib/6to5/transformers/modules.js | 100 ++---------------- .../exports-default/actual.js | 0 .../exports-default/expected.js | 0 .../exports-from/actual.js | 0 .../exports-from/expected.js | 0 .../exports-named/actual.js | 0 .../exports-named/expected.js | 0 .../exports-variable/actual.js | 0 .../exports-variable/expected.js | 0 .../hoist-function-exports/actual.js | 0 .../hoist-function-exports/expected.js | 0 .../imports-default/actual.js | 0 .../imports-default/expected.js | 0 .../imports-glob/actual.js | 0 .../imports-glob/expected.js | 0 .../imports-mixing/actual.js | 0 .../imports-mixing/expected.js | 0 .../imports-named/actual.js | 0 .../imports-named/expected.js | 0 .../imports/actual.js | 0 .../imports/expected.js | 0 .../syntax/modules-common/options.json | 3 + .../overview/actual.js | 0 .../overview/expected.js | 0 31 files changed, 156 insertions(+), 109 deletions(-) create mode 100644 lib/6to5/modules/amd.js create mode 100644 lib/6to5/modules/common.js create mode 100644 lib/6to5/modules/umd.js rename test/fixtures/syntax/{modules => modules-common}/exports-default/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-default/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-from/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-from/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-named/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-named/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-variable/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/exports-variable/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/hoist-function-exports/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/hoist-function-exports/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-default/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-default/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-glob/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-glob/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-mixing/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-mixing/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-named/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports-named/expected.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/imports/expected.js (100%) create mode 100644 test/fixtures/syntax/modules-common/options.json rename test/fixtures/syntax/{modules => modules-common}/overview/actual.js (100%) rename test/fixtures/syntax/{modules => modules-common}/overview/expected.js (100%) diff --git a/bin/6to5/index.js b/bin/6to5/index.js index 0c3c4c3854..5500ff96fd 100755 --- a/bin/6to5/index.js +++ b/bin/6to5/index.js @@ -16,19 +16,27 @@ commander.option("-s, --source-maps", "Save source map alongside the compiled co commander.option("-f, --filename [filename]", "Filename to use when reading from stdin - this will be used in source-maps, errors etc [stdin]", "stdin"); commander.option("-w, --watch", "Recompile files on changes"); +commander.option("-m, --modules [modules]", "Module formatter type to use [common]", "common"); commander.option("-w, --whitelist [whitelist]", "Whitelist of transformers to ONLY use", util2.list); commander.option("-b, --blacklist [blacklist]", "Blacklist of transformers to NOT use", util2.list); commander.option("-o, --out-file [out]", "Compile all input files into a single file"); commander.option("-d, --out-dir [out]", "Compile an input directory of modules into an output directory"); commander.on("--help", function(){ - console.log(" Transformers:"); - console.log(); - _.each(_.keys(transform.transformers).sort(), function (key) { - if (key[0] === "_") return; - console.log(" - " + key); - }); - console.log(); + var outKeys = function (title, obj) { + console.log(" " + title + ":"); + console.log(); + + _.each(_.keys(obj).sort(), function (key) { + if (key[0] === "_") return; + console.log(" - " + key); + }); + + console.log(); + }; + + outKeys("Transformers", transform.transformers); + outKeys("Module formatters", transform.moduleFormatters); }); var pkg = require("../../package.json"); @@ -83,7 +91,8 @@ exports.opts = { sourceMapName: commander.outFile, blacklist: commander.blacklist, whitelist: commander.whitelist, - sourceMap: commander.sourceMaps || commander.sourceMapsInline + sourceMap: commander.sourceMaps || commander.sourceMapsInline, + modules: commander.modules }; var fn; diff --git a/lib/6to5/file.js b/lib/6to5/file.js index b530a20833..0611abd28a 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -9,9 +9,11 @@ var b = require("recast").types.builders; var _ = require("lodash"); function File(opts) { + this.opts = File.normaliseOptions(opts); + this.moduleFormatter = this.getModuleFormatter(opts.modules); + this.declarations = {}; this.uids = {}; - this.opts = File.normaliseOptions(opts); this.ast = {}; } @@ -22,7 +24,8 @@ File.normaliseOptions = function (opts) { blacklist: [], whitelist: [], sourceMap: false, - filename: "unknown" + filename: "unknown", + modules: "common" }); _.defaults(opts, { @@ -36,6 +39,12 @@ File.normaliseOptions = function (opts) { return opts; }; +File.prototype.getModuleFormatter = function (type) { + var ModuleLoader = transform.moduleFormatters[type]; + if (!ModuleLoader) throw new ReferenceError("unknown module formatter type " + type); + return new ModuleLoader(this); +}; + File.prototype.parseShebang = function (code) { var shebangMatch = code.match(SHEBANG_REGEX); if (shebangMatch) { diff --git a/lib/6to5/modules/amd.js b/lib/6to5/modules/amd.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/6to5/modules/common.js b/lib/6to5/modules/common.js new file mode 100644 index 0000000000..a860eaf0cd --- /dev/null +++ b/lib/6to5/modules/common.js @@ -0,0 +1,96 @@ +module.exports = CommonJSModuleFormatter; + +var util = require("../util"); +var b = require("recast").types.builders; + +var getSpecifierName = function (specifier) { + return specifier.name || specifier.id; +}; + +function CommonJSModuleFormatter(file) { + this.file = file; +} + +CommonJSModuleFormatter.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); + var key = specifier.id.name; + + // import foo from "foo"; + if (specifier.type === "ImportDefaultSpecifier") { + key = b.identifier("default"); + } + + var templateName = "require-assign"; + + // import * as bar from "foo"; + if (specifier.type !== "ImportNamespaceSpecifier") templateName += "-key"; + + nodes.push(util.template(templateName, { + VARIABLE_NAME: variableName.name, + MODULE_NAME: node.source.raw, + KEY: key + })); +}; + +CommonJSModuleFormatter.prototype.export = function (node, nodes) { + var declar = node.declaration; + + if (node.default) { + util.ensureExpressionType(declar); + + nodes.push(util.template("exports-default", { + VALUE: declar + }, true)); + } else { + var id = declar.id; + if (declar.type === "VariableDeclaration") { + id = declar.declarations[0].id; + } + + var assign = util.template("exports-assign", { + VALUE: id, + KEY: id + }, true); + + nodes.push(declar); + + if (declar.type === "FunctionDeclaration") { + assign._blockHoist = true; + } + + nodes.push(assign); + } +}; + +CommonJSModuleFormatter.prototype.exportSpecifier = function (specifier, node, nodes) { + var variableName = getSpecifierName(specifier); + + if (node.source) { + if (specifier.type === "ExportBatchSpecifier") { + // export * from "foo"; + nodes.push(util.template("exports-wildcard", { + MODULE_NAME: node.source.raw + }, true)); + } else { + // export { foo } from "test"; + nodes.push(util.template("exports-require-assign-key", { + VARIABLE_NAME: variableName.name, + MODULE_NAME: node.source.raw, + 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/umd.js b/lib/6to5/modules/umd.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/6to5/transform.js b/lib/6to5/transform.js index 65da474122..a85dd1f37d 100644 --- a/lib/6to5/transform.js +++ b/lib/6to5/transform.js @@ -106,6 +106,10 @@ transform.transformers = { useStrict: require("./transformers/use-strict") }; +transform.moduleFormatters = { + common: require("./modules/common") +}; + _.each(transform.transformers, function (transformer, key) { transform.transformers[key] = new Transformer(key, transformer); }); diff --git a/lib/6to5/transformer.js b/lib/6to5/transformer.js index ddd248c951..801c84b545 100644 --- a/lib/6to5/transformer.js +++ b/lib/6to5/transformer.js @@ -11,13 +11,13 @@ function Transformer(key, transformer) { Transformer.normalise = function (transformer) { if (_.isFunction(transformer)) { transformer = { ast: transformer }; - } else { - _.each(transformer, function (fns, type) { - if (type === "ast") return; - if (_.isFunction(fns)) fns = { enter: fns }; - transformer[type] = fns; - }); } + + _.each(transformer, function (fns, type) { + if (_.isFunction(fns)) fns = { enter: fns }; + transformer[type] = fns; + }); + return transformer; }; @@ -27,8 +27,8 @@ Transformer.prototype.transform = function (file) { var transformer = this.transformer; var ast = file.ast; - if (transformer.ast) { - transformer.ast(ast, file); + if (transformer.ast && transformer.ast.enter) { + transformer.ast.enter(ast, file); } var build = function (exit) { @@ -57,6 +57,14 @@ Transformer.prototype.transform = function (file) { enter: build(), exit: build(true) }); + + 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/modules.js b/lib/6to5/transformers/modules.js index e0af365ad7..c93aab04bc 100644 --- a/lib/6to5/transformers/modules.js +++ b/lib/6to5/transformers/modules.js @@ -1,110 +1,28 @@ -var util = require("../util"); -var b = require("recast").types.builders; -var _ = require("lodash"); +var _ = require("lodash"); -exports.ImportDeclaration = function (node) { +exports.ImportDeclaration = function (node, parent, file) { var nodes = []; if (node.specifiers.length) { _.each(node.specifiers, function (specifier) { - var variableName = getSpecifierName(specifier); - var key = specifier.id.name; - - // import foo from "foo"; - if (specifier.type === "ImportDefaultSpecifier") { - key = b.identifier("default"); - } - - var templateName = "require-assign"; - - // import * as bar from "foo"; - if (specifier.type !== "ImportNamespaceSpecifier") templateName += "-key"; - - nodes.push(util.template(templateName, { - VARIABLE_NAME: variableName.name, - MODULE_NAME: node.source.raw, - KEY: key - })); + file.moduleFormatter.importSpecifier(specifier, node, nodes); }); } else { - // import "foo"; - nodes.push(util.template("require", { - MODULE_NAME: node.source.raw - }, true)); + file.moduleFormatter.import(node, nodes); } return nodes; }; -var pushExportSpecifiers = function (node, nodes) { - _.each(node.specifiers, function (specifier) { - var variableName = getSpecifierName(specifier); - - if (node.source) { - if (specifier.type === "ExportBatchSpecifier") { - // export * from "foo"; - nodes.push(util.template("exports-wildcard", { - MODULE_NAME: node.source.raw - }, true)); - } else { - // export { foo } from "test"; - nodes.push(util.template("exports-require-assign-key", { - VARIABLE_NAME: variableName.name, - MODULE_NAME: node.source.raw, - KEY: specifier.id - }, true)); - } - } else { - // export { foo }; - nodes.push(util.template("exports-assign", { - VALUE: specifier.id, - KEY: variableName - }, true)); - } - }); -}; - -var getSpecifierName = function (specifier) { - return specifier.name || specifier.id; -}; - -var pushExportDeclaration = function (node, parent, nodes) { - var declar = node.declaration; - - if (node.default) { - util.ensureExpressionType(declar); - - nodes.push(util.template("exports-default", { - VALUE: declar - }, true)); - } else { - var id = declar.id; - if (declar.type === "VariableDeclaration") { - id = declar.declarations[0].id; - } - - var assign = util.template("exports-assign", { - VALUE: id, - KEY: id - }, true); - - nodes.push(declar); - - if (declar.type === "FunctionDeclaration") { - assign._blockHoist = true; - } - - nodes.push(assign); - } -}; - -exports.ExportDeclaration = function (node, parent) { +exports.ExportDeclaration = function (node, parent, file) { var nodes = []; if (node.declaration) { - pushExportDeclaration(node, parent, nodes); + file.moduleFormatter.export(node, nodes); } else { - pushExportSpecifiers(node, nodes); + _.each(node.specifiers, function (specifier) { + file.moduleFormatter.exportSpecifier(specifier, node, nodes); + }); } return nodes; diff --git a/test/fixtures/syntax/modules/exports-default/actual.js b/test/fixtures/syntax/modules-common/exports-default/actual.js similarity index 100% rename from test/fixtures/syntax/modules/exports-default/actual.js rename to test/fixtures/syntax/modules-common/exports-default/actual.js diff --git a/test/fixtures/syntax/modules/exports-default/expected.js b/test/fixtures/syntax/modules-common/exports-default/expected.js similarity index 100% rename from test/fixtures/syntax/modules/exports-default/expected.js rename to test/fixtures/syntax/modules-common/exports-default/expected.js diff --git a/test/fixtures/syntax/modules/exports-from/actual.js b/test/fixtures/syntax/modules-common/exports-from/actual.js similarity index 100% rename from test/fixtures/syntax/modules/exports-from/actual.js rename to test/fixtures/syntax/modules-common/exports-from/actual.js diff --git a/test/fixtures/syntax/modules/exports-from/expected.js b/test/fixtures/syntax/modules-common/exports-from/expected.js similarity index 100% rename from test/fixtures/syntax/modules/exports-from/expected.js rename to test/fixtures/syntax/modules-common/exports-from/expected.js diff --git a/test/fixtures/syntax/modules/exports-named/actual.js b/test/fixtures/syntax/modules-common/exports-named/actual.js similarity index 100% rename from test/fixtures/syntax/modules/exports-named/actual.js rename to test/fixtures/syntax/modules-common/exports-named/actual.js diff --git a/test/fixtures/syntax/modules/exports-named/expected.js b/test/fixtures/syntax/modules-common/exports-named/expected.js similarity index 100% rename from test/fixtures/syntax/modules/exports-named/expected.js rename to test/fixtures/syntax/modules-common/exports-named/expected.js diff --git a/test/fixtures/syntax/modules/exports-variable/actual.js b/test/fixtures/syntax/modules-common/exports-variable/actual.js similarity index 100% rename from test/fixtures/syntax/modules/exports-variable/actual.js rename to test/fixtures/syntax/modules-common/exports-variable/actual.js diff --git a/test/fixtures/syntax/modules/exports-variable/expected.js b/test/fixtures/syntax/modules-common/exports-variable/expected.js similarity index 100% rename from test/fixtures/syntax/modules/exports-variable/expected.js rename to test/fixtures/syntax/modules-common/exports-variable/expected.js diff --git a/test/fixtures/syntax/modules/hoist-function-exports/actual.js b/test/fixtures/syntax/modules-common/hoist-function-exports/actual.js similarity index 100% rename from test/fixtures/syntax/modules/hoist-function-exports/actual.js rename to test/fixtures/syntax/modules-common/hoist-function-exports/actual.js diff --git a/test/fixtures/syntax/modules/hoist-function-exports/expected.js b/test/fixtures/syntax/modules-common/hoist-function-exports/expected.js similarity index 100% rename from test/fixtures/syntax/modules/hoist-function-exports/expected.js rename to test/fixtures/syntax/modules-common/hoist-function-exports/expected.js diff --git a/test/fixtures/syntax/modules/imports-default/actual.js b/test/fixtures/syntax/modules-common/imports-default/actual.js similarity index 100% rename from test/fixtures/syntax/modules/imports-default/actual.js rename to test/fixtures/syntax/modules-common/imports-default/actual.js diff --git a/test/fixtures/syntax/modules/imports-default/expected.js b/test/fixtures/syntax/modules-common/imports-default/expected.js similarity index 100% rename from test/fixtures/syntax/modules/imports-default/expected.js rename to test/fixtures/syntax/modules-common/imports-default/expected.js diff --git a/test/fixtures/syntax/modules/imports-glob/actual.js b/test/fixtures/syntax/modules-common/imports-glob/actual.js similarity index 100% rename from test/fixtures/syntax/modules/imports-glob/actual.js rename to test/fixtures/syntax/modules-common/imports-glob/actual.js diff --git a/test/fixtures/syntax/modules/imports-glob/expected.js b/test/fixtures/syntax/modules-common/imports-glob/expected.js similarity index 100% rename from test/fixtures/syntax/modules/imports-glob/expected.js rename to test/fixtures/syntax/modules-common/imports-glob/expected.js diff --git a/test/fixtures/syntax/modules/imports-mixing/actual.js b/test/fixtures/syntax/modules-common/imports-mixing/actual.js similarity index 100% rename from test/fixtures/syntax/modules/imports-mixing/actual.js rename to test/fixtures/syntax/modules-common/imports-mixing/actual.js diff --git a/test/fixtures/syntax/modules/imports-mixing/expected.js b/test/fixtures/syntax/modules-common/imports-mixing/expected.js similarity index 100% rename from test/fixtures/syntax/modules/imports-mixing/expected.js rename to test/fixtures/syntax/modules-common/imports-mixing/expected.js diff --git a/test/fixtures/syntax/modules/imports-named/actual.js b/test/fixtures/syntax/modules-common/imports-named/actual.js similarity index 100% rename from test/fixtures/syntax/modules/imports-named/actual.js rename to test/fixtures/syntax/modules-common/imports-named/actual.js diff --git a/test/fixtures/syntax/modules/imports-named/expected.js b/test/fixtures/syntax/modules-common/imports-named/expected.js similarity index 100% rename from test/fixtures/syntax/modules/imports-named/expected.js rename to test/fixtures/syntax/modules-common/imports-named/expected.js diff --git a/test/fixtures/syntax/modules/imports/actual.js b/test/fixtures/syntax/modules-common/imports/actual.js similarity index 100% rename from test/fixtures/syntax/modules/imports/actual.js rename to test/fixtures/syntax/modules-common/imports/actual.js diff --git a/test/fixtures/syntax/modules/imports/expected.js b/test/fixtures/syntax/modules-common/imports/expected.js similarity index 100% rename from test/fixtures/syntax/modules/imports/expected.js rename to test/fixtures/syntax/modules-common/imports/expected.js diff --git a/test/fixtures/syntax/modules-common/options.json b/test/fixtures/syntax/modules-common/options.json new file mode 100644 index 0000000000..878edbcd5d --- /dev/null +++ b/test/fixtures/syntax/modules-common/options.json @@ -0,0 +1,3 @@ +{ + "modules": "common" +} diff --git a/test/fixtures/syntax/modules/overview/actual.js b/test/fixtures/syntax/modules-common/overview/actual.js similarity index 100% rename from test/fixtures/syntax/modules/overview/actual.js rename to test/fixtures/syntax/modules-common/overview/actual.js diff --git a/test/fixtures/syntax/modules/overview/expected.js b/test/fixtures/syntax/modules-common/overview/expected.js similarity index 100% rename from test/fixtures/syntax/modules/overview/expected.js rename to test/fixtures/syntax/modules-common/overview/expected.js