Merge pull request #5430 from loganfsmyth/opts-simplify

Simplify option and plugin pass processing
This commit is contained in:
Logan Smyth 2017-03-10 13:03:19 -08:00 committed by GitHub
commit 01b250a8fa
6 changed files with 57 additions and 76 deletions

View File

@ -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<string>;
pluginVisitors: Array<Object>;
pluginPasses: Array<PluginPass>;
pluginPasses: Array<Array<[Plugin, Object]>>;
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();

View File

@ -21,6 +21,15 @@ export default class Logger {
return parts;
}
wrap<T>(callback: () => T): T {
try {
return callback();
} catch (e) {
e.message = this._buildMessage(e.message);
throw e;
}
}
warn(msg: string) {
console.warn(this._buildMessage(msg));
}

View File

@ -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;
}

View File

@ -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<string>;
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);
}

View File

@ -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/
);
});

View File

@ -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);