From f9bac2a358514d871632a9c715cd4f9c25474532 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Thu, 13 Apr 2017 16:41:50 -0700 Subject: [PATCH 1/2] Implement caching of plugins/presets/options --- .../babel-core/src/config/option-manager.js | 137 +++++----- packages/babel-core/test/config-loading.js | 254 ++++++++++++++++++ .../test/fixtures/config-loading/.babelrc.js | 14 + .../config-loading/folder/.babelignore | 2 + .../test/fixtures/config-loading/plugin1.js | 7 + .../test/fixtures/config-loading/plugin2.js | 7 + .../test/fixtures/config-loading/plugin3.js | 7 + .../test/fixtures/config-loading/plugin4.js | 7 + .../test/fixtures/config-loading/plugin5.js | 7 + .../test/fixtures/config-loading/plugin6.js | 7 + .../test/fixtures/config-loading/preset1.js | 9 + .../test/fixtures/config-loading/preset2.js | 9 + .../test/fixtures/config-loading/preset3.js | 9 + 13 files changed, 409 insertions(+), 67 deletions(-) create mode 100644 packages/babel-core/test/config-loading.js create mode 100644 packages/babel-core/test/fixtures/config-loading/.babelrc.js create mode 100644 packages/babel-core/test/fixtures/config-loading/folder/.babelignore create mode 100644 packages/babel-core/test/fixtures/config-loading/plugin1.js create mode 100644 packages/babel-core/test/fixtures/config-loading/plugin2.js create mode 100644 packages/babel-core/test/fixtures/config-loading/plugin3.js create mode 100644 packages/babel-core/test/fixtures/config-loading/plugin4.js create mode 100644 packages/babel-core/test/fixtures/config-loading/plugin5.js create mode 100644 packages/babel-core/test/fixtures/config-loading/plugin6.js create mode 100644 packages/babel-core/test/fixtures/config-loading/preset1.js create mode 100644 packages/babel-core/test/fixtures/config-loading/preset2.js create mode 100644 packages/babel-core/test/fixtures/config-loading/preset3.js diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index 41610ba722..d1d37579b5 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -9,6 +9,8 @@ import buildConfigChain from "./build-config-chain"; import path from "path"; import traverse from "babel-traverse"; import clone from "lodash/clone"; +import { makeWeakCache } from "./caching"; +import { getEnv } from "./helpers/environment"; import { loadPlugin, @@ -221,13 +223,11 @@ type BasicDescriptor = { /** * Load and validate the given config into a set of options, plugins, and presets. */ -function loadConfig( - config, -): { +const loadConfig = makeWeakCache((config): { options: {}, plugins: Array, presets: Array, -} { +} => { const options = normalizeOptions(config); if ( @@ -277,24 +277,25 @@ function loadConfig( }); return { options, plugins, presets }; -} +}); /** * Load a generic plugin/preset from the given descriptor loaded from the config object. */ -function loadDescriptor(descriptor, skipOptions) { +const loadDescriptor = makeWeakCache((descriptor, cache) => { if (typeof descriptor.value !== "function") { return { value: descriptor.value, descriptor }; } - const { value, options } = descriptor; + + const api = Object.assign(Object.create(context), { + cache, + env: () => cache.using(() => getEnv()), + }); + let item; try { - if (skipOptions) { - item = value(context); - } else { - item = value(context, options, { dirname: descriptor.dirname }); - } + item = value(api, options, { dirname: descriptor.dirname }); } catch (e) { if (descriptor.alias) { e.message += ` (While processing: ${JSON.stringify(descriptor.alias)})`; @@ -307,92 +308,94 @@ function loadDescriptor(descriptor, skipOptions) { } return { value: item, descriptor }; -} +}); /** * Instantiate a plugin for the given descriptor, returning the plugin/options pair. */ -const PLUGIN_CACHE = new WeakMap(); -function loadPluginDescriptor(descriptor) { +function loadPluginDescriptor(descriptor: BasicDescriptor) { if (descriptor.value instanceof Plugin) { return [descriptor.value, descriptor.options]; } - let result = PLUGIN_CACHE.get(descriptor.value); - if (!result) { - result = instantiatePlugin( - loadDescriptor(descriptor, true /* skipOptions */), - ); - PLUGIN_CACHE.set(descriptor.value, result); - } + const result = instantiatePlugin(loadDescriptor(descriptor)); return [result, descriptor.options]; } -function instantiatePlugin({ value: pluginObj, descriptor }) { - Object.keys(pluginObj).forEach(key => { - if (!ALLOWED_PLUGIN_KEYS.has(key)) { +const instantiatePlugin = makeWeakCache( + ({ value: pluginObj, descriptor }, cache) => { + Object.keys(pluginObj).forEach(key => { + if (!ALLOWED_PLUGIN_KEYS.has(key)) { + throw new Error( + `Plugin ${descriptor.alias} provided an invalid property of ${key}`, + ); + } + }); + if ( + pluginObj.visitor && + (pluginObj.visitor.enter || pluginObj.visitor.exit) + ) { throw new Error( - `Plugin ${descriptor.alias} provided an invalid property of ${key}`, + "Plugins aren't allowed to specify catch-all enter/exit handlers. " + + "Please target individual nodes.", ); } - }); - if ( - pluginObj.visitor && - (pluginObj.visitor.enter || pluginObj.visitor.exit) - ) { - throw new Error( - "Plugins aren't allowed to specify catch-all enter/exit handlers. " + - "Please target individual nodes.", - ); - } - const plugin = Object.assign({}, pluginObj, { - visitor: clone(pluginObj.visitor || {}), - }); + const plugin = Object.assign({}, pluginObj, { + visitor: clone(pluginObj.visitor || {}), + }); - traverse.explode(plugin.visitor); + traverse.explode(plugin.visitor); - let inheritsDescriptor; - let inherits; - if (plugin.inherits) { - inheritsDescriptor = { - alias: `${descriptor.loc}$inherits`, - loc: descriptor.loc, - value: plugin.inherits, - options: descriptor.options, - dirname: descriptor.dirname, - }; + let inheritsDescriptor; + let inherits; + if (plugin.inherits) { + inheritsDescriptor = { + alias: `${descriptor.loc}$inherits`, + loc: descriptor.loc, + value: plugin.inherits, + options: descriptor.options, + dirname: descriptor.dirname, + }; - inherits = loadPluginDescriptor(inheritsDescriptor)[0]; + // If the inherited plugin changes, reinstantiate this plugin. + inherits = cache.invalidate( + () => loadPluginDescriptor(inheritsDescriptor)[0], + ); - plugin.pre = chain(inherits.pre, plugin.pre); - plugin.post = chain(inherits.post, plugin.post); - plugin.manipulateOptions = chain( - inherits.manipulateOptions, - plugin.manipulateOptions, - ); - plugin.visitor = traverse.visitors.merge([ - inherits.visitor, - plugin.visitor, - ]); - } + plugin.pre = chain(inherits.pre, plugin.pre); + plugin.post = chain(inherits.post, plugin.post); + plugin.manipulateOptions = chain( + inherits.manipulateOptions, + plugin.manipulateOptions, + ); + plugin.visitor = traverse.visitors.merge([ + inherits.visitor, + plugin.visitor, + ]); + } - return new Plugin(plugin, descriptor.alias); -} + return new Plugin(plugin, descriptor.alias); + }, +); /** * Generate a config object that will act as the root of a new nested config. */ -function loadPresetDescriptor(descriptor): MergeOptions { +const loadPresetDescriptor = (descriptor: BasicDescriptor): MergeOptions => { + return instantiatePreset(loadDescriptor(descriptor)); +}; + +const instantiatePreset = makeWeakCache(({ value, descriptor }) => { return { type: "preset", - options: loadDescriptor(descriptor).value, + options: value, alias: descriptor.alias, loc: descriptor.loc, dirname: descriptor.dirname, }; -} +}); /** * Validate and return the options object for the config. diff --git a/packages/babel-core/test/config-loading.js b/packages/babel-core/test/config-loading.js new file mode 100644 index 0000000000..6eb06e938f --- /dev/null +++ b/packages/babel-core/test/config-loading.js @@ -0,0 +1,254 @@ +import loadConfig from "../lib/config"; +import path from "path"; +import { expect } from "chai"; + +describe("babel-core config loading", () => { + const FILEPATH = path.join( + __dirname, + "fixtures", + "config-loading", + "folder", + "example.js", + ); + + afterEach(() => { + delete process.env.INVALIDATE_BABELRC; + delete process.env.INVALIDATE_PRESET1; + delete process.env.INVALIDATE_PRESET2; + delete process.env.INVALIDATE_PRESET3; + delete process.env.INVALIDATE_PLUGIN1; + delete process.env.INVALIDATE_PLUGIN2; + delete process.env.INVALIDATE_PLUGIN3; + delete process.env.INVALIDATE_PLUGIN4; + delete process.env.INVALIDATE_PLUGIN5; + delete process.env.INVALIDATE_PLUGIN6; + }); + + function makeOpts(skipProgrammatic = false) { + return { + filename: FILEPATH, + presets: skipProgrammatic + ? null + : [require("./fixtures/config-loading/preset3")], + plugins: skipProgrammatic + ? null + : [require("./fixtures/config-loading/plugin6")], + }; + } + + describe("config file", () => { + it("should load and cache the config with plugins and presets", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + expect(options1.plugins.map(p => p.key)).to.eql([ + "plugin6", + "plugin5", + "plugin1", + "plugin2", + "plugin4", + "plugin3", + ]); + + const options2 = loadConfig(opts).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options2.plugins.length; i++) { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + }); + + it("should load and cache the config for unique opts objects", () => { + const options1 = loadConfig(makeOpts(true)).options; + expect(options1.plugins.map(p => p.key)).to.eql([ + "plugin1", + "plugin2", + "plugin4", + "plugin3", + ]); + + const options2 = loadConfig(makeOpts(true)).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options2.plugins.length; i++) { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + }); + + it("should invalidate config file plugins", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + process.env.INVALIDATE_PLUGIN1 = true; + + const options2 = loadConfig(opts).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 2) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + + process.env.INVALIDATE_PLUGIN3 = true; + + const options3 = loadConfig(opts).options; + expect(options3.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 2 || i === 5) { + expect(options3.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options3.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + + it("should invalidate config file presets and their children", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + process.env.INVALIDATE_PRESET1 = true; + + const options2 = loadConfig(opts).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 5) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + + process.env.INVALIDATE_PRESET2 = true; + + const options3 = loadConfig(opts).options; + expect(options3.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 4 || i === 5) { + expect(options3.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options3.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + + it("should invalidate the config file and its plugins/presets", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + process.env.INVALIDATE_BABELRC = true; + + const options2 = loadConfig(opts).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 2 || i === 3 || i === 4 || i === 5 || i === 6) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + }); + + describe("programmatic plugins/presets", () => { + it("should not invalidate the plugins when given a fresh object", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + const options2 = loadConfig(Object.assign({}, opts)).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options2.plugins.length; i++) { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + }); + + it("should invalidate the plugins when given a fresh arrays", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + const options2 = loadConfig({ + ...opts, + plugins: opts.plugins.slice(), + }).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options2.plugins.length; i++) { + if (i === 0) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + + it("should invalidate the presets when given a fresh arrays", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + const options2 = loadConfig({ + ...opts, + presets: opts.presets.slice(), + }).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options2.plugins.length; i++) { + if (i === 1) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + + it("should invalidate the programmatic plugins", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + process.env.INVALIDATE_PLUGIN6 = true; + + const options2 = loadConfig(opts).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 0) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + + it("should invalidate the programmatic presets and their children", () => { + const opts = makeOpts(); + + const options1 = loadConfig(opts).options; + + process.env.INVALIDATE_PRESET3 = true; + + const options2 = loadConfig(opts).options; + expect(options2.plugins.length).to.equal(options1.plugins.length); + + for (let i = 0; i < options1.plugins.length; i++) { + if (i === 1) { + expect(options2.plugins[i]).not.to.equal(options1.plugins[i]); + } else { + expect(options2.plugins[i]).to.equal(options1.plugins[i]); + } + } + }); + }); +}); diff --git a/packages/babel-core/test/fixtures/config-loading/.babelrc.js b/packages/babel-core/test/fixtures/config-loading/.babelrc.js new file mode 100644 index 0000000000..2a890d96e7 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/.babelrc.js @@ -0,0 +1,14 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_BABELRC); + + return { + plugins: [ + "./plugin1", + "./plugin2", + ], + presets: [ + "./preset1", + "./preset2", + ], + } +}; diff --git a/packages/babel-core/test/fixtures/config-loading/folder/.babelignore b/packages/babel-core/test/fixtures/config-loading/folder/.babelignore new file mode 100644 index 0000000000..0098e2b67a --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/folder/.babelignore @@ -0,0 +1,2 @@ +# No-op .babelignore to stop babel from skipping these, which the root +# .babelignore tells it to do. diff --git a/packages/babel-core/test/fixtures/config-loading/plugin1.js b/packages/babel-core/test/fixtures/config-loading/plugin1.js new file mode 100644 index 0000000000..83701eba9c --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/plugin1.js @@ -0,0 +1,7 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_PLUGIN1); + + return { + name: "plugin1", + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/plugin2.js b/packages/babel-core/test/fixtures/config-loading/plugin2.js new file mode 100644 index 0000000000..4181e8f5d5 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/plugin2.js @@ -0,0 +1,7 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_PLUGIN2); + + return { + name: "plugin2", + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/plugin3.js b/packages/babel-core/test/fixtures/config-loading/plugin3.js new file mode 100644 index 0000000000..4d84779a69 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/plugin3.js @@ -0,0 +1,7 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_PLUGIN3); + + return { + name: "plugin3", + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/plugin4.js b/packages/babel-core/test/fixtures/config-loading/plugin4.js new file mode 100644 index 0000000000..462b280f86 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/plugin4.js @@ -0,0 +1,7 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_PLUGIN4); + + return { + name: "plugin4", + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/plugin5.js b/packages/babel-core/test/fixtures/config-loading/plugin5.js new file mode 100644 index 0000000000..bb049350a9 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/plugin5.js @@ -0,0 +1,7 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_PLUGIN5); + + return { + name: "plugin5", + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/plugin6.js b/packages/babel-core/test/fixtures/config-loading/plugin6.js new file mode 100644 index 0000000000..577c03c4ae --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/plugin6.js @@ -0,0 +1,7 @@ +module.exports = function(api) { + api.cache.using(() => process.env.INVALIDATE_PLUGIN6); + + return { + name: "plugin6", + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/preset1.js b/packages/babel-core/test/fixtures/config-loading/preset1.js new file mode 100644 index 0000000000..6364f90cb8 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/preset1.js @@ -0,0 +1,9 @@ +module.exports = function(api, options) { + api.cache.using(() => process.env.INVALIDATE_PRESET1); + + return { + plugins: [ + [require('./plugin3.js'), options], + ], + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/preset2.js b/packages/babel-core/test/fixtures/config-loading/preset2.js new file mode 100644 index 0000000000..7a15eb85ec --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/preset2.js @@ -0,0 +1,9 @@ +module.exports = function(api, options) { + api.cache.using(() => process.env.INVALIDATE_PRESET2); + + return { + plugins: [ + [require('./plugin4.js'), options], + ], + }; +}; diff --git a/packages/babel-core/test/fixtures/config-loading/preset3.js b/packages/babel-core/test/fixtures/config-loading/preset3.js new file mode 100644 index 0000000000..e1e7d5ce13 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/preset3.js @@ -0,0 +1,9 @@ +module.exports = function(api, options) { + api.cache.using(() => process.env.INVALIDATE_PRESET3); + + return { + plugins: [ + [require('./plugin5.js'), options], + ], + }; +}; From 35312dc3d296f1fe48c0a3e58df66a9f1a7c9743 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Thu, 6 Apr 2017 18:38:38 -0700 Subject: [PATCH 2/2] Track options on the plugin instance to avoid array pair usage. --- packages/babel-core/src/config/index.js | 2 +- .../babel-core/src/config/option-manager.js | 24 +++++++++++-------- packages/babel-core/src/config/plugin.js | 5 +++- .../src/transformation/block-hoist-plugin.js | 6 ++--- .../babel-core/src/transformation/index.js | 6 ++--- .../src/transformation/normalize-opts.js | 2 +- packages/babel-core/test/api.js | 5 ++-- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/babel-core/src/config/index.js b/packages/babel-core/src/config/index.js index 61684308e3..2676d24047 100644 --- a/packages/babel-core/src/config/index.js +++ b/packages/babel-core/src/config/index.js @@ -9,7 +9,7 @@ export type ResolvedConfig = { }; export type { Plugin }; -export type PluginPassList = Array<[Plugin, ?{}]>; +export type PluginPassList = Array; export type PluginPasses = Array; /** diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index d1d37579b5..2e504fe161 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -79,7 +79,7 @@ const ALLOWED_PLUGIN_KEYS = new Set([ export default function manageOptions(opts: {}): { options: Object, - passes: Array>, + passes: Array>, } | null { return new OptionManager().init(opts); } @@ -91,7 +91,7 @@ class OptionManager { } options: Object; - passes: Array>; + passes: Array>; /** * This is called when we want to merge the input `opts` into the @@ -102,7 +102,7 @@ class OptionManager { * - `dirname` is used to resolve plugins relative to it. */ - mergeOptions(config: MergeOptions, pass?: Array<[Plugin, ?{}]>) { + mergeOptions(config: MergeOptions, pass?: Array) { const result = loadConfig(config); const plugins = result.plugins.map(descriptor => @@ -315,12 +315,16 @@ const loadDescriptor = makeWeakCache((descriptor, cache) => { */ function loadPluginDescriptor(descriptor: BasicDescriptor) { if (descriptor.value instanceof Plugin) { - return [descriptor.value, descriptor.options]; + if (descriptor.options) { + throw new Error( + "Passed options to an existing Plugin instance will not work.", + ); + } + + return descriptor.value; } - const result = instantiatePlugin(loadDescriptor(descriptor)); - - return [result, descriptor.options]; + return instantiatePlugin(loadDescriptor(descriptor)); } const instantiatePlugin = makeWeakCache( @@ -360,8 +364,8 @@ const instantiatePlugin = makeWeakCache( }; // If the inherited plugin changes, reinstantiate this plugin. - inherits = cache.invalidate( - () => loadPluginDescriptor(inheritsDescriptor)[0], + inherits = cache.invalidate(() => + loadPluginDescriptor(inheritsDescriptor), ); plugin.pre = chain(inherits.pre, plugin.pre); @@ -376,7 +380,7 @@ const instantiatePlugin = makeWeakCache( ]); } - return new Plugin(plugin, descriptor.alias); + return new Plugin(plugin, descriptor.options, descriptor.alias); }, ); diff --git a/packages/babel-core/src/config/plugin.js b/packages/babel-core/src/config/plugin.js index 58a768ffa2..fbf0e1b13e 100644 --- a/packages/babel-core/src/config/plugin.js +++ b/packages/babel-core/src/config/plugin.js @@ -7,7 +7,9 @@ export default class Plugin { pre: ?Function; visitor: ?{}; - constructor(plugin: {}, key?: string) { + options: {} | void; + + constructor(plugin: {}, options: ?{}, key?: string) { if (plugin.name != null && typeof plugin.name !== "string") { throw new Error("Plugin .name must be a string, null, or undefined"); } @@ -35,5 +37,6 @@ export default class Plugin { this.post = plugin.post; this.pre = plugin.pre; this.visitor = plugin.visitor; + this.options = options || undefined; } } diff --git a/packages/babel-core/src/transformation/block-hoist-plugin.js b/packages/babel-core/src/transformation/block-hoist-plugin.js index f307c09e89..d79301672a 100644 --- a/packages/babel-core/src/transformation/block-hoist-plugin.js +++ b/packages/babel-core/src/transformation/block-hoist-plugin.js @@ -6,7 +6,7 @@ import loadConfig, { type Plugin } from "../config"; let LOADED_PLUGIN: Plugin | void; -export default function loadBlockHoistPlugin(): [Plugin, void] { +export default function loadBlockHoistPlugin(): Plugin { if (!LOADED_PLUGIN) { // Lazy-init the internal plugin to remove the init-time circular // dependency between plugins being passed babel-core's export object, @@ -15,11 +15,11 @@ export default function loadBlockHoistPlugin(): [Plugin, void] { babelrc: false, plugins: [blockHoistPlugin], }); - LOADED_PLUGIN = config ? config.passes[0][0][0] : undefined; + LOADED_PLUGIN = config ? config.passes[0][0] : undefined; if (!LOADED_PLUGIN) throw new Error("Assertion failure"); } - return [LOADED_PLUGIN, undefined]; + return LOADED_PLUGIN; } const blockHoistPlugin = { diff --git a/packages/babel-core/src/transformation/index.js b/packages/babel-core/src/transformation/index.js index 44c281d7ff..e6d00dace2 100644 --- a/packages/babel-core/src/transformation/index.js +++ b/packages/babel-core/src/transformation/index.js @@ -49,10 +49,8 @@ function transformFile(file: File, pluginPasses: PluginPasses): void { const passes = []; const visitors = []; - for (const [plugin, pluginOpts] of pluginPairs.concat([ - loadBlockHoistPlugin(), - ])) { - const pass = new PluginPass(file, plugin.key, pluginOpts); + for (const plugin of pluginPairs.concat([loadBlockHoistPlugin()])) { + const pass = new PluginPass(file, plugin.key, plugin.options); passPairs.push([plugin, pass]); passes.push(pass); diff --git a/packages/babel-core/src/transformation/normalize-opts.js b/packages/babel-core/src/transformation/normalize-opts.js index 1746ce4d27..f9844de43e 100644 --- a/packages/babel-core/src/transformation/normalize-opts.js +++ b/packages/babel-core/src/transformation/normalize-opts.js @@ -15,7 +15,7 @@ export default function normalizeOptions(config: ResolvedConfig): {} { }); for (const pluginPairs of config.passes) { - for (const [plugin] of pluginPairs) { + for (const plugin of pluginPairs) { if (plugin.manipulateOptions) { plugin.manipulateOptions(options, options.parserOpts); } diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index b9c0f76d4d..2bcbd9007d 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -154,9 +154,8 @@ describe("api", function() { plugins: [__dirname + "/../../babel-plugin-syntax-jsx"], }).then(function(result) { assert.ok( - result.options.plugins[0][0].manipulateOptions - .toString() - .indexOf("jsx") >= 0, + result.options.plugins[0].manipulateOptions.toString().indexOf("jsx") >= + 0, ); }); });