Instantiate presets before plugins (#11689)

This commit is contained in:
Nicolò Ribaudo 2020-10-09 18:07:26 +02:00
parent ce7b170ab7
commit 1b90d90fcc
6 changed files with 109 additions and 90 deletions

View File

@ -24,6 +24,7 @@ import {
validate, validate,
type CallerMetadata, type CallerMetadata,
checkNoUnwrappedItemOptionPairs, checkNoUnwrappedItemOptionPairs,
type PluginItem,
} from "./validation/options"; } from "./validation/options";
import { validatePluginObject } from "./validation/plugins"; import { validatePluginObject } from "./validation/plugins";
import makeAPI from "./helpers/config-api"; import makeAPI from "./helpers/config-api";
@ -70,58 +71,59 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
} }
const optionDefaults = {}; const optionDefaults = {};
const passes: Array<Array<Plugin>> = [[]];
try {
const { plugins, presets } = options;
if (!plugins || !presets) { const { plugins, presets } = options;
throw new Error("Assertion failure - plugins and presets exist");
if (!plugins || !presets) {
throw new Error("Assertion failure - plugins and presets exist");
}
const toDescriptor = (item: PluginItem) => {
const desc = getItemDescriptor(item);
if (!desc) {
throw new Error("Assertion failure - must be config item");
} }
const ignored = yield* (function* recurseDescriptors(config, pass) { return desc;
const plugins: Array<Plugin> = []; };
for (let i = 0; i < config.plugins.length; i++) {
const descriptor = config.plugins[i];
if (descriptor.options !== false) {
try {
plugins.push(yield* loadPluginDescriptor(descriptor, context));
} catch (e) {
// print special message for `plugins: ["@babel/foo", { foo: "option" }]`
if (i > 0 && e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") {
checkNoUnwrappedItemOptionPairs(
config.plugins[i - 1],
descriptor,
"plugin",
i,
e,
);
}
throw e;
}
}
}
const presetsDescriptors = presets.map(toDescriptor);
const initialPluginsDescriptors = plugins.map(toDescriptor);
const pluginDescriptorsByPass: Array<Array<UnloadedDescriptor>> = [[]];
const passes: Array<Array<Plugin>> = [];
const ignored = yield* enhanceError(
context,
function* recursePresetDescriptors(
rawPresets: Array<UnloadedDescriptor>,
pluginDescriptorsPass: Array<UnloadedDescriptor>,
) {
const presets: Array<{| const presets: Array<{|
preset: ConfigChain | null, preset: ConfigChain | null,
pass: Array<Plugin>, pass: Array<UnloadedDescriptor>,
|}> = []; |}> = [];
for (let i = 0; i < config.presets.length; i++) {
const descriptor = config.presets[i]; for (let i = 0; i < rawPresets.length; i++) {
const descriptor = rawPresets[i];
if (descriptor.options !== false) { if (descriptor.options !== false) {
try { try {
presets.push({ // Presets normally run in reverse order, but if they
preset: yield* loadPresetDescriptor(descriptor, context), // have their own pass they run after the presets
pass: descriptor.ownPass ? [] : pass, // in the previous pass.
}); if (descriptor.ownPass) {
presets.push({
preset: yield* loadPresetDescriptor(descriptor, context),
pass: [],
});
} else {
presets.unshift({
preset: yield* loadPresetDescriptor(descriptor, context),
pass: pluginDescriptorsPass,
});
}
} catch (e) { } catch (e) {
if (i > 0 && e.code === "BABEL_UNKNOWN_OPTION") { if (e.code === "BABEL_UNKNOWN_OPTION") {
checkNoUnwrappedItemOptionPairs( checkNoUnwrappedItemOptionPairs(rawPresets, i, "preset", e);
config.presets[i - 1],
descriptor,
"preset",
i,
e,
);
} }
throw e; throw e;
} }
@ -132,22 +134,18 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
if (presets.length > 0) { if (presets.length > 0) {
// The passes are created in the same order as the preset list, but are inserted before any // The passes are created in the same order as the preset list, but are inserted before any
// existing additional passes. // existing additional passes.
passes.splice( pluginDescriptorsByPass.splice(
1, 1,
0, 0,
...presets.map(o => o.pass).filter(p => p !== pass), ...presets.map(o => o.pass).filter(p => p !== pluginDescriptorsPass),
); );
for (const { preset, pass } of presets) { for (const { preset, pass } of presets) {
if (!preset) return true; if (!preset) return true;
const ignored = yield* recurseDescriptors( pass.push(...preset.plugins);
{
plugins: preset.plugins, const ignored = yield* recursePresetDescriptors(preset.presets, pass);
presets: preset.presets,
},
pass,
);
if (ignored) return true; if (ignored) return true;
preset.options.forEach(opts => { preset.options.forEach(opts => {
@ -155,47 +153,38 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
}); });
} }
} }
},
)(presetsDescriptors, pluginDescriptorsByPass[0]);
// resolve plugins if (ignored) return null;
if (plugins.length > 0) {
pass.unshift(...plugins);
}
})(
{
plugins: plugins.map(item => {
const desc = getItemDescriptor(item);
if (!desc) {
throw new Error("Assertion failure - must be config item");
}
return desc;
}),
presets: presets.map(item => {
const desc = getItemDescriptor(item);
if (!desc) {
throw new Error("Assertion failure - must be config item");
}
return desc;
}),
},
passes[0],
);
if (ignored) return null;
} catch (e) {
// There are a few case where thrown errors will try to annotate themselves multiple times, so
// to keep things simple we just bail out if re-wrapping the message.
if (!/^\[BABEL\]/.test(e.message)) {
e.message = `[BABEL] ${context.filename || "unknown"}: ${e.message}`;
}
throw e;
}
const opts: Object = optionDefaults; const opts: Object = optionDefaults;
mergeOptions(opts, options); mergeOptions(opts, options);
yield* enhanceError(context, function* loadPluginDescriptors() {
pluginDescriptorsByPass[0].unshift(...initialPluginsDescriptors);
for (const descs of pluginDescriptorsByPass) {
const pass = [];
passes.push(pass);
for (let i = 0; i < descs.length; i++) {
const descriptor: UnloadedDescriptor = descs[i];
if (descriptor.options !== false) {
try {
pass.push(yield* loadPluginDescriptor(descriptor, context));
} catch (e) {
if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") {
// print special message for `plugins: ["@babel/foo", { foo: "option" }]`
checkNoUnwrappedItemOptionPairs(descs, i, "plugin", e);
}
throw e;
}
}
}
}
})();
opts.plugins = passes[0]; opts.plugins = passes[0];
opts.presets = passes opts.presets = passes
.slice(1) .slice(1)
@ -209,6 +198,22 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
}; };
}); });
function enhanceError<T: Function>(context, fn: T): T {
return (function* (arg1, arg2) {
try {
return yield* fn(arg1, arg2);
} catch (e) {
// There are a few case where thrown errors will try to annotate themselves multiple times, so
// to keep things simple we just bail out if re-wrapping the message.
if (!/^\[BABEL\]/.test(e.message)) {
e.message = `[BABEL] ${context.filename || "unknown"}: ${e.message}`;
}
throw e;
}
}: any);
}
/** /**
* Load a generic plugin/preset from the given descriptor loaded from the config object. * Load a generic plugin/preset from the given descriptor loaded from the config object.
*/ */

