Add targets and browserslist* options to @babel/core (#12189)
This commit is contained in:
parent
31ca15ef58
commit
cb404e4776
@ -39,13 +39,16 @@
|
|||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"./lib/config/files/index.js": "./lib/config/files/index-browser.js",
|
"./lib/config/files/index.js": "./lib/config/files/index-browser.js",
|
||||||
|
"./lib/config/resolve-targets.js": "./lib/config/resolve-targets-browser.js",
|
||||||
"./lib/transform-file.js": "./lib/transform-file-browser.js",
|
"./lib/transform-file.js": "./lib/transform-file-browser.js",
|
||||||
"./src/config/files/index.js": "./src/config/files/index-browser.js",
|
"./src/config/files/index.js": "./src/config/files/index-browser.js",
|
||||||
|
"./src/config/resolve-targets.js": "./src/config/resolve-targets-browser.js",
|
||||||
"./src/transform-file.js": "./src/transform-file-browser.js"
|
"./src/transform-file.js": "./src/transform-file-browser.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "workspace:^7.12.13",
|
"@babel/code-frame": "workspace:^7.12.13",
|
||||||
"@babel/generator": "workspace:^7.12.17",
|
"@babel/generator": "workspace:^7.12.17",
|
||||||
|
"@babel/helper-compilation-targets": "workspace:^7.12.17",
|
||||||
"@babel/helper-module-transforms": "workspace:^7.12.17",
|
"@babel/helper-module-transforms": "workspace:^7.12.17",
|
||||||
"@babel/helpers": "workspace:^7.12.17",
|
"@babel/helpers": "workspace:^7.12.17",
|
||||||
"@babel/parser": "workspace:^7.12.17",
|
"@babel/parser": "workspace:^7.12.17",
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
makeWeakCacheSync,
|
makeWeakCacheSync,
|
||||||
type CacheConfigurator,
|
type CacheConfigurator,
|
||||||
} from "../caching";
|
} from "../caching";
|
||||||
import makeAPI, { type PluginAPI } from "../helpers/config-api";
|
import { makeConfigAPI, type ConfigAPI } from "../helpers/config-api";
|
||||||
import { makeStaticFileCache } from "./utils";
|
import { makeStaticFileCache } from "./utils";
|
||||||
import loadCjsOrMjsDefault from "./module-types";
|
import loadCjsOrMjsDefault from "./module-types";
|
||||||
import pathPatternToRegex from "../pattern-to-regex";
|
import pathPatternToRegex from "../pattern-to-regex";
|
||||||
@ -203,7 +203,7 @@ const readConfigJS = makeStrongCache(function* readConfigJS(
|
|||||||
let assertCache = false;
|
let assertCache = false;
|
||||||
if (typeof options === "function") {
|
if (typeof options === "function") {
|
||||||
yield* []; // if we want to make it possible to use async configs
|
yield* []; // if we want to make it possible to use async configs
|
||||||
options = ((options: any): (api: PluginAPI) => {})(makeAPI(cache));
|
options = ((options: any): (api: ConfigAPI) => {})(makeConfigAPI(cache));
|
||||||
|
|
||||||
assertCache = true;
|
assertCache = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
type PresetInstance,
|
type PresetInstance,
|
||||||
} from "./config-chain";
|
} from "./config-chain";
|
||||||
import type { UnloadedDescriptor } from "./config-descriptors";
|
import type { UnloadedDescriptor } from "./config-descriptors";
|
||||||
|
import type { Targets } from "@babel/helper-compilation-targets";
|
||||||
import traverse from "@babel/traverse";
|
import traverse from "@babel/traverse";
|
||||||
import {
|
import {
|
||||||
makeWeakCache,
|
makeWeakCache,
|
||||||
@ -27,7 +28,7 @@ import {
|
|||||||
type PluginItem,
|
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 { makePluginAPI } from "./helpers/config-api";
|
||||||
|
|
||||||
import loadPrivatePartialConfig from "./partial";
|
import loadPrivatePartialConfig from "./partial";
|
||||||
import type { ValidatedOptions } from "./validation/options";
|
import type { ValidatedOptions } from "./validation/options";
|
||||||
@ -39,6 +40,11 @@ type LoadedDescriptor = {
|
|||||||
alias: string,
|
alias: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PluginContext = {
|
||||||
|
...ConfigContext,
|
||||||
|
targets: Targets,
|
||||||
|
};
|
||||||
|
|
||||||
export type { InputOptions } from "./validation/options";
|
export type { InputOptions } from "./validation/options";
|
||||||
|
|
||||||
export type ResolvedConfig = {
|
export type ResolvedConfig = {
|
||||||
@ -55,6 +61,7 @@ export type PluginPasses = Array<PluginPassList>;
|
|||||||
type SimpleContext = {
|
type SimpleContext = {
|
||||||
envName: string,
|
envName: string,
|
||||||
caller: CallerMetadata | void,
|
caller: CallerMetadata | void,
|
||||||
|
targets: Targets,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
||||||
@ -78,6 +85,11 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
|||||||
throw new Error("Assertion failure - plugins and presets exist");
|
throw new Error("Assertion failure - plugins and presets exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pluginContext: PluginContext = {
|
||||||
|
...context,
|
||||||
|
targets: options.targets,
|
||||||
|
};
|
||||||
|
|
||||||
const toDescriptor = (item: PluginItem) => {
|
const toDescriptor = (item: PluginItem) => {
|
||||||
const desc = getItemDescriptor(item);
|
const desc = getItemDescriptor(item);
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
@ -112,12 +124,12 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
|||||||
// in the previous pass.
|
// in the previous pass.
|
||||||
if (descriptor.ownPass) {
|
if (descriptor.ownPass) {
|
||||||
presets.push({
|
presets.push({
|
||||||
preset: yield* loadPresetDescriptor(descriptor, context),
|
preset: yield* loadPresetDescriptor(descriptor, pluginContext),
|
||||||
pass: [],
|
pass: [],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
presets.unshift({
|
presets.unshift({
|
||||||
preset: yield* loadPresetDescriptor(descriptor, context),
|
preset: yield* loadPresetDescriptor(descriptor, pluginContext),
|
||||||
pass: pluginDescriptorsPass,
|
pass: pluginDescriptorsPass,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -172,7 +184,7 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
|||||||
const descriptor: UnloadedDescriptor = descs[i];
|
const descriptor: UnloadedDescriptor = descs[i];
|
||||||
if (descriptor.options !== false) {
|
if (descriptor.options !== false) {
|
||||||
try {
|
try {
|
||||||
pass.push(yield* loadPluginDescriptor(descriptor, context));
|
pass.push(yield* loadPluginDescriptor(descriptor, pluginContext));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") {
|
if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") {
|
||||||
// print special message for `plugins: ["@babel/foo", { foo: "option" }]`
|
// print special message for `plugins: ["@babel/foo", { foo: "option" }]`
|
||||||
@ -235,7 +247,7 @@ const loadDescriptor = makeWeakCache(function* (
|
|||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
...context,
|
...context,
|
||||||
...makeAPI(cache),
|
...makePluginAPI(cache),
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
item = yield* factory(api, options, dirname);
|
item = yield* factory(api, options, dirname);
|
||||||
@ -375,7 +387,7 @@ const validatePreset = (
|
|||||||
*/
|
*/
|
||||||
function* loadPresetDescriptor(
|
function* loadPresetDescriptor(
|
||||||
descriptor: UnloadedDescriptor,
|
descriptor: UnloadedDescriptor,
|
||||||
context: ConfigContext,
|
context: PluginContext,
|
||||||
): Handler<ConfigChain | null> {
|
): Handler<ConfigChain | null> {
|
||||||
const preset = instantiatePreset(yield* loadDescriptor(descriptor, context));
|
const preset = instantiatePreset(yield* loadDescriptor(descriptor, context));
|
||||||
validatePreset(preset, context, descriptor);
|
validatePreset(preset, context, descriptor);
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
import type { Targets } from "@babel/helper-compilation-targets";
|
||||||
|
|
||||||
import { version as coreVersion } from "../../";
|
import { version as coreVersion } from "../../";
|
||||||
import {
|
import {
|
||||||
assertSimpleType,
|
assertSimpleType,
|
||||||
@ -20,7 +22,9 @@ type EnvFunction = {
|
|||||||
|
|
||||||
type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType;
|
type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType;
|
||||||
|
|
||||||
export type PluginAPI = {|
|
type TargetsFunction = () => Targets;
|
||||||
|
|
||||||
|
export type ConfigAPI = {|
|
||||||
version: string,
|
version: string,
|
||||||
cache: SimpleCacheConfigurator,
|
cache: SimpleCacheConfigurator,
|
||||||
env: EnvFunction,
|
env: EnvFunction,
|
||||||
@ -29,9 +33,14 @@ export type PluginAPI = {|
|
|||||||
caller?: CallerFactory,
|
caller?: CallerFactory,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export default function makeAPI(
|
export type PluginAPI = {|
|
||||||
cache: CacheConfigurator<{ envName: string, caller: CallerMetadata | void }>,
|
...ConfigAPI,
|
||||||
): PluginAPI {
|
targets: TargetsFunction,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export function makeConfigAPI<
|
||||||
|
SideChannel: { envName: string, caller: CallerMetadata | void },
|
||||||
|
>(cache: CacheConfigurator<SideChannel>): ConfigAPI {
|
||||||
const env: any = value =>
|
const env: any = value =>
|
||||||
cache.using(data => {
|
cache.using(data => {
|
||||||
if (typeof value === "undefined") return data.envName;
|
if (typeof value === "undefined") return data.envName;
|
||||||
@ -61,6 +70,22 @@ export default function makeAPI(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makePluginAPI(
|
||||||
|
cache: CacheConfigurator<{
|
||||||
|
envName: string,
|
||||||
|
caller: CallerMetadata | void,
|
||||||
|
targets: Targets,
|
||||||
|
}>,
|
||||||
|
): PluginAPI {
|
||||||
|
const targets = () =>
|
||||||
|
// We are using JSON.parse/JSON.stringify because it's only possible to cache
|
||||||
|
// primitive values. We can safely stringify the targets object because it
|
||||||
|
// only contains strings as its properties.
|
||||||
|
// Please make the Record and Tuple proposal happen!
|
||||||
|
JSON.parse(cache.using(data => JSON.stringify(data.targets)));
|
||||||
|
return { ...makeConfigAPI(cache), targets };
|
||||||
|
}
|
||||||
|
|
||||||
function assertVersion(range: string | number): void {
|
function assertVersion(range: string | number): void {
|
||||||
if (typeof range === "number") {
|
if (typeof range === "number") {
|
||||||
if (!Number.isInteger(range)) {
|
if (!Number.isInteger(range)) {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { getEnv } from "./helpers/environment";
|
|||||||
import {
|
import {
|
||||||
validate,
|
validate,
|
||||||
type ValidatedOptions,
|
type ValidatedOptions,
|
||||||
|
type NormalizedOptions,
|
||||||
type RootMode,
|
type RootMode,
|
||||||
} from "./validation/options";
|
} from "./validation/options";
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ import {
|
|||||||
type ConfigFile,
|
type ConfigFile,
|
||||||
type IgnoreFile,
|
type IgnoreFile,
|
||||||
} from "./files";
|
} from "./files";
|
||||||
|
import { resolveTargets } from "./resolve-targets";
|
||||||
|
|
||||||
function* resolveRootMode(
|
function* resolveRootMode(
|
||||||
rootDir: string,
|
rootDir: string,
|
||||||
@ -61,7 +63,7 @@ function* resolveRootMode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PrivPartialConfig = {
|
type PrivPartialConfig = {
|
||||||
options: ValidatedOptions,
|
options: NormalizedOptions,
|
||||||
context: ConfigContext,
|
context: ConfigContext,
|
||||||
fileHandling: FileHandling,
|
fileHandling: FileHandling,
|
||||||
ignore: IgnoreFile | void,
|
ignore: IgnoreFile | void,
|
||||||
@ -115,30 +117,36 @@ export default function* loadPrivatePartialConfig(
|
|||||||
const configChain = yield* buildRootChain(args, context);
|
const configChain = yield* buildRootChain(args, context);
|
||||||
if (!configChain) return null;
|
if (!configChain) return null;
|
||||||
|
|
||||||
const options = {};
|
const merged: ValidatedOptions = {};
|
||||||
configChain.options.forEach(opts => {
|
configChain.options.forEach(opts => {
|
||||||
mergeOptions(options, opts);
|
mergeOptions((merged: any), opts);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const options: NormalizedOptions = {
|
||||||
|
...merged,
|
||||||
|
targets: resolveTargets(merged, absoluteRootDir, filename),
|
||||||
|
|
||||||
// Tack the passes onto the object itself so that, if this object is
|
// Tack the passes onto the object itself so that, if this object is
|
||||||
// passed back to Babel a second time, it will be in the right structure
|
// passed back to Babel a second time, it will be in the right structure
|
||||||
// to not change behavior.
|
// to not change behavior.
|
||||||
options.cloneInputAst = cloneInputAst;
|
cloneInputAst,
|
||||||
options.babelrc = false;
|
babelrc: false,
|
||||||
options.configFile = false;
|
configFile: false,
|
||||||
options.passPerPreset = false;
|
browserslistConfigFile: false,
|
||||||
options.envName = context.envName;
|
passPerPreset: false,
|
||||||
options.cwd = context.cwd;
|
envName: context.envName,
|
||||||
options.root = context.root;
|
cwd: context.cwd,
|
||||||
options.filename =
|
root: context.root,
|
||||||
typeof context.filename === "string" ? context.filename : undefined;
|
filename:
|
||||||
|
typeof context.filename === "string" ? context.filename : undefined,
|
||||||
|
|
||||||
options.plugins = configChain.plugins.map(descriptor =>
|
plugins: configChain.plugins.map(descriptor =>
|
||||||
createItemFromDescriptor(descriptor),
|
createItemFromDescriptor(descriptor),
|
||||||
);
|
),
|
||||||
options.presets = configChain.presets.map(descriptor =>
|
presets: configChain.presets.map(descriptor =>
|
||||||
createItemFromDescriptor(descriptor),
|
createItemFromDescriptor(descriptor),
|
||||||
);
|
),
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options,
|
options,
|
||||||
@ -201,7 +209,7 @@ class PartialConfig {
|
|||||||
* These properties are public, so any changes to them should be considered
|
* These properties are public, so any changes to them should be considered
|
||||||
* a breaking change to Babel's API.
|
* a breaking change to Babel's API.
|
||||||
*/
|
*/
|
||||||
options: ValidatedOptions;
|
options: NormalizedOptions;
|
||||||
babelrc: string | void;
|
babelrc: string | void;
|
||||||
babelignore: string | void;
|
babelignore: string | void;
|
||||||
config: string | void;
|
config: string | void;
|
||||||
@ -209,7 +217,7 @@ class PartialConfig {
|
|||||||
files: Set<string>;
|
files: Set<string>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
options: ValidatedOptions,
|
options: NormalizedOptions,
|
||||||
babelrc: string | void,
|
babelrc: string | void,
|
||||||
ignore: string | void,
|
ignore: string | void,
|
||||||
config: string | void,
|
config: string | void,
|
||||||
|
|||||||
26
packages/babel-core/src/config/resolve-targets-browser.js
Normal file
26
packages/babel-core/src/config/resolve-targets-browser.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import type { ValidatedOptions } from "./validation/options";
|
||||||
|
import getTargets, { type Targets } from "@babel/helper-compilation-targets";
|
||||||
|
|
||||||
|
export function resolveTargets(
|
||||||
|
options: ValidatedOptions,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
root: string,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
filename: string | void,
|
||||||
|
): Targets {
|
||||||
|
let { targets } = options;
|
||||||
|
if (typeof targets === "string" || Array.isArray(targets)) {
|
||||||
|
targets = { browsers: targets };
|
||||||
|
}
|
||||||
|
// $FlowIgnore it thinks that targets.esmodules doesn't exist.
|
||||||
|
if (targets && targets.esmodules) {
|
||||||
|
targets = { ...targets, esmodules: "intersect" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return getTargets((targets: any), {
|
||||||
|
ignoreBrowserslistConfig: true,
|
||||||
|
browserslistEnv: options.browserslistEnv,
|
||||||
|
});
|
||||||
|
}
|
||||||
39
packages/babel-core/src/config/resolve-targets.js
Normal file
39
packages/babel-core/src/config/resolve-targets.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import typeof * as browserType from "./resolve-targets-browser";
|
||||||
|
import typeof * as nodeType from "./resolve-targets";
|
||||||
|
|
||||||
|
// Kind of gross, but essentially asserting that the exports of this module are the same as the
|
||||||
|
// exports of index-browser, since this file may be replaced at bundle time with index-browser.
|
||||||
|
((({}: any): $Exact<browserType>): $Exact<nodeType>);
|
||||||
|
|
||||||
|
import type { ValidatedOptions } from "./validation/options";
|
||||||
|
import path from "path";
|
||||||
|
import getTargets, { type Targets } from "@babel/helper-compilation-targets";
|
||||||
|
|
||||||
|
export function resolveTargets(
|
||||||
|
options: ValidatedOptions,
|
||||||
|
root: string,
|
||||||
|
filename: string | void,
|
||||||
|
): Targets {
|
||||||
|
let { targets } = options;
|
||||||
|
if (typeof targets === "string" || Array.isArray(targets)) {
|
||||||
|
targets = { browsers: targets };
|
||||||
|
}
|
||||||
|
// $FlowIgnore it thinks that targets.esmodules doesn't exist.
|
||||||
|
if (targets && targets.esmodules) {
|
||||||
|
targets = { ...targets, esmodules: "intersect" };
|
||||||
|
}
|
||||||
|
|
||||||
|
let configFile;
|
||||||
|
if (typeof options.browserslistConfigFile === "string") {
|
||||||
|
configFile = path.resolve(root, options.browserslistConfigFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getTargets((targets: any), {
|
||||||
|
ignoreBrowserslistConfig: options.browserslistConfigFile === false,
|
||||||
|
configFile,
|
||||||
|
configPath: filename ?? root,
|
||||||
|
browserslistEnv: options.browserslistEnv,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import type { ValidatedOptions } from "./validation/options";
|
import type { ValidatedOptions, NormalizedOptions } from "./validation/options";
|
||||||
|
|
||||||
export function mergeOptions(
|
export function mergeOptions(
|
||||||
target: ValidatedOptions,
|
target: ValidatedOptions,
|
||||||
source: ValidatedOptions,
|
source: ValidatedOptions | NormalizedOptions,
|
||||||
): void {
|
): void {
|
||||||
for (const k of Object.keys(source)) {
|
for (const k of Object.keys(source)) {
|
||||||
if (k === "parserOpts" && source.parserOpts) {
|
if (k === "parserOpts" && source.parserOpts) {
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import {
|
||||||
|
isBrowsersQueryValid,
|
||||||
|
TargetNames,
|
||||||
|
} from "@babel/helper-compilation-targets";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ConfigFileSearch,
|
ConfigFileSearch,
|
||||||
BabelrcSearch,
|
BabelrcSearch,
|
||||||
@ -16,6 +21,7 @@ import type {
|
|||||||
NestingPath,
|
NestingPath,
|
||||||
CallerMetadata,
|
CallerMetadata,
|
||||||
RootMode,
|
RootMode,
|
||||||
|
TargetsListOrObject,
|
||||||
} from "./options";
|
} from "./options";
|
||||||
|
|
||||||
export type { RootPath } from "./options";
|
export type { RootPath } from "./options";
|
||||||
@ -373,3 +379,55 @@ function assertPluginTarget(loc: GeneralPath, value: mixed): PluginTarget {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertTargets(
|
||||||
|
loc: GeneralPath,
|
||||||
|
value: mixed,
|
||||||
|
): TargetsListOrObject {
|
||||||
|
if (isBrowsersQueryValid(value)) return (value: any);
|
||||||
|
|
||||||
|
if (typeof value !== "object" || !value || Array.isArray(value)) {
|
||||||
|
throw new Error(
|
||||||
|
`${msg(loc)} must be a string, an array of strings or an object`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const browsersLoc = access(loc, "browsers");
|
||||||
|
const esmodulesLoc = access(loc, "esmodules");
|
||||||
|
|
||||||
|
assertBrowsersList(browsersLoc, value.browsers);
|
||||||
|
assertBoolean(esmodulesLoc, value.esmodules);
|
||||||
|
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
const val = value[key];
|
||||||
|
const subLoc = access(loc, key);
|
||||||
|
|
||||||
|
if (key === "esmodules") assertBoolean(subLoc, val);
|
||||||
|
else if (key === "browsers") assertBrowsersList(subLoc, val);
|
||||||
|
else if (!Object.hasOwnProperty.call(TargetNames, key)) {
|
||||||
|
const validTargets = Object.keys(TargetNames).join(", ");
|
||||||
|
throw new Error(
|
||||||
|
`${msg(
|
||||||
|
subLoc,
|
||||||
|
)} is not a valid target. Supported targets are ${validTargets}`,
|
||||||
|
);
|
||||||
|
} else assertBrowserVersion(subLoc, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (value: any);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertBrowsersList(loc: GeneralPath, value: mixed) {
|
||||||
|
if (value !== undefined && !isBrowsersQueryValid(value)) {
|
||||||
|
throw new Error(
|
||||||
|
`${msg(loc)} must be undefined, a string or an array of strings`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertBrowserVersion(loc: GeneralPath, value: mixed) {
|
||||||
|
if (typeof value === "number" && Math.round(value) === value) return;
|
||||||
|
if (typeof value === "string") return;
|
||||||
|
|
||||||
|
throw new Error(`${msg(loc)} must be a string or an integer number`);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import type { InputTargets, Targets } from "@babel/helper-compilation-targets";
|
||||||
|
|
||||||
import type { ConfigItem } from "../item";
|
import type { ConfigItem } from "../item";
|
||||||
import Plugin from "../plugin";
|
import Plugin from "../plugin";
|
||||||
|
|
||||||
@ -23,6 +25,7 @@ import {
|
|||||||
assertSourceMaps,
|
assertSourceMaps,
|
||||||
assertCompact,
|
assertCompact,
|
||||||
assertSourceType,
|
assertSourceType,
|
||||||
|
assertTargets,
|
||||||
type ValidatorSet,
|
type ValidatorSet,
|
||||||
type Validator,
|
type Validator,
|
||||||
type OptionPath,
|
type OptionPath,
|
||||||
@ -77,6 +80,16 @@ const NONPRESET_VALIDATORS: ValidatorSet = {
|
|||||||
$PropertyType<ValidatedOptions, "ignore">,
|
$PropertyType<ValidatedOptions, "ignore">,
|
||||||
>),
|
>),
|
||||||
only: (assertIgnoreList: Validator<$PropertyType<ValidatedOptions, "only">>),
|
only: (assertIgnoreList: Validator<$PropertyType<ValidatedOptions, "only">>),
|
||||||
|
|
||||||
|
targets: (assertTargets: Validator<
|
||||||
|
$PropertyType<ValidatedOptions, "targets">,
|
||||||
|
>),
|
||||||
|
browserslistConfigFile: (assertConfigFileSearch: Validator<
|
||||||
|
$PropertyType<ValidatedOptions, "browserslistConfigFile">,
|
||||||
|
>),
|
||||||
|
browserslistEnv: (assertString: Validator<
|
||||||
|
$PropertyType<ValidatedOptions, "browserslistEnv">,
|
||||||
|
>),
|
||||||
};
|
};
|
||||||
|
|
||||||
const COMMON_VALIDATORS: ValidatorSet = {
|
const COMMON_VALIDATORS: ValidatorSet = {
|
||||||
@ -208,6 +221,11 @@ export type ValidatedOptions = {
|
|||||||
plugins?: PluginList,
|
plugins?: PluginList,
|
||||||
passPerPreset?: boolean,
|
passPerPreset?: boolean,
|
||||||
|
|
||||||
|
// browserslists-related options
|
||||||
|
targets?: TargetsListOrObject,
|
||||||
|
browserslistConfigFile?: ConfigFileSearch,
|
||||||
|
browserslistEnv?: string,
|
||||||
|
|
||||||
// Options for @babel/generator
|
// Options for @babel/generator
|
||||||
retainLines?: boolean,
|
retainLines?: boolean,
|
||||||
comments?: boolean,
|
comments?: boolean,
|
||||||
@ -241,6 +259,11 @@ export type ValidatedOptions = {
|
|||||||
generatorOpts?: {},
|
generatorOpts?: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NormalizedOptions = {
|
||||||
|
...$Diff<ValidatedOptions, { targets: any }>,
|
||||||
|
+targets: Targets,
|
||||||
|
};
|
||||||
|
|
||||||
export type CallerMetadata = {
|
export type CallerMetadata = {
|
||||||
// If 'caller' is specified, require that the name is given for debugging
|
// If 'caller' is specified, require that the name is given for debugging
|
||||||
// messages.
|
// messages.
|
||||||
@ -273,6 +296,11 @@ export type CompactOption = boolean | "auto";
|
|||||||
export type RootInputSourceMapOption = {} | boolean;
|
export type RootInputSourceMapOption = {} | boolean;
|
||||||
export type RootMode = "root" | "upward" | "upward-optional";
|
export type RootMode = "root" | "upward" | "upward-optional";
|
||||||
|
|
||||||
|
export type TargetsListOrObject =
|
||||||
|
| Targets
|
||||||
|
| InputTargets
|
||||||
|
| $PropertyType<InputTargets, "browsers">;
|
||||||
|
|
||||||
export type OptionsSource =
|
export type OptionsSource =
|
||||||
| "arguments"
|
| "arguments"
|
||||||
| "configfile"
|
| "configfile"
|
||||||
|
|||||||
@ -976,6 +976,7 @@ describe("buildConfigChain", function () {
|
|||||||
const getDefaults = () => ({
|
const getDefaults = () => ({
|
||||||
babelrc: false,
|
babelrc: false,
|
||||||
configFile: false,
|
configFile: false,
|
||||||
|
browserslistConfigFile: false,
|
||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
root: process.cwd(),
|
root: process.cwd(),
|
||||||
envName: "development",
|
envName: "development",
|
||||||
@ -983,6 +984,7 @@ describe("buildConfigChain", function () {
|
|||||||
plugins: [],
|
plugins: [],
|
||||||
presets: [],
|
presets: [],
|
||||||
cloneInputAst: true,
|
cloneInputAst: true,
|
||||||
|
targets: {},
|
||||||
});
|
});
|
||||||
const realEnv = process.env.NODE_ENV;
|
const realEnv = process.env.NODE_ENV;
|
||||||
const realBabelEnv = process.env.BABEL_ENV;
|
const realBabelEnv = process.env.BABEL_ENV;
|
||||||
|
|||||||
1
packages/babel-core/test/fixtures/plugins/targets/plugin/input.js
vendored
Normal file
1
packages/babel-core/test/fixtures/plugins/targets/plugin/input.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
;
|
||||||
4
packages/babel-core/test/fixtures/plugins/targets/plugin/options.json
vendored
Normal file
4
packages/babel-core/test/fixtures/plugins/targets/plugin/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"targets": ["firefox 64", "node 8"],
|
||||||
|
"plugins": ["./plugin"]
|
||||||
|
}
|
||||||
2
packages/babel-core/test/fixtures/plugins/targets/plugin/output.js
vendored
Normal file
2
packages/babel-core/test/fixtures/plugins/targets/plugin/output.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
;
|
||||||
|
"plugin: {\"firefox\":\"64.0.0\",\"node\":\"8.17.0\"}"
|
||||||
14
packages/babel-core/test/fixtures/plugins/targets/plugin/plugin.js
vendored
Normal file
14
packages/babel-core/test/fixtures/plugins/targets/plugin/plugin.js
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module.exports = function (api) {
|
||||||
|
const { types: t } = api;
|
||||||
|
|
||||||
|
const targets = api.targets();
|
||||||
|
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
Program(path) {
|
||||||
|
const output = t.stringLiteral(`plugin: ${JSON.stringify(targets)}`);
|
||||||
|
path.pushContainer("body", output);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
1
packages/babel-core/test/fixtures/plugins/targets/preset/input.js
vendored
Normal file
1
packages/babel-core/test/fixtures/plugins/targets/preset/input.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
;
|
||||||
4
packages/babel-core/test/fixtures/plugins/targets/preset/options.json
vendored
Normal file
4
packages/babel-core/test/fixtures/plugins/targets/preset/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"targets": ["firefox 64", "node 8"],
|
||||||
|
"presets": ["./preset"]
|
||||||
|
}
|
||||||
2
packages/babel-core/test/fixtures/plugins/targets/preset/output.js
vendored
Normal file
2
packages/babel-core/test/fixtures/plugins/targets/preset/output.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
;
|
||||||
|
"preset: {\"firefox\":\"64.0.0\",\"node\":\"8.17.0\"}"
|
||||||
18
packages/babel-core/test/fixtures/plugins/targets/preset/preset.js
vendored
Normal file
18
packages/babel-core/test/fixtures/plugins/targets/preset/preset.js
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module.exports = function (api) {
|
||||||
|
const targets = api.targets();
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [plugin],
|
||||||
|
};
|
||||||
|
|
||||||
|
function plugin({ types: t }) {
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
Program(path) {
|
||||||
|
const output = t.stringLiteral(`preset: ${JSON.stringify(targets)}`);
|
||||||
|
path.pushContainer("body", output);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
4
packages/babel-core/test/fixtures/targets/.browserslistrc
vendored
Normal file
4
packages/babel-core/test/fixtures/targets/.browserslistrc
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
chrome 80
|
||||||
|
|
||||||
|
[browserslist-loading-test]
|
||||||
|
chrome 70
|
||||||
1
packages/babel-core/test/fixtures/targets/.browserslistrc-firefox
vendored
Normal file
1
packages/babel-core/test/fixtures/targets/.browserslistrc-firefox
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
firefox 74
|
||||||
1
packages/babel-core/test/fixtures/targets/nested/.browserslistrc
vendored
Normal file
1
packages/babel-core/test/fixtures/targets/nested/.browserslistrc
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
edge 14
|
||||||
150
packages/babel-core/test/targets.js
Normal file
150
packages/babel-core/test/targets.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { loadOptions as loadOptionsOrig } from "../lib";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
function loadOptions(opts) {
|
||||||
|
return loadOptionsOrig({ cwd: __dirname, ...opts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function withTargets(targets) {
|
||||||
|
return loadOptions({ targets });
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("targets", () => {
|
||||||
|
it("throws if invalid type", () => {
|
||||||
|
expect(() => withTargets(2)).toThrow(
|
||||||
|
".targets must be a string, an array of strings or an object",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets([2])).toThrow(
|
||||||
|
".targets must be a string, an array of strings or an object",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets([{}])).toThrow(
|
||||||
|
".targets must be a string, an array of strings or an object",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets([])).not.toThrow();
|
||||||
|
expect(() => withTargets({})).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws if invalid target", () => {
|
||||||
|
expect(() => withTargets({ uglify: "2.3" })).toThrow(
|
||||||
|
/\.targets\["uglify"\] is not a valid target/,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ foo: "bar" })).toThrow(
|
||||||
|
/\.targets\["foo"\] is not a valid target/,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ firefox: 71 })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws if invalid version", () => {
|
||||||
|
expect(() => withTargets({ node: 10.1 /* or 10.10? */ })).toThrow(
|
||||||
|
`.targets["node"] must be a string or an integer number`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ node: true })).toThrow(
|
||||||
|
`.targets["node"] must be a string or an integer number`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ node: "10.1" })).not.toThrow();
|
||||||
|
|
||||||
|
expect(() => withTargets({ node: "current" })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("esmodules", () => {
|
||||||
|
expect(() => withTargets({ esmodules: "7" })).toThrow(
|
||||||
|
`.targets["esmodules"] must be a boolean, or undefined`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ esmodules: false })).not.toThrow();
|
||||||
|
expect(() => withTargets({ esmodules: true })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("browsers", () => {
|
||||||
|
expect(() => withTargets({ browsers: 2 })).toThrow(
|
||||||
|
`.targets["browsers"] must be undefined, a string or an array of strings`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ browsers: [2] })).toThrow(
|
||||||
|
`.targets["browsers"] must be undefined, a string or an array of strings`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ browsers: {} })).toThrow(
|
||||||
|
`.targets["browsers"] must be undefined, a string or an array of strings`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => withTargets({ browsers: [] })).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("browserslist", () => {
|
||||||
|
it("loads .browserslistrc by default", () => {
|
||||||
|
expect(
|
||||||
|
loadOptions({
|
||||||
|
cwd: join(__dirname, "fixtures", "targets"),
|
||||||
|
}).targets,
|
||||||
|
).toEqual({ chrome: "80.0.0" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads .browserslistrc relative to the input file", () => {
|
||||||
|
expect(
|
||||||
|
loadOptions({
|
||||||
|
cwd: join(__dirname, "fixtures", "targets"),
|
||||||
|
filename: "./nested/test.js",
|
||||||
|
}).targets,
|
||||||
|
).toEqual({ edge: "14.0.0" });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("browserslistConfigFile", () => {
|
||||||
|
it("can disable config loading", () => {
|
||||||
|
expect(
|
||||||
|
loadOptions({
|
||||||
|
cwd: join(__dirname, "fixtures", "targets"),
|
||||||
|
browserslistConfigFile: false,
|
||||||
|
}).targets,
|
||||||
|
).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can specify a custom file", () => {
|
||||||
|
expect(
|
||||||
|
loadOptions({
|
||||||
|
cwd: join(__dirname, "fixtures", "targets"),
|
||||||
|
browserslistConfigFile: "./.browserslistrc-firefox",
|
||||||
|
}).targets,
|
||||||
|
).toEqual({ firefox: "74.0.0" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is relative to the project root", () => {
|
||||||
|
expect(
|
||||||
|
loadOptions({
|
||||||
|
cwd: join(__dirname, "fixtures", "targets"),
|
||||||
|
root: "..",
|
||||||
|
filename: "./nested/test.js",
|
||||||
|
browserslistConfigFile: "./targets/.browserslistrc-firefox",
|
||||||
|
}).targets,
|
||||||
|
).toEqual({ firefox: "74.0.0" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("browserslistEnv", () => {
|
||||||
|
it("is forwarded to browserslist", () => {
|
||||||
|
expect(
|
||||||
|
loadOptions({
|
||||||
|
cwd: join(__dirname, "fixtures", "targets"),
|
||||||
|
browserslistEnv: "browserslist-loading-test",
|
||||||
|
}).targets,
|
||||||
|
).toEqual({ chrome: "70.0.0" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("esmodules and browsers are intersected", () => {
|
||||||
|
expect(
|
||||||
|
withTargets({
|
||||||
|
esmodules: true,
|
||||||
|
browsers: "chrome >= 80, firefox >= 30",
|
||||||
|
}).targets,
|
||||||
|
).toEqual({ chrome: "80.0.0", firefox: "60.0.0" });
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -9,6 +9,7 @@ import {
|
|||||||
semverMin,
|
semverMin,
|
||||||
isUnreleasedVersion,
|
isUnreleasedVersion,
|
||||||
getLowestUnreleased,
|
getLowestUnreleased,
|
||||||
|
getHighestUnreleased,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { OptionValidator } from "@babel/helper-validator-option";
|
import { OptionValidator } from "@babel/helper-validator-option";
|
||||||
import { browserNameMap } from "./targets";
|
import { browserNameMap } from "./targets";
|
||||||
@ -21,9 +22,11 @@ export { prettifyTargets } from "./pretty";
|
|||||||
export { getInclusionReasons } from "./debug";
|
export { getInclusionReasons } from "./debug";
|
||||||
export { default as filterItems, isRequired } from "./filter-items";
|
export { default as filterItems, isRequired } from "./filter-items";
|
||||||
export { unreleasedLabels } from "./targets";
|
export { unreleasedLabels } from "./targets";
|
||||||
|
export { TargetNames };
|
||||||
|
|
||||||
|
const ESM_SUPPORT = browserModulesData["es6.module"];
|
||||||
|
|
||||||
const v = new OptionValidator(PACKAGE_JSON.name);
|
const v = new OptionValidator(PACKAGE_JSON.name);
|
||||||
const browserslistDefaults = browserslist.defaults;
|
|
||||||
|
|
||||||
function validateTargetNames(targets: Targets): TargetsTuple {
|
function validateTargetNames(targets: Targets): TargetsTuple {
|
||||||
const validTargets = Object.keys(TargetNames);
|
const validTargets = Object.keys(TargetNames);
|
||||||
@ -39,8 +42,11 @@ function validateTargetNames(targets: Targets): TargetsTuple {
|
|||||||
return (targets: any);
|
return (targets: any);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBrowsersQueryValid(browsers: Browsers | Targets): boolean {
|
export function isBrowsersQueryValid(browsers: mixed): boolean %checks {
|
||||||
return typeof browsers === "string" || Array.isArray(browsers);
|
return (
|
||||||
|
typeof browsers === "string" ||
|
||||||
|
(Array.isArray(browsers) && browsers.every(b => typeof b === "string"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateBrowsers(browsers: Browsers | void) {
|
function validateBrowsers(browsers: Browsers | void) {
|
||||||
@ -149,55 +155,83 @@ function generateTargets(inputTargets: InputTargets): Targets {
|
|||||||
return ((input: any): Targets);
|
return ((input: any): Targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveTargets(queries: Browsers): Targets {
|
||||||
|
const resolved = browserslist(queries, { mobileToDesktop: true });
|
||||||
|
return getLowestVersions(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetTargetsOption = {
|
||||||
|
// This is not the path of the config file, but the path where start searching it from
|
||||||
|
configPath?: string,
|
||||||
|
|
||||||
|
// The path of the config file
|
||||||
|
configFile?: string,
|
||||||
|
|
||||||
|
// The env to pass to browserslist
|
||||||
|
browserslistEnv?: string,
|
||||||
|
|
||||||
|
// true to disable config loading
|
||||||
|
ignoreBrowserslistConfig?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
export default function getTargets(
|
export default function getTargets(
|
||||||
inputTargets: InputTargets = {},
|
inputTargets: InputTargets = {},
|
||||||
options: Object = {},
|
options: GetTargetsOption = {},
|
||||||
): Targets {
|
): Targets {
|
||||||
let { browsers } = inputTargets;
|
let { browsers, esmodules } = inputTargets;
|
||||||
|
|
||||||
// `esmodules` as a target indicates the specific set of browsers supporting ES Modules.
|
validateBrowsers(browsers);
|
||||||
// These values OVERRIDE the `browsers` field.
|
|
||||||
if (inputTargets.esmodules) {
|
|
||||||
const supportsESModules = browserModulesData["es6.module"];
|
|
||||||
browsers = Object.keys(supportsESModules)
|
|
||||||
.map(browser => `${browser} ${supportsESModules[browser]}`)
|
|
||||||
.join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse browsers target via browserslist
|
|
||||||
const browsersquery = validateBrowsers(browsers);
|
|
||||||
|
|
||||||
const input = generateTargets(inputTargets);
|
const input = generateTargets(inputTargets);
|
||||||
let targets: TargetsTuple = validateTargetNames(input);
|
let targets: TargetsTuple = validateTargetNames(input);
|
||||||
|
|
||||||
const shouldParseBrowsers = !!browsersquery;
|
const shouldParseBrowsers = !!browsers;
|
||||||
const hasTargets = shouldParseBrowsers || Object.keys(targets).length > 0;
|
const hasTargets = shouldParseBrowsers || Object.keys(targets).length > 0;
|
||||||
const shouldSearchForConfig =
|
const shouldSearchForConfig =
|
||||||
!options.ignoreBrowserslistConfig && !hasTargets;
|
!options.ignoreBrowserslistConfig && !hasTargets;
|
||||||
|
|
||||||
if (shouldParseBrowsers || shouldSearchForConfig) {
|
if (!browsers && shouldSearchForConfig) {
|
||||||
|
browsers =
|
||||||
|
browserslist.loadConfig({
|
||||||
|
config: options.configFile,
|
||||||
|
path: options.configPath,
|
||||||
|
env: options.browserslistEnv,
|
||||||
|
}) ??
|
||||||
// If no targets are passed, we need to overwrite browserslist's defaults
|
// If no targets are passed, we need to overwrite browserslist's defaults
|
||||||
// so that we enable all transforms (acting like the now deprecated
|
// so that we enable all transforms (acting like the now deprecated
|
||||||
// preset-latest).
|
// preset-latest).
|
||||||
//
|
[];
|
||||||
// Note, if browserslist resolves the config (ex. package.json), then usage
|
|
||||||
// of `defaults` in queries will be different since we don't want to break
|
|
||||||
// the behavior of "no targets is the same as preset-latest".
|
|
||||||
if (!hasTargets) {
|
|
||||||
browserslist.defaults = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const browsers = browserslist(browsersquery, {
|
// `esmodules` as a target indicates the specific set of browsers supporting ES Modules.
|
||||||
path: options.configPath,
|
// These values OVERRIDE the `browsers` field.
|
||||||
mobileToDesktop: true,
|
if (esmodules && (esmodules !== "intersect" || !browsers)) {
|
||||||
env: options.browserslistEnv,
|
browsers = Object.keys(ESM_SUPPORT)
|
||||||
});
|
.map(browser => `${browser} >= ${ESM_SUPPORT[browser]}`)
|
||||||
|
.join(", ");
|
||||||
|
esmodules = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browsers) {
|
||||||
|
const queryBrowsers = resolveTargets(browsers);
|
||||||
|
|
||||||
|
if (esmodules === "intersect") {
|
||||||
|
for (const browser of Object.keys(queryBrowsers)) {
|
||||||
|
const version = queryBrowsers[browser];
|
||||||
|
|
||||||
|
if (ESM_SUPPORT[browser]) {
|
||||||
|
queryBrowsers[browser] = getHighestUnreleased(
|
||||||
|
version,
|
||||||
|
semverify(ESM_SUPPORT[browser]),
|
||||||
|
browser,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
delete queryBrowsers[browser];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const queryBrowsers = getLowestVersions(browsers);
|
|
||||||
targets = Object.assign(queryBrowsers, targets);
|
targets = Object.assign(queryBrowsers, targets);
|
||||||
|
|
||||||
// Reset browserslist defaults
|
|
||||||
browserslist.defaults = browserslistDefaults;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse remaining targets
|
// Parse remaining targets
|
||||||
|
|||||||
@ -22,11 +22,17 @@ export type TargetsTuple = {|
|
|||||||
[target: Target]: string,
|
[target: Target]: string,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export type Browsers = string | Array<string>;
|
export type Browsers = string | $ReadOnlyArray<string>;
|
||||||
|
|
||||||
export type InputTargets = {
|
export type InputTargets = {
|
||||||
...Targets,
|
...Targets,
|
||||||
|
|
||||||
browsers?: Browsers,
|
browsers?: Browsers,
|
||||||
esmodules?: boolean,
|
|
||||||
|
// When `true`, this completely replaces the `browsers` option.
|
||||||
|
// When `intersect`, this is intersected with the `browsers`
|
||||||
|
// option (giving the higher browsers as the result).
|
||||||
|
// TODO(Babel 8): Make `true` behave like `intersect` and
|
||||||
|
// remove `intersect`.
|
||||||
|
esmodules?: boolean | "intersect",
|
||||||
};
|
};
|
||||||
|
|||||||
@ -51,6 +51,14 @@ export function getLowestUnreleased(a: string, b: string, env: string): string {
|
|||||||
return semverMin(a, b);
|
return semverMin(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getHighestUnreleased(
|
||||||
|
a: string,
|
||||||
|
b: string,
|
||||||
|
env: string,
|
||||||
|
): string {
|
||||||
|
return getLowestUnreleased(a, b, env) === a ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
export function getLowestImplementedVersion(
|
export function getLowestImplementedVersion(
|
||||||
plugin: Targets,
|
plugin: Targets,
|
||||||
environment: Target,
|
environment: Target,
|
||||||
|
|||||||
@ -1,5 +1,26 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`getTargets esmodules can be intersected with a .browserslistrc file 1`] = `
|
||||||
|
Object {
|
||||||
|
"chrome": "70.0.0",
|
||||||
|
"firefox": "60.0.0",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`getTargets esmodules can be intersected with the browsers option 1`] = `
|
||||||
|
Object {
|
||||||
|
"chrome": "70.0.0",
|
||||||
|
"firefox": "60.0.0",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`getTargets esmodules explicit browser versions have the precedence over 'esmodules' 1`] = `
|
||||||
|
Object {
|
||||||
|
"chrome": "20.0.0",
|
||||||
|
"firefox": "70.0.0",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`getTargets esmodules returns browser supporting modules and keyed browser overrides 1`] = `
|
exports[`getTargets esmodules returns browser supporting modules and keyed browser overrides 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"android": "61.0.0",
|
"android": "61.0.0",
|
||||||
|
|||||||
2
packages/babel-helper-compilation-targets/test/fixtures/.browserslistrc
vendored
Normal file
2
packages/babel-helper-compilation-targets/test/fixtures/.browserslistrc
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
chrome >= 70
|
||||||
|
firefox >= 30
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import browserslist from "browserslist";
|
import browserslist from "browserslist";
|
||||||
|
import { join } from "path";
|
||||||
import getTargets from "..";
|
import getTargets from "..";
|
||||||
|
|
||||||
describe("getTargets", () => {
|
describe("getTargets", () => {
|
||||||
@ -233,6 +234,48 @@ describe("getTargets", () => {
|
|||||||
}),
|
}),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("can be intersected with the browsers option", () => {
|
||||||
|
expect(
|
||||||
|
getTargets({
|
||||||
|
esmodules: "intersect",
|
||||||
|
browsers: ["chrome >= 70", "firefox >= 30"],
|
||||||
|
}),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can be intersected with a .browserslistrc file", () => {
|
||||||
|
expect(
|
||||||
|
getTargets(
|
||||||
|
{
|
||||||
|
esmodules: "intersect",
|
||||||
|
},
|
||||||
|
{ configPath: join(__dirname, "fixtures", "foo.js") },
|
||||||
|
),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("explicit browser versions have the precedence over 'esmodules'", () => {
|
||||||
|
expect(
|
||||||
|
getTargets({
|
||||||
|
browsers: "chrome 5, firefox 5",
|
||||||
|
esmodules: "intersect",
|
||||||
|
chrome: 20,
|
||||||
|
firefox: 70,
|
||||||
|
}),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'intersect' behaves like 'true' if no browsers are specified", () => {
|
||||||
|
expect(
|
||||||
|
getTargets(
|
||||||
|
{ esmodules: "intersect" },
|
||||||
|
{ ignoreBrowserslistConfig: true },
|
||||||
|
),
|
||||||
|
).toEqual(
|
||||||
|
getTargets({ esmodules: true }, { ignoreBrowserslistConfig: true }),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("node", () => {
|
describe("node", () => {
|
||||||
|
|||||||
@ -1,19 +1,33 @@
|
|||||||
export function declare(builder) {
|
export function declare(builder) {
|
||||||
return (api, options, dirname) => {
|
return (api, options, dirname) => {
|
||||||
if (!api.assertVersion) {
|
let clonedApi;
|
||||||
// Inject a custom version of 'assertVersion' for Babel 6 and early
|
|
||||||
// versions of Babel 7's beta that didn't have it.
|
for (const name of Object.keys(apiPolyfills)) {
|
||||||
api = Object.assign(copyApiObject(api), {
|
if (api[name]) continue;
|
||||||
assertVersion(range) {
|
|
||||||
throwVersionError(range, api.version);
|
// TODO: Use ??= when flow lets us to do so
|
||||||
},
|
clonedApi = clonedApi ?? copyApiObject(api);
|
||||||
});
|
clonedApi[name] = apiPolyfills[name](clonedApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder(api, options || {}, dirname);
|
return builder(clonedApi ?? api, options || {}, dirname);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const apiPolyfills = {
|
||||||
|
// Not supported by Babel 7 and early versions of Babel 7 beta.
|
||||||
|
// It's important that this is polyfilled for older Babel versions
|
||||||
|
// since it's needed to report the version mismatch.
|
||||||
|
assertVersion: api => range => {
|
||||||
|
throwVersionError(range, api.version);
|
||||||
|
},
|
||||||
|
// This is supported starting from Babel 7.13
|
||||||
|
// TODO(Babel 8): Remove this polyfill
|
||||||
|
targets: () => () => {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function copyApiObject(api) {
|
function copyApiObject(api) {
|
||||||
// Babel >= 7 <= beta.41 passed the API as a new object that had
|
// Babel >= 7 <= beta.41 passed the API as a new object that had
|
||||||
// babel/core as the prototype. While slightly faster, it also
|
// babel/core as the prototype. While slightly faster, it also
|
||||||
|
|||||||
@ -206,6 +206,26 @@ export const getPolyfillPlugins = ({
|
|||||||
return polyfillPlugins;
|
return polyfillPlugins;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getLocalTargets(
|
||||||
|
optionsTargets,
|
||||||
|
ignoreBrowserslistConfig,
|
||||||
|
configPath,
|
||||||
|
browserslistEnv,
|
||||||
|
) {
|
||||||
|
if (optionsTargets?.esmodules && optionsTargets.browsers) {
|
||||||
|
console.warn(`
|
||||||
|
@babel/preset-env: esmodules and browsers targets have been specified together.
|
||||||
|
\`browsers\` target, \`${optionsTargets.browsers.toString()}\` will be ignored.
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getTargets(
|
||||||
|
// $FlowIgnore optionsTargets doesn't have an "uglify" property anymore
|
||||||
|
(optionsTargets: InputTargets),
|
||||||
|
{ ignoreBrowserslistConfig, configPath, browserslistEnv },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function supportsStaticESM(caller) {
|
function supportsStaticESM(caller) {
|
||||||
return !!caller?.supportsStaticESM;
|
return !!caller?.supportsStaticESM;
|
||||||
}
|
}
|
||||||
@ -225,6 +245,8 @@ function supportsTopLevelAwait(caller) {
|
|||||||
export default declare((api, opts) => {
|
export default declare((api, opts) => {
|
||||||
api.assertVersion(7);
|
api.assertVersion(7);
|
||||||
|
|
||||||
|
const babelTargets = api.targets();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
bugfixes,
|
bugfixes,
|
||||||
configPath,
|
configPath,
|
||||||
@ -243,6 +265,16 @@ export default declare((api, opts) => {
|
|||||||
browserslistEnv,
|
browserslistEnv,
|
||||||
} = normalizeOptions(opts);
|
} = normalizeOptions(opts);
|
||||||
|
|
||||||
|
let targets = babelTargets;
|
||||||
|
|
||||||
|
if (
|
||||||
|
// If any browserslist-related option is specified, fallback to the old
|
||||||
|
// behavior of not using the targets specified in the top-level options.
|
||||||
|
opts.targets ||
|
||||||
|
opts.configPath ||
|
||||||
|
opts.browserslistEnv ||
|
||||||
|
opts.ignoreBrowserslistConfig
|
||||||
|
) {
|
||||||
if (!process.env.BABEL_8_BREAKING) {
|
if (!process.env.BABEL_8_BREAKING) {
|
||||||
// eslint-disable-next-line no-var
|
// eslint-disable-next-line no-var
|
||||||
var hasUglifyTarget = false;
|
var hasUglifyTarget = false;
|
||||||
@ -258,20 +290,13 @@ option \`forceAllTransforms: true\` instead.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optionsTargets?.esmodules && optionsTargets.browsers) {
|
targets = getLocalTargets(
|
||||||
console.warn(`
|
optionsTargets,
|
||||||
@babel/preset-env: esmodules and browsers targets have been specified together.
|
ignoreBrowserslistConfig,
|
||||||
\`browsers\` target, \`${optionsTargets.browsers.toString()}\` will be ignored.
|
configPath,
|
||||||
`);
|
browserslistEnv,
|
||||||
}
|
|
||||||
|
|
||||||
const targets = getTargets(
|
|
||||||
// $FlowIgnore optionsTargets doesn't have an "uglify" property anymore
|
|
||||||
(optionsTargets: InputTargets),
|
|
||||||
{ ignoreBrowserslistConfig, configPath, browserslistEnv },
|
|
||||||
);
|
);
|
||||||
const include = transformIncludesAndExcludes(optionsInclude);
|
}
|
||||||
const exclude = transformIncludesAndExcludes(optionsExclude);
|
|
||||||
|
|
||||||
const transformTargets = (
|
const transformTargets = (
|
||||||
process.env.BABEL_8_BREAKING
|
process.env.BABEL_8_BREAKING
|
||||||
@ -281,6 +306,9 @@ option \`forceAllTransforms: true\` instead.
|
|||||||
? {}
|
? {}
|
||||||
: targets;
|
: targets;
|
||||||
|
|
||||||
|
const include = transformIncludesAndExcludes(optionsInclude);
|
||||||
|
const exclude = transformIncludesAndExcludes(optionsExclude);
|
||||||
|
|
||||||
const compatData = getPluginList(shippedProposals, bugfixes);
|
const compatData = getPluginList(shippedProposals, bugfixes);
|
||||||
const shouldSkipExportNamespaceFrom =
|
const shouldSkipExportNamespaceFrom =
|
||||||
(modules === "auto" && api.caller?.(supportsExportNamespaceFrom)) ||
|
(modules === "auto" && api.caller?.(supportsExportNamespaceFrom)) ||
|
||||||
|
|||||||
1
packages/babel-preset-env/test/fixtures/debug/top-level-targets-shadowed/input.mjs
vendored
Normal file
1
packages/babel-preset-env/test/fixtures/debug/top-level-targets-shadowed/input.mjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo?.bar;
|
||||||
8
packages/babel-preset-env/test/fixtures/debug/top-level-targets-shadowed/options.json
vendored
Normal file
8
packages/babel-preset-env/test/fixtures/debug/top-level-targets-shadowed/options.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"validateLogs": true,
|
||||||
|
"ignoreOutput": true,
|
||||||
|
"targets": "chrome 80",
|
||||||
|
"presets": [
|
||||||
|
["env", { "debug": true, "targets": "chrome 60" }]
|
||||||
|
]
|
||||||
|
}
|
||||||
26
packages/babel-preset-env/test/fixtures/debug/top-level-targets-shadowed/stdout.txt
vendored
Normal file
26
packages/babel-preset-env/test/fixtures/debug/top-level-targets-shadowed/stdout.txt
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@babel/preset-env: `DEBUG` option
|
||||||
|
|
||||||
|
Using targets:
|
||||||
|
{
|
||||||
|
"chrome": "60"
|
||||||
|
}
|
||||||
|
|
||||||
|
Using modules transform: auto
|
||||||
|
|
||||||
|
Using plugins:
|
||||||
|
proposal-numeric-separator { "chrome":"60" }
|
||||||
|
proposal-logical-assignment-operators { "chrome":"60" }
|
||||||
|
proposal-nullish-coalescing-operator { "chrome":"60" }
|
||||||
|
proposal-optional-chaining { "chrome":"60" }
|
||||||
|
proposal-json-strings { "chrome":"60" }
|
||||||
|
proposal-optional-catch-binding { "chrome":"60" }
|
||||||
|
proposal-async-generator-functions { "chrome":"60" }
|
||||||
|
syntax-object-rest-spread { "chrome":"60" }
|
||||||
|
transform-dotall-regex { "chrome":"60" }
|
||||||
|
proposal-unicode-property-regex { "chrome":"60" }
|
||||||
|
transform-named-capturing-groups-regex { "chrome":"60" }
|
||||||
|
proposal-export-namespace-from { "chrome":"60" }
|
||||||
|
transform-modules-commonjs { "chrome":"60" }
|
||||||
|
proposal-dynamic-import { "chrome":"60" }
|
||||||
|
|
||||||
|
Using polyfills: No polyfills were added, since the `useBuiltIns` option was not set.
|
||||||
1
packages/babel-preset-env/test/fixtures/debug/top-level-targets/input.mjs
vendored
Normal file
1
packages/babel-preset-env/test/fixtures/debug/top-level-targets/input.mjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo?.bar;
|
||||||
8
packages/babel-preset-env/test/fixtures/debug/top-level-targets/options.json
vendored
Normal file
8
packages/babel-preset-env/test/fixtures/debug/top-level-targets/options.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"validateLogs": true,
|
||||||
|
"ignoreOutput": true,
|
||||||
|
"targets": "chrome 80",
|
||||||
|
"presets": [
|
||||||
|
["env", { "debug": true }]
|
||||||
|
]
|
||||||
|
}
|
||||||
23
packages/babel-preset-env/test/fixtures/debug/top-level-targets/stdout.txt
vendored
Normal file
23
packages/babel-preset-env/test/fixtures/debug/top-level-targets/stdout.txt
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
@babel/preset-env: `DEBUG` option
|
||||||
|
|
||||||
|
Using targets:
|
||||||
|
{
|
||||||
|
"chrome": "80"
|
||||||
|
}
|
||||||
|
|
||||||
|
Using modules transform: auto
|
||||||
|
|
||||||
|
Using plugins:
|
||||||
|
syntax-numeric-separator { "chrome":"80" }
|
||||||
|
proposal-logical-assignment-operators { "chrome":"80" }
|
||||||
|
syntax-nullish-coalescing-operator { "chrome":"80" }
|
||||||
|
syntax-optional-chaining { "chrome":"80" }
|
||||||
|
syntax-json-strings { "chrome":"80" }
|
||||||
|
syntax-optional-catch-binding { "chrome":"80" }
|
||||||
|
syntax-async-generators { "chrome":"80" }
|
||||||
|
syntax-object-rest-spread { "chrome":"80" }
|
||||||
|
transform-modules-commonjs { "chrome":"80" }
|
||||||
|
proposal-dynamic-import { "chrome":"80" }
|
||||||
|
proposal-export-namespace-from {}
|
||||||
|
|
||||||
|
Using polyfills: No polyfills were added, since the `useBuiltIns` option was not set.
|
||||||
@ -131,6 +131,7 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame": "workspace:^7.12.13"
|
"@babel/code-frame": "workspace:^7.12.13"
|
||||||
"@babel/generator": "workspace:^7.12.17"
|
"@babel/generator": "workspace:^7.12.17"
|
||||||
|
"@babel/helper-compilation-targets": "workspace:^7.12.17"
|
||||||
"@babel/helper-module-transforms": "workspace:^7.12.17"
|
"@babel/helper-module-transforms": "workspace:^7.12.17"
|
||||||
"@babel/helper-transform-fixture-test-runner": "workspace:*"
|
"@babel/helper-transform-fixture-test-runner": "workspace:*"
|
||||||
"@babel/helpers": "workspace:^7.12.17"
|
"@babel/helpers": "workspace:^7.12.17"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user