diff --git a/packages/babel-core/src/transformation/file/index.js b/packages/babel-core/src/transformation/file/index.js index dad9ec5b13..f11e750303 100644 --- a/packages/babel-core/src/transformation/file/index.js +++ b/packages/babel-core/src/transformation/file/index.js @@ -45,7 +45,17 @@ export default class File extends Store { super(); this.log = new Logger(this, opts.filename || "unknown"); - this.opts = this.initOptions(opts); + + opts = this.initOptions(opts); + + let passes = []; + if (opts.plugins) passes.push(opts.plugins); + + // With "passPerPreset" enabled there may still be presets in the options. + if (opts.presets) passes = passes.concat(opts.presets.map((preset) => preset.plugins).filter(Boolean)); + + this.pluginPasses = passes; + this.opts = opts; this.parserOpts = { sourceType: this.opts.sourceType, @@ -53,22 +63,12 @@ export default class File extends Store { plugins: [], }; - this.pluginVisitors = []; - this.pluginPasses = []; - - // Plugins for top-level options. - this.buildPluginsForOptions(this.opts); - - // If we are in the "pass per preset" mode, build - // also plugins for each preset. - if (this.opts.passPerPreset) { - // All the "per preset" options are inherited from the main options. - this.perPresetOpts = []; - this.opts.presets.forEach((presetOpts) => { - const perPresetOpts = Object.assign(Object.create(this.opts), presetOpts); - this.perPresetOpts.push(perPresetOpts); - this.buildPluginsForOptions(perPresetOpts); - }); + for (const pluginPairs of passes) { + for (const [ plugin ] of pluginPairs) { + if (plugin.manipulateOptions) { + plugin.manipulateOptions(opts, this.parserOpts, this); + } + } } this.metadata = { @@ -100,8 +100,7 @@ export default class File extends Store { static helpers: Array; - pluginVisitors: Array; - pluginPasses: Array; + pluginPasses: Array>; parserOpts: BabelParserOptions; log: Logger; opts: Object; @@ -132,7 +131,7 @@ export default class File extends Store { } initOptions(opts) { - opts = new OptionManager(this.log).init(opts); + opts = this.log.wrap(() => new OptionManager().init(opts)); if (opts.inputSourceMap) { opts.sourceMaps = true; @@ -170,31 +169,6 @@ export default class File extends Store { return opts; } - buildPluginsForOptions(opts) { - if (!Array.isArray(opts.plugins)) { - return; - } - - const plugins: Array<[PluginPass, Object]> = opts.plugins.concat(INTERNAL_PLUGINS); - const currentPluginVisitors = []; - const currentPluginPasses = []; - - // init plugins! - for (const ref of plugins) { - const [plugin, pluginOpts] = ref; // todo: fix - can't embed in loop head because of flow bug - - currentPluginVisitors.push(plugin.visitor); - currentPluginPasses.push(new PluginPass(this, plugin, pluginOpts)); - - if (plugin.manipulateOptions) { - plugin.manipulateOptions(opts, this.parserOpts, this); - } - } - - this.pluginVisitors.push(currentPluginVisitors); - this.pluginPasses.push(currentPluginPasses); - } - getModuleName(): ?string { const opts = this.opts; if (!opts.moduleIds) { @@ -460,20 +434,25 @@ export default class File extends Store { } transform(): BabelFileResult { - // In the "pass per preset" mode, we have grouped passes. - // Otherwise, there is only one plain pluginPasses array. - for (let i = 0; i < this.pluginPasses.length; i++) { - const pluginPasses = this.pluginPasses[i]; - this.call("pre", pluginPasses); + for (const pluginPairs of this.pluginPasses) { + const passes = []; + const visitors = []; + + for (const [ plugin, pluginOpts ] of pluginPairs.concat(INTERNAL_PLUGINS)) { + const pass = new PluginPass(this, plugin, pluginOpts); + passes.push(pass); + visitors.push(plugin.visitor); + } + + this.call("pre", passes); this.log.debug("Start transform traverse"); // merge all plugin visitors into a single visitor - const visitor = traverse.visitors.merge(this.pluginVisitors[i], pluginPasses, - this.opts.wrapPluginVisitorMethod); + const visitor = traverse.visitors.merge(visitors, passes, this.opts.wrapPluginVisitorMethod); traverse(this.ast, visitor, this.scope); this.log.debug("End transform traverse"); - this.call("post", pluginPasses); + this.call("post", passes); } return this.generate(); diff --git a/packages/babel-core/src/transformation/file/logger.js b/packages/babel-core/src/transformation/file/logger.js index aa4a57d718..c07273e114 100644 --- a/packages/babel-core/src/transformation/file/logger.js +++ b/packages/babel-core/src/transformation/file/logger.js @@ -21,6 +21,15 @@ export default class Logger { return parts; } + wrap(callback: () => T): T { + try { + return callback(); + } catch (e) { + e.message = this._buildMessage(e.message); + throw e; + } + } + warn(msg: string) { console.warn(this._buildMessage(msg)); } diff --git a/packages/babel-core/src/transformation/file/options/build-config-chain.js b/packages/babel-core/src/transformation/file/options/build-config-chain.js index 5902d4c16e..a5af4e82ba 100644 --- a/packages/babel-core/src/transformation/file/options/build-config-chain.js +++ b/packages/babel-core/src/transformation/file/options/build-config-chain.js @@ -1,4 +1,3 @@ -import type Logger from "../logger"; import resolve from "../../../helpers/resolve"; import json5 from "json5"; import path from "path"; @@ -21,9 +20,9 @@ function exists(filename) { } } -export default function buildConfigChain(opts: Object = {}, log?: Logger) { +export default function buildConfigChain(opts: Object = {}) { const filename = opts.filename; - const builder = new ConfigChainBuilder(log); + const builder = new ConfigChainBuilder(); // resolve all .babelrc files if (opts.babelrc !== false) { @@ -40,10 +39,9 @@ export default function buildConfigChain(opts: Object = {}, log?: Logger) { } class ConfigChainBuilder { - constructor(log?: Logger) { + constructor() { this.resolvedConfigs = []; this.configs = []; - this.log = log; } errorMultipleConfigs(loc1: string, loc2: string) { @@ -183,7 +181,7 @@ class ConfigChainBuilder { if (extendsLoc) { this.addConfig(extendsLoc); } else { - if (this.log) this.log.error(`Couldn't resolve extends clause of ${options.extends} in ${alias}`); + throw new Error(`Couldn't resolve extends clause of ${options.extends} in ${alias}`); } delete options.extends; } diff --git a/packages/babel-core/src/transformation/file/options/option-manager.js b/packages/babel-core/src/transformation/file/options/option-manager.js index 6c63ba32fa..762a643784 100644 --- a/packages/babel-core/src/transformation/file/options/option-manager.js +++ b/packages/babel-core/src/transformation/file/options/option-manager.js @@ -1,5 +1,4 @@ import * as context from "../../../index"; -import type Logger from "../logger"; import Plugin from "../../plugin"; import * as messages from "babel-messages"; import { normaliseOptions } from "./index"; @@ -35,15 +34,13 @@ type MergeOptions = { }; export default class OptionManager { - constructor(log?: Logger) { + constructor() { this.resolvedConfigs = []; this.options = OptionManager.createBareOptions(); - this.log = log; } resolvedConfigs: Array; options: Object; - log: ?Logger; static memoisedPlugins: Array<{ container: Function; @@ -158,7 +155,7 @@ export default class OptionManager { // if (typeof rawOpts !== "object" || Array.isArray(rawOpts)) { - this.log.error(`Invalid options type for ${alias}`, TypeError); + throw new TypeError(`Invalid options type for ${alias}`); } // @@ -176,15 +173,14 @@ export default class OptionManager { const option = config[key]; // check for an unknown option - if (!option && this.log) { + if (!option) { if (removed[key]) { - this.log.error(`Using removed Babel 5 option: ${alias}.${key} - ${removed[key].message}`, - ReferenceError); + throw new ReferenceError(`Using removed Babel 5 option: ${alias}.${key} - ${removed[key].message}`); } else { // eslint-disable-next-line max-len const unknownOptErr = `Unknown option: ${alias}.${key}. Check out http://babeljs.io/docs/usage/options/ for more information about options.`; - this.log.error(unknownOptErr, ReferenceError); + throw new ReferenceError(unknownOptErr); } } } @@ -333,7 +329,7 @@ export default class OptionManager { } init(opts: Object = {}): Object { - for (const config of buildConfigChain(opts, this.log)) { + for (const config of buildConfigChain(opts)) { this.mergeOptions(config); } diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index bf03ff2f46..097b515b1a 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -141,7 +141,7 @@ describe("api", function () { plugins: [__dirname + "/../../babel-plugin-syntax-jsx", false], }); }, - /TypeError: Falsy value found in plugins/ + /TypeError: \[BABEL\] unknown: Falsy value found in plugins/ ); }); diff --git a/packages/babel-core/test/option-manager.js b/packages/babel-core/test/option-manager.js index a38436d197..126ed83c27 100644 --- a/packages/babel-core/test/option-manager.js +++ b/packages/babel-core/test/option-manager.js @@ -1,6 +1,5 @@ import assert from "assert"; import OptionManager from "../lib/transformation/file/options/option-manager"; -import Logger from "../lib/transformation/file/logger"; import path from "path"; describe("option-manager", () => { @@ -17,7 +16,7 @@ describe("option-manager", () => { it("throws for removed babel 5 options", () => { return assert.throws( () => { - const opt = new OptionManager(new Logger(null, "unknown")); + const opt = new OptionManager(); opt.init({ "randomOption": true, }); @@ -29,7 +28,7 @@ describe("option-manager", () => { it("throws for removed babel 5 options", () => { return assert.throws( () => { - const opt = new OptionManager(new Logger(null, "unknown")); + const opt = new OptionManager(); opt.init({ "auxiliaryComment": true, "blacklist": true, @@ -43,7 +42,7 @@ describe("option-manager", () => { it("throws for resolved but erroring preset", () => { return assert.throws( () => { - const opt = new OptionManager(new Logger(null, "unknown")); + const opt = new OptionManager(); opt.init({ "presets": [path.join(__dirname, "fixtures/option-manager/not-a-preset")], }); @@ -56,7 +55,7 @@ describe("option-manager", () => { describe("presets", function () { function presetTest(name) { it(name, function () { - const opt = new OptionManager(new Logger(null, "unknown")); + const opt = new OptionManager(); const options = opt.init({ "presets": [path.join(__dirname, "fixtures/option-manager/presets", name)], }); @@ -68,7 +67,7 @@ describe("option-manager", () => { function presetThrowsTest(name, msg) { it(name, function () { - const opt = new OptionManager(new Logger(null, "unknown")); + const opt = new OptionManager(); assert.throws(() => opt.init({ "presets": [path.join(__dirname, "fixtures/option-manager/presets", name)], }), msg);