View File

@ -454,12 +454,16 @@ function assertOverridesList(loc: OptionPath, value: mixed): OverridesList {
} }
export function checkNoUnwrappedItemOptionPairs( export function checkNoUnwrappedItemOptionPairs(
lastItem: UnloadedDescriptor, items: Array<UnloadedDescriptor>,
thisItem: UnloadedDescriptor,
type: "plugin" | "preset",
index: number, index: number,
type: "plugin" | "preset",
e: Error, e: Error,
): void { ): void {
if (index === 0) return;
const lastItem = items[index - 1];
const thisItem = items[index];
if ( if (
lastItem.file && lastItem.file &&
lastItem.options === undefined && lastItem.options === undefined &&

View File

@ -423,6 +423,8 @@ describe("api", function () {
"argone;", "argone;",
"five;", "five;",
"six;", "six;",
"twentyone;",
"twentytwo;",
"three;", "three;",
"four;", "four;",
"nineteen;", "nineteen;",

View File

@ -14,6 +14,12 @@
"./five", "./five",
"./six", "./six",
], ],
presets: [{
plugins: [
"./twentyone",
"./twentytwo",
]
}]
}, { }, {
passPerPreset: true, passPerPreset: true,
presets: [{ presets: [{

View File

@ -0,0 +1 @@
module.exports = require("./plugin")("twentyone");

View File

@ -0,0 +1 @@
module.exports = require("./plugin")("twentytwo");