refactor: add @babel/helper-validator-option (#12006)
* refactor: add @babel/helper-validator-option * refactor: simplify validateTopLevelOptions * perf: the recursive version is not practically fast * Update packages/babel-helper-validator-option/README.md Co-authored-by: Brian Ng <bng412@gmail.com> * Update packages/babel-helper-validator-option/src/validator.js * fix: incorrect type annotation * refactor: use babel/helper-option-validator in babel/compat-data * chore: fix flow types error * Address review comments * address review comments Co-authored-by: Brian Ng <bng412@gmail.com>
This commit is contained in:
parent
0d32e3fc36
commit
f2da186714
@ -106,7 +106,10 @@ module.exports = function (api) {
|
||||
plugins: [
|
||||
// TODO: Use @babel/preset-flow when
|
||||
// https://github.com/babel/babel/issues/7233 is fixed
|
||||
"@babel/plugin-transform-flow-strip-types",
|
||||
[
|
||||
"@babel/plugin-transform-flow-strip-types",
|
||||
{ allowDeclareFields: true },
|
||||
],
|
||||
[
|
||||
"@babel/proposal-object-rest-spread",
|
||||
{ useBuiltIns: true, loose: true },
|
||||
|
||||
@ -22,9 +22,8 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "workspace:^7.10.4",
|
||||
"@babel/helper-validator-option": "workspace:^7.11.4",
|
||||
"browserslist": "^4.12.0",
|
||||
"invariant": "^2.2.4",
|
||||
"levenary": "^1.1.1",
|
||||
"semver": "^5.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import browserslist from "browserslist";
|
||||
import findSuggestion from "levenary";
|
||||
import invariant from "invariant";
|
||||
import { findSuggestion } from "@babel/helper-validator-option";
|
||||
import browserModulesData from "@babel/compat-data/native-modules";
|
||||
|
||||
import {
|
||||
@ -11,9 +10,11 @@ import {
|
||||
isUnreleasedVersion,
|
||||
getLowestUnreleased,
|
||||
} from "./utils";
|
||||
import { OptionValidator } from "@babel/helper-validator-option";
|
||||
import { browserNameMap } from "./targets";
|
||||
import { TargetNames } from "./options";
|
||||
import type { Target, Targets, InputTargets, Browsers } from "./types";
|
||||
import { name as packageName } from "../package.json";
|
||||
import type { Targets, InputTargets, Browsers, TargetsTuple } from "./types";
|
||||
|
||||
export type { Targets, InputTargets };
|
||||
|
||||
@ -22,6 +23,7 @@ export { getInclusionReasons } from "./debug";
|
||||
export { default as filterItems, isRequired } from "./filter-items";
|
||||
export { unreleasedLabels } from "./targets";
|
||||
|
||||
const v = new OptionValidator(packageName);
|
||||
const browserslistDefaults = browserslist.defaults;
|
||||
|
||||
const validBrowserslistTargets = [
|
||||
@ -39,19 +41,18 @@ function objectToBrowserslist(object: Targets): Array<string> {
|
||||
}, []);
|
||||
}
|
||||
|
||||
function validateTargetNames(targets: InputTargets): Targets {
|
||||
function validateTargetNames(targets: Targets): TargetsTuple {
|
||||
const validTargets = Object.keys(TargetNames);
|
||||
for (const target in targets) {
|
||||
if (!TargetNames[target]) {
|
||||
for (const target of Object.keys(targets)) {
|
||||
if (!(target in TargetNames)) {
|
||||
throw new Error(
|
||||
`Invalid Option: '${target}' is not a valid target
|
||||
Maybe you meant to use '${findSuggestion(target, validTargets)}'?`,
|
||||
v.formatMessage(`'${target}' is not a valid target
|
||||
- Did you mean '${findSuggestion(target, validTargets)}'?`),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// $FlowIgnore
|
||||
return targets;
|
||||
return (targets: any);
|
||||
}
|
||||
|
||||
export function isBrowsersQueryValid(browsers: Browsers | Targets): boolean {
|
||||
@ -59,9 +60,9 @@ export function isBrowsersQueryValid(browsers: Browsers | Targets): boolean {
|
||||
}
|
||||
|
||||
function validateBrowsers(browsers: Browsers | void) {
|
||||
invariant(
|
||||
typeof browsers === "undefined" || isBrowsersQueryValid(browsers),
|
||||
`Invalid Option: '${String(browsers)}' is not a valid browserslist query`,
|
||||
v.invariant(
|
||||
browsers === undefined || isBrowsersQueryValid(browsers),
|
||||
`'${String(browsers)}' is not a valid browserslist query`,
|
||||
);
|
||||
|
||||
return browsers;
|
||||
@ -110,8 +111,10 @@ function getLowestVersions(browsers: Array<string>): Targets {
|
||||
}, {});
|
||||
}
|
||||
|
||||
function outputDecimalWarning(decimalTargets: Array<Object>): void {
|
||||
if (!decimalTargets?.length) {
|
||||
function outputDecimalWarning(
|
||||
decimalTargets: Array<{| target: string, value: string |}>,
|
||||
): void {
|
||||
if (!decimalTargets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -133,7 +136,9 @@ function semverifyTarget(target, value) {
|
||||
return semverify(value);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Invalid Option: '${value}' is not a valid value for 'targets.${target}'.`,
|
||||
v.formatMessage(
|
||||
`'${value}' is not a valid value for 'targets.${target}'.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -156,16 +161,17 @@ const targetParserMap = {
|
||||
},
|
||||
};
|
||||
|
||||
type ParsedResult = {
|
||||
targets: Targets,
|
||||
decimalWarnings: Array<Object>,
|
||||
};
|
||||
function generateTargets(inputTargets: InputTargets): Targets {
|
||||
const input = { ...inputTargets };
|
||||
delete input.esmodules;
|
||||
delete input.browsers;
|
||||
return ((input: any): Targets);
|
||||
}
|
||||
|
||||
export default function getTargets(
|
||||
inputTargets: InputTargets = {},
|
||||
options: Object = {},
|
||||
): Targets {
|
||||
const targetOpts: Targets = {};
|
||||
let { browsers } = inputTargets;
|
||||
|
||||
// `esmodules` as a target indicates the specific set of browsers supporting ES Modules.
|
||||
@ -180,12 +186,8 @@ export default function getTargets(
|
||||
// Parse browsers target via browserslist
|
||||
const browsersquery = validateBrowsers(browsers);
|
||||
|
||||
// Remove esmodules after being consumed to fix `hasTargets` below
|
||||
const input = { ...inputTargets };
|
||||
delete input.esmodules;
|
||||
delete input.browsers;
|
||||
|
||||
let targets: Targets = validateTargetNames(input);
|
||||
const input = generateTargets(inputTargets);
|
||||
let targets: TargetsTuple = validateTargetNames(input);
|
||||
|
||||
const shouldParseBrowsers = !!browsersquery;
|
||||
const hasTargets = shouldParseBrowsers || Object.keys(targets).length > 0;
|
||||
@ -218,34 +220,28 @@ export default function getTargets(
|
||||
}
|
||||
|
||||
// Parse remaining targets
|
||||
const parsed = (Object.keys(targets): Array<Target>).sort().reduce(
|
||||
(results: ParsedResult, target: $Keys<Targets>): ParsedResult => {
|
||||
const value = targets[target];
|
||||
const result: Targets = {};
|
||||
const decimalWarnings = [];
|
||||
for (const target of Object.keys(targets).sort()) {
|
||||
const value = targets[target];
|
||||
|
||||
// Warn when specifying minor/patch as a decimal
|
||||
if (typeof value === "number" && value % 1 !== 0) {
|
||||
results.decimalWarnings.push({ target, value });
|
||||
}
|
||||
// Warn when specifying minor/patch as a decimal
|
||||
if (typeof value === "number" && value % 1 !== 0) {
|
||||
decimalWarnings.push({ target, value });
|
||||
}
|
||||
|
||||
// Check if we have a target parser?
|
||||
// $FlowIgnore - Flow doesn't like that some targetParserMap[target] might be missing
|
||||
const parser = targetParserMap[target] ?? targetParserMap.__default;
|
||||
const [parsedTarget, parsedValue] = parser(target, value);
|
||||
// Check if we have a target parser?
|
||||
// $FlowIgnore - Flow doesn't like that some targetParserMap[target] might be missing
|
||||
const parser = targetParserMap[target] ?? targetParserMap.__default;
|
||||
const [parsedTarget, parsedValue] = parser(target, value);
|
||||
|
||||
if (parsedValue) {
|
||||
// Merge (lowest wins)
|
||||
results.targets[parsedTarget] = parsedValue;
|
||||
}
|
||||
if (parsedValue) {
|
||||
// Merge (lowest wins)
|
||||
result[parsedTarget] = parsedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
{
|
||||
targets: targetOpts,
|
||||
decimalWarnings: [],
|
||||
},
|
||||
);
|
||||
outputDecimalWarning(decimalWarnings);
|
||||
|
||||
outputDecimalWarning(parsed.decimalWarnings);
|
||||
|
||||
return parsed.targets;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -18,6 +18,10 @@ export type Targets = {
|
||||
[target: Target]: string,
|
||||
};
|
||||
|
||||
export type TargetsTuple = {|
|
||||
[target: Target]: string,
|
||||
|};
|
||||
|
||||
export type Browsers = string | Array<string>;
|
||||
|
||||
export type InputTargets = {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
// @flow
|
||||
|
||||
import invariant from "invariant";
|
||||
import semver from "semver";
|
||||
|
||||
import { OptionValidator } from "@babel/helper-validator-option";
|
||||
import { name as packageName } from "../package.json";
|
||||
import { unreleasedLabels } from "./targets";
|
||||
import type { Target, Targets } from "./types";
|
||||
|
||||
const versionRegExp = /^(\d+|\d+.\d+)$/;
|
||||
|
||||
const v = new OptionValidator(packageName);
|
||||
|
||||
export function semverMin(first: ?string, second: string): string {
|
||||
return first && semver.lt(first, second) ? first : second;
|
||||
}
|
||||
@ -19,7 +20,7 @@ export function semverify(version: number | string): string {
|
||||
return version;
|
||||
}
|
||||
|
||||
invariant(
|
||||
v.invariant(
|
||||
typeof version === "number" ||
|
||||
(typeof version === "string" && versionRegExp.test(version)),
|
||||
`'${version}' is not a valid version`,
|
||||
|
||||
@ -57,3 +57,5 @@ Object {
|
||||
"samsung": "8.2.0",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getTargets exception throws when version is not a semver 1`] = `"@babel/helper-compilation-targets: 'seventy-two' is not a valid value for 'targets.chrome'."`;
|
||||
|
||||
@ -260,4 +260,12 @@ describe("getTargets", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("exception", () => {
|
||||
it("throws when version is not a semver", () => {
|
||||
expect(() =>
|
||||
getTargets({ chrome: "seventy-two" }),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
3
packages/babel-helper-validator-option/.npmignore
Normal file
3
packages/babel-helper-validator-option/.npmignore
Normal file
@ -0,0 +1,3 @@
|
||||
src
|
||||
test
|
||||
*.log
|
||||
19
packages/babel-helper-validator-option/README.md
Normal file
19
packages/babel-helper-validator-option/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# @babel/helper-validator-option
|
||||
|
||||
> Validate plugin/preset options
|
||||
|
||||
See our website [@babel/helper-validator-option](https://babeljs.io/docs/en/next/babel-helper-validator-option.html) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/helper-validator-option
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/helper-validator-option --dev
|
||||
```
|
||||
16
packages/babel-helper-validator-option/package.json
Normal file
16
packages/babel-helper-validator-option/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@babel/helper-validator-option",
|
||||
"version": "7.11.4",
|
||||
"description": "Validate plugin/preset options",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-helper-validator-option"
|
||||
},
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"exports": "./lib/index.js"
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
// @flow
|
||||
|
||||
const { min } = Math;
|
||||
|
||||
// a minimal leven distance implementation
|
||||
// balanced maintenability with code size
|
||||
// It is not blazingly fast but should be okay for Babel user case
|
||||
// where it will be run for at most tens of time on strings
|
||||
// that have less than 20 ASCII characters
|
||||
|
||||
// https://rosettacode.org/wiki/Levenshtein_distance#ES5
|
||||
function levenshtein(a, b) {
|
||||
let t = [],
|
||||
u = [],
|
||||
i,
|
||||
j;
|
||||
const m = a.length,
|
||||
n = b.length;
|
||||
if (!m) {
|
||||
return n;
|
||||
}
|
||||
if (!n) {
|
||||
return m;
|
||||
}
|
||||
for (j = 0; j <= n; j++) {
|
||||
t[j] = j;
|
||||
}
|
||||
for (i = 1; i <= m; i++) {
|
||||
for (u = [i], j = 1; j <= n; j++) {
|
||||
u[j] =
|
||||
a[i - 1] === b[j - 1] ? t[j - 1] : min(t[j - 1], t[j], u[j - 1]) + 1;
|
||||
}
|
||||
t = u;
|
||||
}
|
||||
return u[n];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string `str` and an array of candidates `arr`,
|
||||
* return the first of elements in candidates that has minimal
|
||||
* Levenshtein distance with `str`.
|
||||
* @export
|
||||
* @param {string} str
|
||||
* @param {string[]} arr
|
||||
* @returns {string}
|
||||
*/
|
||||
export function findSuggestion(str: string, arr: string[]): string {
|
||||
const distances = arr.map<number>(el => levenshtein(el, str));
|
||||
return arr[distances.indexOf(min(...distances))];
|
||||
}
|
||||
2
packages/babel-helper-validator-option/src/index.js
Normal file
2
packages/babel-helper-validator-option/src/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { OptionValidator } from "./validator";
|
||||
export { findSuggestion } from "./find-suggestion";
|
||||
81
packages/babel-helper-validator-option/src/validator.js
Normal file
81
packages/babel-helper-validator-option/src/validator.js
Normal file
@ -0,0 +1,81 @@
|
||||
// @flow
|
||||
import { findSuggestion } from "./find-suggestion.js";
|
||||
|
||||
export class OptionValidator {
|
||||
declare descriptor: string;
|
||||
constructor(descriptor: string) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if the given `options` follow the name of keys defined in the `TopLevelOptionShape`
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Object} TopLevelOptionShape
|
||||
* An object with all the valid key names that `options` should be allowed to have
|
||||
* The property values of `TopLevelOptionShape` can be arbitrary
|
||||
* @memberof OptionValidator
|
||||
*/
|
||||
validateTopLevelOptions(options: Object, TopLevelOptionShape: Object): void {
|
||||
const validOptionNames = Object.keys(TopLevelOptionShape);
|
||||
for (const option of Object.keys(options)) {
|
||||
if (!validOptionNames.includes(option)) {
|
||||
throw new Error(
|
||||
this.formatMessage(`'${option}' is not a valid top-level option.
|
||||
- Did you mean '${findSuggestion(option, validOptionNames)}'?`),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: we do not consider rewrite them to high order functions
|
||||
// until we have to support `validateNumberOption`.
|
||||
validateBooleanOption(
|
||||
name: string,
|
||||
value?: boolean,
|
||||
defaultValue?: boolean,
|
||||
): boolean | void {
|
||||
if (value === undefined) {
|
||||
value = defaultValue;
|
||||
} else {
|
||||
this.invariant(
|
||||
typeof value === "boolean",
|
||||
`'${name}' option must be a boolean.`,
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
validateStringOption(
|
||||
name: string,
|
||||
value?: string,
|
||||
defaultValue?: string,
|
||||
): string | void {
|
||||
if (value === undefined) {
|
||||
value = defaultValue;
|
||||
} else {
|
||||
this.invariant(
|
||||
typeof value === "string",
|
||||
`'${name}' option must be a string.`,
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* A helper interface copied from the `invariant` npm package.
|
||||
* It throws given `message` when `condition` is not met
|
||||
*
|
||||
* @param {boolean} condition
|
||||
* @param {string} message
|
||||
* @memberof OptionValidator
|
||||
*/
|
||||
invariant(condition: boolean, message: string): void {
|
||||
if (!condition) {
|
||||
throw new Error(this.formatMessage(message));
|
||||
}
|
||||
}
|
||||
|
||||
formatMessage(message: string): string {
|
||||
return `${this.descriptor}: ${message}`;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import { findSuggestion } from "..";
|
||||
|
||||
describe("findSuggestion", function () {
|
||||
test.each([
|
||||
["cat", ["cow", "dog", "pig"], "cow"],
|
||||
["uglifyjs", [], undefined],
|
||||
])("findSuggestion(%p, %p) returns %p", (str, arr, expected) => {
|
||||
expect(findSuggestion(str, arr)).toBe(expected);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,81 @@
|
||||
import { OptionValidator } from "..";
|
||||
|
||||
describe("OptionValidator", () => {
|
||||
describe("validateTopLevelOptions", () => {
|
||||
let v;
|
||||
beforeAll(() => {
|
||||
v = new OptionValidator("test-descriptor");
|
||||
});
|
||||
it("should throw when option key is not found", () => {
|
||||
expect(() =>
|
||||
v.validateTopLevelOptions(
|
||||
{ unknown: "options" },
|
||||
{ foo: "foo" },
|
||||
"test",
|
||||
),
|
||||
).toThrow();
|
||||
});
|
||||
it("should throw when option key is an own property but not found", () => {
|
||||
expect(() =>
|
||||
v.validateTopLevelOptions(
|
||||
{ hasOwnProperty: "foo" },
|
||||
{
|
||||
foo: "foo",
|
||||
bar: "bar",
|
||||
aLongPropertyKeyToSeeLevenPerformance: "a",
|
||||
},
|
||||
"test",
|
||||
),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
describe("validateBooleanOption", () => {
|
||||
let v;
|
||||
beforeAll(() => {
|
||||
v = new OptionValidator("test-descriptor");
|
||||
});
|
||||
it("`undefined` option returns false", () => {
|
||||
expect(v.validateBooleanOption("test", undefined, false)).toBe(false);
|
||||
});
|
||||
|
||||
it("`false` option returns false", () => {
|
||||
expect(v.validateBooleanOption("test", false, false)).toBe(false);
|
||||
});
|
||||
|
||||
it("`true` option returns true", () => {
|
||||
expect(v.validateBooleanOption("test", true, false)).toBe(true);
|
||||
});
|
||||
|
||||
it("array option is invalid", () => {
|
||||
expect(() => {
|
||||
v.validateBooleanOption("test", [], false);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateStringOption", () => {
|
||||
let v;
|
||||
beforeAll(() => {
|
||||
v = new OptionValidator("test-descriptor");
|
||||
});
|
||||
it("`undefined` option default", () => {
|
||||
expect(v.validateStringOption("test", undefined, "default")).toBe(
|
||||
"default",
|
||||
);
|
||||
});
|
||||
|
||||
it("`value` option returns value", () => {
|
||||
expect(v.validateStringOption("test", "value", "default")).toBe("value");
|
||||
});
|
||||
|
||||
it("no default returns undefined", () => {
|
||||
expect(v.validateStringOption("test", undefined)).toBe(undefined);
|
||||
});
|
||||
|
||||
it("array option is invalid", () => {
|
||||
expect(() => {
|
||||
v.validateStringOption("test", [], "default");
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -19,6 +19,7 @@
|
||||
"@babel/helper-compilation-targets": "workspace:^7.10.4",
|
||||
"@babel/helper-module-imports": "workspace:^7.10.4",
|
||||
"@babel/helper-plugin-utils": "workspace:^7.10.4",
|
||||
"@babel/helper-validator-option": "workspace:^7.11.4",
|
||||
"@babel/plugin-proposal-async-generator-functions": "workspace:^7.10.4",
|
||||
"@babel/plugin-proposal-class-properties": "workspace:^7.10.4",
|
||||
"@babel/plugin-proposal-dynamic-import": "workspace:^7.10.4",
|
||||
@ -80,8 +81,6 @@
|
||||
"@babel/types": "workspace:^7.11.5",
|
||||
"browserslist": "^4.12.0",
|
||||
"core-js-compat": "^3.6.2",
|
||||
"invariant": "^2.2.2",
|
||||
"levenary": "^1.1.1",
|
||||
"semver": "^5.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
// @flow
|
||||
import corejs3Polyfills from "core-js-compat/data";
|
||||
import findSuggestion from "levenary";
|
||||
import invariant from "invariant";
|
||||
import { coerce, SemVer } from "semver";
|
||||
import corejs2Polyfills from "@babel/compat-data/corejs2-built-ins";
|
||||
import { plugins as pluginsList } from "./plugins-compat-data";
|
||||
import moduleTransformations from "./module-transformations";
|
||||
import { TopLevelOptions, ModulesOption, UseBuiltInsOption } from "./options";
|
||||
import { OptionValidator } from "@babel/helper-validator-option";
|
||||
import { defaultWebIncludes } from "./polyfills/corejs2/get-platform-specific-default";
|
||||
import { name as packageName } from "../package.json";
|
||||
|
||||
import type {
|
||||
BuiltInsOption,
|
||||
@ -18,18 +18,7 @@ import type {
|
||||
PluginListOption,
|
||||
} from "./types";
|
||||
|
||||
const validateTopLevelOptions = (options: Options) => {
|
||||
const validOptions = Object.keys(TopLevelOptions);
|
||||
|
||||
for (const option in options) {
|
||||
if (!TopLevelOptions[option]) {
|
||||
throw new Error(
|
||||
`Invalid Option: ${option} is not a valid top-level option.
|
||||
Maybe you meant to use '${findSuggestion(option, validOptions)}'?`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
const v = new OptionValidator(packageName);
|
||||
|
||||
const allPluginsList = Object.keys(pluginsList);
|
||||
|
||||
@ -89,9 +78,9 @@ const expandIncludesAndExcludes = (
|
||||
(p, i) => selectedPlugins[i].length === 0,
|
||||
);
|
||||
|
||||
invariant(
|
||||
v.invariant(
|
||||
invalidRegExpList.length === 0,
|
||||
`Invalid Option: The plugins/built-ins '${invalidRegExpList.join(
|
||||
`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`,
|
||||
@ -109,9 +98,9 @@ export const checkDuplicateIncludeExcludes = (
|
||||
) => {
|
||||
const duplicates = include.filter(opt => exclude.indexOf(opt) >= 0);
|
||||
|
||||
invariant(
|
||||
v.invariant(
|
||||
duplicates.length === 0,
|
||||
`Invalid Option: The plugins/built-ins '${duplicates.join(
|
||||
`The plugins/built-ins '${duplicates.join(
|
||||
", ",
|
||||
)}' were found in both the "include" and
|
||||
"exclude" options.`,
|
||||
@ -126,61 +115,12 @@ const normalizeTargets = targets => {
|
||||
return { ...targets };
|
||||
};
|
||||
|
||||
export const validateConfigPathOption = (
|
||||
configPath: string = process.cwd(),
|
||||
) => {
|
||||
invariant(
|
||||
typeof configPath === "string",
|
||||
`Invalid Option: The configPath option '${configPath}' is invalid, only strings are allowed.`,
|
||||
);
|
||||
return configPath;
|
||||
};
|
||||
|
||||
export const validateBoolOption = (
|
||||
name: string,
|
||||
value?: boolean,
|
||||
defaultValue: boolean,
|
||||
) => {
|
||||
if (typeof value === "undefined") {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
if (typeof value !== "boolean") {
|
||||
throw new Error(`Preset env: '${name}' option must be a boolean.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const validateStringOption = (
|
||||
name: string,
|
||||
value?: string,
|
||||
defaultValue?: string,
|
||||
) => {
|
||||
if (typeof value === "undefined") {
|
||||
value = defaultValue;
|
||||
} else if (typeof value !== "string") {
|
||||
throw new Error(`Preset env: '${name}' option must be a string.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export const validateIgnoreBrowserslistConfig = (
|
||||
ignoreBrowserslistConfig: boolean,
|
||||
) =>
|
||||
validateBoolOption(
|
||||
TopLevelOptions.ignoreBrowserslistConfig,
|
||||
ignoreBrowserslistConfig,
|
||||
false,
|
||||
);
|
||||
|
||||
export const validateModulesOption = (
|
||||
modulesOpt: ModuleOption = ModulesOption.auto,
|
||||
) => {
|
||||
invariant(
|
||||
v.invariant(
|
||||
ModulesOption[modulesOpt.toString()] || modulesOpt === ModulesOption.false,
|
||||
`Invalid Option: The 'modules' option must be one of \n` +
|
||||
`The 'modules' option must be one of \n` +
|
||||
` - 'false' to indicate no module processing\n` +
|
||||
` - a specific module type: 'commonjs', 'amd', 'umd', 'systemjs'` +
|
||||
` - 'auto' (default) which will automatically select 'false' if the current\n` +
|
||||
@ -193,10 +133,10 @@ export const validateModulesOption = (
|
||||
export const validateUseBuiltInsOption = (
|
||||
builtInsOpt: BuiltInsOption = false,
|
||||
) => {
|
||||
invariant(
|
||||
v.invariant(
|
||||
UseBuiltInsOption[builtInsOpt.toString()] ||
|
||||
builtInsOpt === UseBuiltInsOption.false,
|
||||
`Invalid Option: The 'useBuiltIns' option must be either
|
||||
`The 'useBuiltIns' option must be either
|
||||
'false' (default) to indicate no polyfill,
|
||||
'"entry"' to indicate replacing the entry polyfill, or
|
||||
'"usage"' to import only used polyfills per file`,
|
||||
@ -258,7 +198,7 @@ export function normalizeCoreJSOption(
|
||||
}
|
||||
|
||||
export default function normalizeOptions(opts: Options) {
|
||||
validateTopLevelOptions(opts);
|
||||
v.validateTopLevelOptions(opts, TopLevelOptions);
|
||||
|
||||
const useBuiltIns = validateUseBuiltInsOption(opts.useBuiltIns);
|
||||
|
||||
@ -278,38 +218,42 @@ export default function normalizeOptions(opts: Options) {
|
||||
|
||||
checkDuplicateIncludeExcludes(include, exclude);
|
||||
|
||||
const shippedProposals = validateBoolOption(
|
||||
TopLevelOptions.shippedProposals,
|
||||
opts.shippedProposals,
|
||||
false,
|
||||
);
|
||||
|
||||
return {
|
||||
bugfixes: validateBoolOption(
|
||||
bugfixes: v.validateBooleanOption(
|
||||
TopLevelOptions.bugfixes,
|
||||
opts.bugfixes,
|
||||
false,
|
||||
),
|
||||
configPath: validateConfigPathOption(opts.configPath),
|
||||
configPath: v.validateStringOption(
|
||||
TopLevelOptions.configPath,
|
||||
opts.configPath,
|
||||
process.cwd(),
|
||||
),
|
||||
corejs,
|
||||
debug: validateBoolOption(TopLevelOptions.debug, opts.debug, false),
|
||||
debug: v.validateBooleanOption(TopLevelOptions.debug, opts.debug, false),
|
||||
include,
|
||||
exclude,
|
||||
forceAllTransforms: validateBoolOption(
|
||||
forceAllTransforms: v.validateBooleanOption(
|
||||
TopLevelOptions.forceAllTransforms,
|
||||
opts.forceAllTransforms,
|
||||
false,
|
||||
),
|
||||
ignoreBrowserslistConfig: validateIgnoreBrowserslistConfig(
|
||||
ignoreBrowserslistConfig: v.validateBooleanOption(
|
||||
TopLevelOptions.ignoreBrowserslistConfig,
|
||||
opts.ignoreBrowserslistConfig,
|
||||
false,
|
||||
),
|
||||
loose: validateBoolOption(TopLevelOptions.loose, opts.loose, false),
|
||||
loose: v.validateBooleanOption(TopLevelOptions.loose, opts.loose, false),
|
||||
modules: validateModulesOption(opts.modules),
|
||||
shippedProposals,
|
||||
spec: validateBoolOption(TopLevelOptions.spec, opts.spec, false),
|
||||
shippedProposals: v.validateBooleanOption(
|
||||
TopLevelOptions.shippedProposals,
|
||||
opts.shippedProposals,
|
||||
false,
|
||||
),
|
||||
spec: v.validateBooleanOption(TopLevelOptions.spec, opts.spec, false),
|
||||
targets: normalizeTargets(opts.targets),
|
||||
useBuiltIns: useBuiltIns,
|
||||
browserslistEnv: validateStringOption(
|
||||
browserslistEnv: v.validateStringOption(
|
||||
TopLevelOptions.browserslistEnv,
|
||||
opts.browserslistEnv,
|
||||
),
|
||||
|
||||
@ -4,8 +4,6 @@ const normalizeOptions = require("../lib/normalize-options.js");
|
||||
|
||||
const {
|
||||
checkDuplicateIncludeExcludes,
|
||||
validateBoolOption,
|
||||
validateStringOption,
|
||||
validateModulesOption,
|
||||
validateUseBuiltInsOption,
|
||||
normalizePluginName,
|
||||
@ -172,48 +170,6 @@ describe("normalize-options", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateBoolOption", () => {
|
||||
it("`undefined` option returns false", () => {
|
||||
expect(validateBoolOption("test", undefined, false)).toBe(false);
|
||||
});
|
||||
|
||||
it("`false` option returns false", () => {
|
||||
expect(validateBoolOption("test", false, false)).toBe(false);
|
||||
});
|
||||
|
||||
it("`true` option returns true", () => {
|
||||
expect(validateBoolOption("test", true, false)).toBe(true);
|
||||
});
|
||||
|
||||
it("array option is invalid", () => {
|
||||
expect(() => {
|
||||
validateBoolOption("test", [], false);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateStringOption", () => {
|
||||
it("`undefined` option default", () => {
|
||||
expect(validateStringOption("test", undefined, "default")).toBe(
|
||||
"default",
|
||||
);
|
||||
});
|
||||
|
||||
it("`value` option returns value", () => {
|
||||
expect(validateStringOption("test", "value", "default")).toBe("value");
|
||||
});
|
||||
|
||||
it("no default returns undefined", () => {
|
||||
expect(validateStringOption("test", undefined)).toBe(undefined);
|
||||
});
|
||||
|
||||
it("array option is invalid", () => {
|
||||
expect(() => {
|
||||
validateStringOption("test", [], "default");
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("checkDuplicateIncludeExcludes", function () {
|
||||
it("should throw if duplicate names in both", function () {
|
||||
expect(() => {
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@ -358,9 +358,8 @@ __metadata:
|
||||
"@babel/compat-data": "workspace:^7.10.4"
|
||||
"@babel/core": "workspace:^7.10.4"
|
||||
"@babel/helper-plugin-test-runner": "workspace:^7.10.4"
|
||||
"@babel/helper-validator-option": "workspace:^7.11.4"
|
||||
browserslist: ^4.12.0
|
||||
invariant: ^2.2.4
|
||||
levenary: ^1.1.1
|
||||
semver: ^5.5.0
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0
|
||||
@ -794,6 +793,12 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@babel/helper-validator-option@workspace:^7.11.4, @babel/helper-validator-option@workspace:packages/babel-helper-validator-option":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@babel/helper-validator-option@workspace:packages/babel-helper-validator-option"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@babel/helper-wrap-function@npm:^7.10.4":
|
||||
version: 7.10.4
|
||||
resolution: "@babel/helper-wrap-function@npm:7.10.4"
|
||||
@ -2957,6 +2962,7 @@ __metadata:
|
||||
"@babel/helper-module-imports": "workspace:^7.10.4"
|
||||
"@babel/helper-plugin-test-runner": "workspace:^7.10.4"
|
||||
"@babel/helper-plugin-utils": "workspace:^7.10.4"
|
||||
"@babel/helper-validator-option": "workspace:^7.11.4"
|
||||
"@babel/plugin-proposal-async-generator-functions": "workspace:^7.10.4"
|
||||
"@babel/plugin-proposal-class-properties": "workspace:^7.10.4"
|
||||
"@babel/plugin-proposal-dynamic-import": "workspace:^7.10.4"
|
||||
@ -3018,8 +3024,6 @@ __metadata:
|
||||
"@babel/types": "workspace:^7.11.5"
|
||||
browserslist: ^4.12.0
|
||||
core-js-compat: ^3.6.2
|
||||
invariant: ^2.2.2
|
||||
levenary: ^1.1.1
|
||||
semver: ^5.5.0
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0-0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user