diff --git a/package.json b/package.json index e1c5ed16506a..651250fcdf04 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "ast-types": "~0.7.0", + "bluebird": "^2.9.25", "chalk": "^1.0.0", "convert-source-map": "^1.1.0", "core-js": "^0.9.0", @@ -49,6 +50,7 @@ "regenerator": "^0.8.20", "regexpu": "^1.1.2", "repeating": "^1.1.2", + "resolve": "^1.1.6", "shebang-regex": "^1.0.0", "slash": "^1.0.0", "source-map": "^0.4.0", diff --git a/src/babel/api/node.js b/src/babel/api/node.js index 95b16129f295..025e0510cd8d 100644 --- a/src/babel/api/node.js +++ b/src/babel/api/node.js @@ -9,6 +9,7 @@ export { canCompile } from "../util"; export { default as options } from "../transformation/file/options"; export { default as Transformer } from "../transformation/transformer"; +export { default as Pipeline } from "../transformation/transformer-pipeline"; export { default as traverse } from "../traversal"; export { default as buildExternalHelpers } from "../tools/build-external-helpers"; export { version } from "../../../package"; diff --git a/src/babel/transformation/bundler/index.js b/src/babel/transformation/bundler/index.js new file mode 100644 index 000000000000..5946993dfa25 --- /dev/null +++ b/src/babel/transformation/bundler/index.js @@ -0,0 +1,15 @@ +export default class Bundler { + constructor(opts) { + this.resolvers = []; + this.cache = {}; + this.opts = opts; + } + + addResolver(resolver) { + this.resolvers.push(resolver); + } + + transform() { + + } +} diff --git a/src/babel/transformation/bundler/resolvers/node.js b/src/babel/transformation/bundler/resolvers/node.js new file mode 100644 index 000000000000..dce7f9b111d3 --- /dev/null +++ b/src/babel/transformation/bundler/resolvers/node.js @@ -0,0 +1,25 @@ +import { Promise } from "bluebird"; +import resolve from "resolve"; +import path from "path"; +import fs from "fs"; + +export default function (request, parent) { + return new Promise(function (resolve, reject) { + resolve(request, { basedir: path.dirname(parent) }, function (err, res) { + if (err) { + resolve(null); + } else { + fs.readFile(res, "utf8", function (err) { + if (err) { + reject(err); + } else { + resolve({ + filename: filename, + content: content + }); + } + }); + } + }); + }); +} diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 76ccd08cd657..16763d0c2b13 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -1,5 +1,6 @@ import convertSourceMap from "convert-source-map"; import * as optionParsers from "./option-parsers"; +import moduleFormatters from "../modules"; import PluginManager from "./plugin-manager"; import shebangRegex from "shebang-regex"; import TraversalPath from "../../traversal/path"; @@ -38,7 +39,7 @@ function checkPath(stack, path) { } export default class File { - constructor(opts = {}) { + constructor(opts = {}, pipeline) { this.dynamicImportTypes = {}; this.dynamicImportIds = {}; this.dynamicImports = []; @@ -49,9 +50,10 @@ export default class File { this.data = {}; this.uids = {}; - this.log = new Logger(this, opts.filename || "unknown"); - this.opts = this.normalizeOptions(opts); - this.ast = {}; + this.pipeline = pipeline; + this.log = new Logger(this, opts.filename || "unknown"); + this.opts = this.normalizeOptions(opts); + this.ast = {}; this.buildTransformers(); } @@ -133,7 +135,7 @@ export default class File { } var optionParser = optionParsers[option.type]; - if (optionParser) val = optionParser(key, val); + if (optionParser) val = optionParser(key, val, this.pipeline); if (option.alias) { opts[option.alias] = opts[option.alias] || val; @@ -200,7 +202,7 @@ export default class File { var stack = []; // build internal transformers - each(transform.transformers, function (transformer, key) { + each(this.pipeline.transformers, function (transformer, key) { var pass = transformers[key] = transformer.buildPass(file); if (pass.canTransform()) { @@ -235,7 +237,7 @@ export default class File { } getModuleFormatter(type: string) { - var ModuleFormatter = isFunction(type) ? type : transform.moduleFormatters[type]; + var ModuleFormatter = isFunction(type) ? type : moduleFormatters[type]; if (!ModuleFormatter) { var loc = util.resolveRelative(type); diff --git a/src/babel/transformation/file/option-parsers.js b/src/babel/transformation/file/option-parsers.js index c693c0c3068f..c5ebdac1bea8 100644 --- a/src/babel/transformation/file/option-parsers.js +++ b/src/babel/transformation/file/option-parsers.js @@ -1,14 +1,13 @@ -import transform from "./../index"; import * as util from "../../util"; -export function transformerList(key, val) { +export function transformerList(key, val, pipeline) { val = util.arrayify(val); if (val.indexOf("all") >= 0 || val.indexOf(true) >= 0) { - val = Object.keys(transform.transformers); + val = Object.keys(pipeline.transformers); } - return transform._ensureTransformerNames(key, val); + return pipeline._ensureTransformerNames(key, val); } export function number(key, val) { diff --git a/src/babel/transformation/index.js b/src/babel/transformation/index.js index 4e6494e8f169..f5bf8b2cb7ed 100644 --- a/src/babel/transformation/index.js +++ b/src/babel/transformation/index.js @@ -1,68 +1,34 @@ -import normalizeAst from "../helpers/normalize-ast"; -import Transformer from "./transformer"; -import object from "../helpers/object"; -import File from "./file"; -import each from "lodash/collection/each"; +import Pipeline from "./transformer-pipeline"; -export default function transform(code: string, opts?: Object) { - var file = new File(opts); - return file.parse(code); -} +var pipeline = new Pipeline; -transform.fromAst = function (ast, code, opts) { - ast = normalizeAst(ast); +// - var file = new File(opts); - file.addCode(code); - file.transform(ast); - return file.generate(); -}; +import transformers from "./transformers"; +pipeline.addTransformers(transformers); -transform._ensureTransformerNames = function (type: string, rawKeys: Array) { - var keys = []; +// - for (var i = 0; i < rawKeys.length; i++) { - var key = rawKeys[i]; +import deprecated from "./transformers/deprecated"; +pipeline.addDeprecated(deprecated); - var deprecatedKey = transform.deprecatedTransformerMap[key]; - var aliasKey = transform.aliasTransformerMap[key]; - if (aliasKey) { - keys.push(aliasKey); - } else if (deprecatedKey) { - // deprecated key, remap it to the new one - console.error(`The transformer ${key} has been renamed to ${deprecatedKey}`); - rawKeys.push(deprecatedKey); - } else if (transform.transformers[key]) { - // valid key - keys.push(key); - } else if (transform.namespaces[key]) { - // namespace, append all transformers within this namespace - keys = keys.concat(transform.namespaces[key]); - } else { - // invalid key - throw new ReferenceError(`Unknown transformer ${key} specified in ${type}`); - } - } +// - return keys; -}; +import aliases from "./transformers/aliases"; +pipeline.addDeprecated(aliases); -transform.transformerNamespaces = object(); -transform.transformers = object(); -transform.namespaces = object(); +// -transform.deprecatedTransformerMap = require("./transformers/deprecated"); -transform.aliasTransformerMap = require("./transformers/aliases"); -transform.moduleFormatters = require("./modules"); +import * as filters from "./transformers/filters"; +pipeline.addFilter(filters.internal); +pipeline.addFilter(filters.blacklist); +pipeline.addFilter(filters.whitelist); +pipeline.addFilter(filters.stage); +pipeline.addFilter(filters.optional); -import rawTransformers from "./transformers"; +// -each(rawTransformers, function (transformer, key) { - var namespace = key.split(".")[0]; - - transform.namespaces[namespace] = transform.namespaces[namespace] || []; - transform.namespaces[namespace].push(key); - transform.transformerNamespaces[key] = namespace; - - transform.transformers[key] = new Transformer(key, transformer); -}); +var transform = pipeline.transform.bind(pipeline); +transform.fromAst = pipeline.transformFromAst.bind(pipeline); +transform.pipeline = pipeline; +export default transform; diff --git a/src/babel/transformation/transformer-pass.js b/src/babel/transformation/transformer-pass.js index 81e03eda287d..38c3e8ef8c3e 100644 --- a/src/babel/transformation/transformer-pass.js +++ b/src/babel/transformation/transformer-pass.js @@ -17,30 +17,7 @@ export default class TransformerPass { } canTransform(): boolean { - var transformer = this.transformer; - - var opts = this.file.opts; - var key = transformer.key; - - // internal - if (key[0] === "_") return true; - - // blacklist - var blacklist = opts.blacklist; - if (blacklist.length && includes(blacklist, key)) return false; - - // whitelist - var whitelist = opts.whitelist; - if (whitelist) return includes(whitelist, key); - - // stage - var stage = transformer.metadata.stage; - if (stage != null && stage >= opts.stage) return true; - - // optional - if (transformer.metadata.optional && !includes(opts.optional, key)) return false; - - return true; + return this.file.pipeline.canTransform(this.transformer, this.file.opts); } checkPath(path: TraversalPath): boolean { diff --git a/src/babel/transformation/transformer-pipeline.js b/src/babel/transformation/transformer-pipeline.js new file mode 100644 index 000000000000..77cd57a3a7bb --- /dev/null +++ b/src/babel/transformation/transformer-pipeline.js @@ -0,0 +1,105 @@ +import Transformer from "./transformer"; +import normalizeAst from "../helpers/normalize-ast"; +import Bundler from "./bundler"; +import assign from "lodash/object/assign"; +import object from "../helpers/object"; +import File from "./file"; + +export default class TransformerPipeline { + constructor() { + this.transformers = object(); + this.namespaces = object(); + this.deprecated = object(); + this.aliases = object(); + this.filters = []; + } + + addTransformers(transformers) { + for (var key in transformers) { + this.addTransformer(key, transformers[key]); + } + return this; + } + + addTransformer(key, transformer) { + if (this.transformers[key]) throw new Error(); // todo: error + + var namespace = key.split(".")[0]; + this.namespaces[namespace] = this.namespaces[namespace] || []; + this.namespaces[namespace].push(key); + this.namespaces[key] = namespace; + + this.transformers[key] = new Transformer(key, transformer); + } + + addAliases(names) { + assign(this.aliases, names); + return this; + } + + addDeprecated(names) { + assign(this.deprecated, names); + return this; + } + + addFilter(filter: Function) { + this.filters.push(filter); + return this; + } + + canTransform(transformer, fileOpts) { + for (var filter of (this.filters: Array)) { + var result = filter(transformer, fileOpts); + if (result != null) return result; + } + + return true; + } + + transform(code: string, opts?: Object) { + var file = new File(opts, this); + return file.parse(code); + } + + transformFromAst(ast, code, opts) { + ast = normalizeAst(ast); + + var file = new File(opts, this); + file.addCode(code); + file.transform(ast); + return file.generate(); + } + + createBundler() { + return new Bundler(this); + } + + _ensureTransformerNames(type: string, rawKeys: Array) { + var keys = []; + + for (var i = 0; i < rawKeys.length; i++) { + var key = rawKeys[i]; + + var deprecatedKey = this.deprecated[key]; + var aliasKey = this.aliases[key]; + if (aliasKey) { + keys.push(aliasKey); + } else if (deprecatedKey) { + // deprecated key, remap it to the new one + console.error(`The transformer ${key} has been renamed to ${deprecatedKey}`); + rawKeys.push(deprecatedKey); + } else if (this.transformers[key]) { + // valid key + keys.push(key); + } else if (this.namespaces[key]) { + // namespace, append all transformers within this namespace + keys = keys.concat(this.namespaces[key]); + } else { + // invalid key + throw new ReferenceError(`Unknown transformer ${key} specified in ${type}`); + } + } + + return keys; + } +} diff --git a/src/babel/transformation/transformer.js b/src/babel/transformation/transformer.js index c07b41ea94e2..723a5c8c9769 100644 --- a/src/babel/transformation/transformer.js +++ b/src/babel/transformation/transformer.js @@ -15,7 +15,7 @@ import each from "lodash/collection/each"; */ export default class Transformer { - constructor(transformerKey: string, transformer: Object, opts: Object) { + constructor(transformerKey: string, transformer: Object) { transformer = assign({}, transformer); var take = function (key) { @@ -40,7 +40,6 @@ export default class Transformer { // this.handlers = this.normalize(transformer); - this.opts = this.opts || {}; this.key = transformerKey; // diff --git a/src/babel/transformation/transformers/filters.js b/src/babel/transformation/transformers/filters.js new file mode 100644 index 000000000000..821896544198 --- /dev/null +++ b/src/babel/transformation/transformers/filters.js @@ -0,0 +1,24 @@ +import includes from "lodash/collection/includes"; + +export function internal(transformer, opts) { + if (transformer.key[0] === "_") return true; +} + +export function blacklist(transformer, opts) { + var blacklist = opts.blacklist; + if (blacklist.length && includes(blacklist, transformer.key)) return false; +} + +export function whitelist(transformer, opts) { + var whitelist = opts.whitelist; + if (whitelist) return includes(whitelist, transformer.key); +} + +export function stage(transformer, opts) { + var stage = transformer.metadata.stage; + if (stage != null && stage >= opts.stage) return true; +} + +export function optional(transformer, opts) { + if (transformer.metadata.optional && !includes(opts.optional, transformer.key)) return false; +} diff --git a/test/core/api.js b/test/core/api.js index c4f4a5607ddf..7e83b24d06b0 100644 --- a/test/core/api.js +++ b/test/core/api.js @@ -19,7 +19,7 @@ suite("api", function () { }); test("addHelper unknown", function () { - var file = new File; + var file = new File({}, transform.pipeline); assert.throws(function () { file.addHelper("foob"); }, /Unknown helper foob/); @@ -40,11 +40,11 @@ suite("api", function () { }); test("extra options", function () { - var file1 = new File({ extra: { foo: "bar" } }); + var file1 = new File({ extra: { foo: "bar" } }, transform.pipeline); assert.equal(file1.opts.extra.foo, "bar"); - var file2 = new File; - var file3 = new File; + var file2 = new File({}, transform.pipeline); + var file3 = new File({}, transform.pipeline); assert.ok(file2.opts.extra !== file3.opts.extra); });