diff --git a/babel.config.js b/babel.config.js index f6fb35028752..b55690d087b8 100644 --- a/babel.config.js +++ b/babel.config.js @@ -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 }, diff --git a/packages/babel-helper-compilation-targets/package.json b/packages/babel-helper-compilation-targets/package.json index 929fdba2b42b..6c36213c9dbb 100644 --- a/packages/babel-helper-compilation-targets/package.json +++ b/packages/babel-helper-compilation-targets/package.json @@ -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": { diff --git a/packages/babel-helper-compilation-targets/src/index.js b/packages/babel-helper-compilation-targets/src/index.js index 33a534beb14c..0241bcb90f26 100644 --- a/packages/babel-helper-compilation-targets/src/index.js +++ b/packages/babel-helper-compilation-targets/src/index.js @@ -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 { }, []); } -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): Targets { }, {}); } -function outputDecimalWarning(decimalTargets: Array): 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, -}; +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).sort().reduce( - (results: ParsedResult, target: $Keys): ParsedResult => { - const value = targets[target]; - - // Warn when specifying minor/patch as a decimal - if (typeof value === "number" && value % 1 !== 0) { - results.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); + 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) { + decimalWarnings.push({ target, value }); + } - if (parsedValue) { - // Merge (lowest wins) - results.targets[parsedTarget] = parsedValue; - } + // 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); - return results; - }, - { - targets: targetOpts, - decimalWarnings: [], - }, - ); + if (parsedValue) { + // Merge (lowest wins) + result[parsedTarget] = parsedValue; + } + } - outputDecimalWarning(parsed.decimalWarnings); + outputDecimalWarning(decimalWarnings); - return parsed.targets; + return result; } diff --git a/packages/babel-helper-compilation-targets/src/types.js b/packages/babel-helper-compilation-targets/src/types.js index 8d488052d6b8..9e0bc3a06f5e 100644 --- a/packages/babel-helper-compilation-targets/src/types.js +++ b/packages/babel-helper-compilation-targets/src/types.js @@ -18,6 +18,10 @@ export type Targets = { [target: Target]: string, }; +export type TargetsTuple = {| + [target: Target]: string, +|}; + export type Browsers = string | Array; export type InputTargets = { diff --git a/packages/babel-helper-compilation-targets/src/utils.js b/packages/babel-helper-compilation-targets/src/utils.js index 31eeae8c8e23..96c607531869 100644 --- a/packages/babel-helper-compilation-targets/src/utils.js +++ b/packages/babel-helper-compilation-targets/src/utils.js @@ -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`, diff --git a/packages/babel-helper-compilation-targets/test/__snapshots__/targets-parser.spec.js.snap b/packages/babel-helper-compilation-targets/test/__snapshots__/targets-parser.spec.js.snap index 5c3fed386ddf..9b49cb2af4b6 100644 --- a/packages/babel-helper-compilation-targets/test/__snapshots__/targets-parser.spec.js.snap +++ b/packages/babel-helper-compilation-targets/test/__snapshots__/targets-parser.spec.js.snap @@ -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'."`; diff --git a/packages/babel-helper-compilation-targets/test/targets-parser.spec.js b/packages/babel-helper-compilation-targets/test/targets-parser.spec.js index e5222b68cec8..562ee2fc3b87 100644 --- a/packages/babel-helper-compilation-targets/test/targets-parser.spec.js +++ b/packages/babel-helper-compilation-targets/test/targets-parser.spec.js @@ -260,4 +260,12 @@ describe("getTargets", () => { }); }); }); + + describe("exception", () => { + it("throws when version is not a semver", () => { + expect(() => + getTargets({ chrome: "seventy-two" }), + ).toThrowErrorMatchingSnapshot(); + }); + }); }); diff --git a/packages/babel-helper-validator-option/.npmignore b/packages/babel-helper-validator-option/.npmignore new file mode 100644 index 000000000000..f9806945836e --- /dev/null +++ b/packages/babel-helper-validator-option/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-helper-validator-option/README.md b/packages/babel-helper-validator-option/README.md new file mode 100644 index 000000000000..b222f83a5c21 --- /dev/null +++ b/packages/babel-helper-validator-option/README.md @@ -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 +``` diff --git a/packages/babel-helper-validator-option/package.json b/packages/babel-helper-validator-option/package.json new file mode 100644 index 000000000000..8ec8180d1f6c --- /dev/null +++ b/packages/babel-helper-validator-option/package.json @@ -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" +} diff --git a/packages/babel-helper-validator-option/src/find-suggestion.js b/packages/babel-helper-validator-option/src/find-suggestion.js new file mode 100644 index 000000000000..57f14fc4a298 --- /dev/null +++ b/packages/babel-helper-validator-option/src/find-suggestion.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(el => levenshtein(el, str)); + return arr[distances.indexOf(min(...distances))]; +} diff --git a/packages/babel-helper-validator-option/src/index.js b/packages/babel-helper-validator-option/src/index.js new file mode 100644 index 000000000000..e0326669962e --- /dev/null +++ b/packages/babel-helper-validator-option/src/index.js @@ -0,0 +1,2 @@ +export { OptionValidator } from "./validator"; +export { findSuggestion } from "./find-suggestion"; diff --git a/packages/babel-helper-validator-option/src/validator.js b/packages/babel-helper-validator-option/src/validator.js new file mode 100644 index 000000000000..d899023c5dab --- /dev/null +++ b/packages/babel-helper-validator-option/src/validator.js @@ -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}`; + } +} diff --git a/packages/babel-helper-validator-option/test/find-suggestion.spec.js b/packages/babel-helper-validator-option/test/find-suggestion.spec.js new file mode 100644 index 000000000000..db6a20cc2833 --- /dev/null +++ b/packages/babel-helper-validator-option/test/find-suggestion.spec.js @@ -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); + }); +}); diff --git a/packages/babel-helper-validator-option/test/validator.spec.js b/packages/babel-helper-validator-option/test/validator.spec.js new file mode 100644 index 000000000000..dc27b524d27b --- /dev/null +++ b/packages/babel-helper-validator-option/test/validator.spec.js @@ -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(); + }); + }); +}); diff --git a/packages/babel-preset-env/package.json b/packages/babel-preset-env/package.json index f42391aa6de6..b52945faa78a 100644 --- a/packages/babel-preset-env/package.json +++ b/packages/babel-preset-env/package.json @@ -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": { diff --git a/packages/babel-preset-env/src/normalize-options.js b/packages/babel-preset-env/src/normalize-options.js index bef9fe27677e..84ebf85300d0 100644 --- a/packages/babel-preset-env/src/normalize-options.js +++ b/packages/babel-preset-env/src/normalize-options.js @@ -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, ), diff --git a/packages/babel-preset-env/test/normalize-options.spec.js b/packages/babel-preset-env/test/normalize-options.spec.js index be301d202cab..948c107cee70 100644 --- a/packages/babel-preset-env/test/normalize-options.spec.js +++ b/packages/babel-preset-env/test/normalize-options.spec.js @@ -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(() => { diff --git a/yarn.lock b/yarn.lock index e925d7a2429b..1564f0e910f8 100644 --- a/yarn.lock +++ b/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