Add RegExp support to include/exclude preset-env options (#7242)
* Add support for RegExp includes/excludes * Keep the plugin order * Detect invalid modules in regexp * Add more tests for regexp * Cover builtins, and unnormalized in the RegExp tests * Remove babel-plugin- in all positions * Change babel-plugin- prefix to string * Add a test for the same module in include/exclude * Handle partial matches explicitly * Remove extra valid regexp check * Optimise validation of plugins * Optimise selecting the plugins * Fix undefined include/exclude option * Update documentation to reflect the new include matching * Fix typo * Apply reviews Use regexp.test instead of string.match (slower) Define flatten helper Do not normalize babel-plugin anywhere in the string
This commit is contained in:
parent
d260bfaec4
commit
8eee435cd6
@ -245,7 +245,7 @@ Outputs the targets/plugins used and the version specified in [plugin data versi
|
||||
|
||||
### `include`
|
||||
|
||||
`Array<string>`, defaults to `[]`.
|
||||
`Array<string|RegExp>`, defaults to `[]`.
|
||||
|
||||
An array of plugins to always include.
|
||||
|
||||
@ -255,6 +255,16 @@ Valid options include any:
|
||||
|
||||
- [Built-ins](https://github.com/babel/babel/blob/master/packages/babel-preset-env/data/built-in-features.js), such as `es6.map`, `es6.set`, or `es6.object.assign`.
|
||||
|
||||
Plugin names can be fully or partially specified (or using `RegExp`).
|
||||
|
||||
Acceptable inputs:
|
||||
|
||||
- Full name (`string`): `"es6.math.sign"`
|
||||
- Partial name (`string`): `"es6.math.*"` (resolves to all plugins with `es6.math` prefix)
|
||||
- `RegExp` Object: `/^transform-.*$/` or `new RegExp("^transform-modules-.*")`
|
||||
|
||||
Note that the above `.` is the `RegExp` equivalent to match any character, and not the actual `'.'` character. Also note that to match any character `.*` is used in `RegExp` as opposed to `*` in `glob` format.
|
||||
|
||||
This option is useful if there is a bug in a native implementation, or a combination of a non-supported feature + a supported one doesn't work.
|
||||
|
||||
For example, Node 4 supports native classes but not spread. If `super` is used with a spread argument, then the `@babel/plugin-transform-classes` transform needs to be `include`d, as it is not possible to transpile a spread with `super` otherwise.
|
||||
@ -263,7 +273,7 @@ For example, Node 4 supports native classes but not spread. If `super` is used w
|
||||
|
||||
### `exclude`
|
||||
|
||||
`Array<string>`, defaults to `[]`.
|
||||
`Array<string|RegExp>`, defaults to `[]`.
|
||||
|
||||
An array of plugins to always exclude/remove.
|
||||
|
||||
|
||||
@ -15,26 +15,44 @@ const validIncludesAndExcludes = new Set([
|
||||
...defaultWebIncludes,
|
||||
]);
|
||||
|
||||
export const validateIncludesAndExcludes = (
|
||||
opts: Array<string> = [],
|
||||
type: string,
|
||||
): Array<string> => {
|
||||
invariant(
|
||||
Array.isArray(opts),
|
||||
`Invalid Option: The '${type}' option must be an Array<String> of plugins/built-ins`,
|
||||
const pluginToRegExp = (plugin: any): RegExp => {
|
||||
if (plugin instanceof RegExp) return plugin;
|
||||
try {
|
||||
return new RegExp(`^${normalizePluginName(plugin)}$`);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const selectPlugins = (regexp: RegExp): Array<string> =>
|
||||
Array.from(validIncludesAndExcludes).filter(
|
||||
item => regexp instanceof RegExp && regexp.test(item),
|
||||
);
|
||||
|
||||
const unknownOpts = opts.filter(opt => !validIncludesAndExcludes.has(opt));
|
||||
const flatten = array => [].concat(...array);
|
||||
|
||||
const expandIncludesAndExcludes = (
|
||||
plugins: Array<string | RegExp> = [],
|
||||
type: string,
|
||||
): Array<string> => {
|
||||
if (plugins.length === 0) return plugins;
|
||||
|
||||
const selectedPlugins = plugins.map(plugin =>
|
||||
selectPlugins(pluginToRegExp(plugin)),
|
||||
);
|
||||
const invalidRegExpList = plugins.filter(
|
||||
(p, i) => selectedPlugins[i].length === 0,
|
||||
);
|
||||
|
||||
invariant(
|
||||
unknownOpts.length === 0,
|
||||
`Invalid Option: The plugins/built-ins '${unknownOpts.join(
|
||||
invalidRegExpList.length === 0,
|
||||
`Invalid Option: The plugins/built-ins '${invalidRegExpList.join(
|
||||
", ",
|
||||
)}' passed to the '${type}' option are not
|
||||
valid. Please check data/[plugin-features|built-in-features].js in babel-preset-env`,
|
||||
);
|
||||
|
||||
return opts;
|
||||
return flatten(selectedPlugins);
|
||||
};
|
||||
|
||||
const validBrowserslistTargets = [
|
||||
@ -45,9 +63,6 @@ const validBrowserslistTargets = [
|
||||
export const normalizePluginName = (plugin: string): string =>
|
||||
plugin.replace(/^babel-plugin-/, "");
|
||||
|
||||
export const normalizePluginNames = (plugins: Array<string>): Array<string> =>
|
||||
plugins.map(normalizePluginName);
|
||||
|
||||
export const checkDuplicateIncludeExcludes = (
|
||||
include: Array<string> = [],
|
||||
exclude: Array<string> = [],
|
||||
@ -138,20 +153,16 @@ export const validateUseBuiltInsOption = (
|
||||
};
|
||||
|
||||
export default function normalizeOptions(opts: Options) {
|
||||
if (opts.exclude) {
|
||||
opts.exclude = normalizePluginNames(opts.exclude);
|
||||
}
|
||||
const include = expandIncludesAndExcludes(opts.include, "include");
|
||||
const exclude = expandIncludesAndExcludes(opts.exclude, "exclude");
|
||||
|
||||
if (opts.include) {
|
||||
opts.include = normalizePluginNames(opts.include);
|
||||
}
|
||||
|
||||
checkDuplicateIncludeExcludes(opts.include, opts.exclude);
|
||||
checkDuplicateIncludeExcludes(include, exclude);
|
||||
|
||||
return {
|
||||
configPath: validateConfigPathOption(opts.configPath),
|
||||
debug: opts.debug,
|
||||
exclude: validateIncludesAndExcludes(opts.exclude, "exclude"),
|
||||
include,
|
||||
exclude,
|
||||
forceAllTransforms: validateBoolOption(
|
||||
"forceAllTransforms",
|
||||
opts.forceAllTransforms,
|
||||
@ -160,7 +171,6 @@ export default function normalizeOptions(opts: Options) {
|
||||
ignoreBrowserslistConfig: validateIgnoreBrowserslistConfig(
|
||||
opts.ignoreBrowserslistConfig,
|
||||
),
|
||||
include: validateIncludesAndExcludes(opts.include, "include"),
|
||||
loose: validateBoolOption("loose", opts.loose, false),
|
||||
modules: validateModulesOption(opts.modules),
|
||||
shippedProposals: validateBoolOption(
|
||||
|
||||
@ -5,12 +5,10 @@ const assert = require("assert");
|
||||
|
||||
const {
|
||||
checkDuplicateIncludeExcludes,
|
||||
normalizePluginNames,
|
||||
validateBoolOption,
|
||||
validateIncludesAndExcludes,
|
||||
validateModulesOption,
|
||||
normalizePluginName,
|
||||
} = normalizeOptions;
|
||||
|
||||
describe("normalize-options", () => {
|
||||
describe("normalizeOptions", () => {
|
||||
it("should return normalized `include` and `exclude`", () => {
|
||||
@ -23,6 +21,11 @@ describe("normalize-options", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("should not normalize babel-plugin with prefix", () => {
|
||||
const normalized = normalizePluginName("prefix-babel-plugin-postfix");
|
||||
assert.equal(normalized, "prefix-babel-plugin-postfix");
|
||||
});
|
||||
|
||||
it("should throw if duplicate names in `include` and `exclude`", () => {
|
||||
const normalizeWithSameIncludes = () => {
|
||||
normalizeOptions.default({
|
||||
@ -34,6 +37,57 @@ describe("normalize-options", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("RegExp include/exclude", () => {
|
||||
it("should not allow invalid plugins in `include` and `exclude`", () => {
|
||||
const normalizeWithNonExistingPlugin = () => {
|
||||
normalizeOptions.default({
|
||||
include: ["non-existing-plugin"],
|
||||
});
|
||||
};
|
||||
assert.throws(normalizeWithNonExistingPlugin, Error);
|
||||
});
|
||||
|
||||
it("should expand regular expressions in `include` and `exclude`", () => {
|
||||
const normalized = normalizeOptions.default({
|
||||
include: ["^[a-z]*-spread", "babel-plugin-transform-classes"],
|
||||
});
|
||||
assert.deepEqual(normalized.include, [
|
||||
"transform-spread",
|
||||
"transform-classes",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should expand regular expressions in `include` and `exclude`", () => {
|
||||
const normalized = normalizeOptions.default({
|
||||
exclude: ["es6.math.log.*"],
|
||||
});
|
||||
assert.deepEqual(normalized.exclude, [
|
||||
"es6.math.log1p",
|
||||
"es6.math.log10",
|
||||
"es6.math.log2",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should not allow the same modules in `include` and `exclude`", () => {
|
||||
const normalizeWithNonExistingPlugin = () => {
|
||||
normalizeOptions.default({
|
||||
include: ["es6.math.log2"],
|
||||
exclude: ["es6.math.log.*"],
|
||||
});
|
||||
};
|
||||
assert.throws(normalizeWithNonExistingPlugin, Error);
|
||||
});
|
||||
|
||||
it("should not do partial match if not explicitly defined `include` and `exclude`", () => {
|
||||
const normalized = normalizeOptions.default({
|
||||
include: ["es6.reflect.set-prototype-of"],
|
||||
exclude: ["es6.reflect.set"],
|
||||
});
|
||||
assert.deepEqual(normalized.include, ["es6.reflect.set-prototype-of"]);
|
||||
assert.deepEqual(normalized.exclude, ["es6.reflect.set"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateBoolOption", () => {
|
||||
it("`undefined` option returns false", () => {
|
||||
assert(validateBoolOption("test", undefined, false) === false);
|
||||
@ -71,24 +125,6 @@ describe("normalize-options", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizePluginNames", function() {
|
||||
it("should drop `babel-plugin-` prefix if needed", function() {
|
||||
assert.deepEqual(
|
||||
normalizePluginNames([
|
||||
"babel-plugin-transform-object-super",
|
||||
"transform-parameters",
|
||||
]),
|
||||
["transform-object-super", "transform-parameters"],
|
||||
);
|
||||
});
|
||||
|
||||
it("should not throw if no duplicate names in both", function() {
|
||||
assert.doesNotThrow(() => {
|
||||
checkDuplicateIncludeExcludes(["transform-regenerator"], ["map"]);
|
||||
}, Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateModulesOption", () => {
|
||||
it("`undefined` option returns commonjs", () => {
|
||||
assert(validateModulesOption() === "commonjs");
|
||||
@ -126,14 +162,4 @@ describe("normalize-options", () => {
|
||||
}, Error);
|
||||
});
|
||||
});
|
||||
describe("validateIncludesAndExcludes", function() {
|
||||
it("should return empty arrays if undefined", function() {
|
||||
assert.deepEqual(validateIncludesAndExcludes(), []);
|
||||
});
|
||||
it("should throw if not in features", function() {
|
||||
assert.throws(() => {
|
||||
validateIncludesAndExcludes(["asdf"]);
|
||||
}, Error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user