Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add targets and browserslist* options to @babel/core #12189

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/babel-core/package.json
Expand Up @@ -38,13 +38,16 @@
},
"browser": {
"./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",
"./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"
},
"dependencies": {
"@babel/code-frame": "workspace:^7.10.4",
"@babel/generator": "workspace:^7.12.10",
"@babel/helper-compilation-targets": "workspace:^7.12.5",
"@babel/helper-module-transforms": "workspace:^7.12.1",
"@babel/helpers": "workspace:^7.12.5",
"@babel/parser": "workspace:^7.12.10",
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-core/src/config/files/configuration.js
Expand Up @@ -9,7 +9,7 @@ import {
makeWeakCacheSync,
type CacheConfigurator,
} from "../caching";
import makeAPI, { type PluginAPI } from "../helpers/config-api";
import { makeConfigAPI, type ConfigAPI } from "../helpers/config-api";
import { makeStaticFileCache } from "./utils";
import loadCjsOrMjsDefault from "./module-types";
import pathPatternToRegex from "../pattern-to-regex";
Expand Down Expand Up @@ -203,7 +203,7 @@ const readConfigJS = makeStrongCache(function* readConfigJS(
let assertCache = false;
if (typeof options === "function") {
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;
}
Expand Down
24 changes: 18 additions & 6 deletions packages/babel-core/src/config/full.js
Expand Up @@ -14,6 +14,7 @@ import {
type PresetInstance,
} from "./config-chain";
import type { UnloadedDescriptor } from "./config-descriptors";
import type { Targets } from "@babel/helper-compilation-targets";
import traverse from "@babel/traverse";
import {
makeWeakCache,
Expand All @@ -27,7 +28,7 @@ import {
type PluginItem,
} from "./validation/options";
import { validatePluginObject } from "./validation/plugins";
import makeAPI from "./helpers/config-api";
import { makePluginAPI } from "./helpers/config-api";

import loadPrivatePartialConfig from "./partial";
import type { ValidatedOptions } from "./validation/options";
Expand All @@ -39,6 +40,11 @@ type LoadedDescriptor = {
alias: string,
};

type PluginContext = {
...ConfigContext,
targets: Targets,
};

export type { InputOptions } from "./validation/options";

export type ResolvedConfig = {
Expand All @@ -55,6 +61,7 @@ export type PluginPasses = Array<PluginPassList>;
type SimpleContext = {
envName: string,
caller: CallerMetadata | void,
targets: Targets,
};

export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
Expand All @@ -78,6 +85,11 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
throw new Error("Assertion failure - plugins and presets exist");
}

const pluginContext: PluginContext = {
...context,
targets: options.targets,
};

const toDescriptor = (item: PluginItem) => {
const desc = getItemDescriptor(item);
if (!desc) {
Expand Down Expand Up @@ -112,12 +124,12 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
// in the previous pass.
if (descriptor.ownPass) {
presets.push({
preset: yield* loadPresetDescriptor(descriptor, context),
preset: yield* loadPresetDescriptor(descriptor, pluginContext),
pass: [],
});
} else {
presets.unshift({
preset: yield* loadPresetDescriptor(descriptor, context),
preset: yield* loadPresetDescriptor(descriptor, pluginContext),
pass: pluginDescriptorsPass,
});
}
Expand Down Expand Up @@ -172,7 +184,7 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
const descriptor: UnloadedDescriptor = descs[i];
if (descriptor.options !== false) {
try {
pass.push(yield* loadPluginDescriptor(descriptor, context));
pass.push(yield* loadPluginDescriptor(descriptor, pluginContext));
} catch (e) {
if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") {
// print special message for `plugins: ["@babel/foo", { foo: "option" }]`
Expand Down Expand Up @@ -235,7 +247,7 @@ const loadDescriptor = makeWeakCache(function* (

const api = {
...context,
...makeAPI(cache),
...makePluginAPI(cache),
};
try {
item = yield* factory(api, options, dirname);
Expand Down Expand Up @@ -375,7 +387,7 @@ const validatePreset = (
*/
function* loadPresetDescriptor(
descriptor: UnloadedDescriptor,
context: ConfigContext,
context: PluginContext,
): Handler<ConfigChain | null> {
const preset = instantiatePreset(yield* loadDescriptor(descriptor, context));
validatePreset(preset, context, descriptor);
Expand Down
33 changes: 29 additions & 4 deletions packages/babel-core/src/config/helpers/config-api.js
@@ -1,6 +1,8 @@
// @flow

import semver from "semver";
import type { Targets } from "@babel/helper-compilation-targets";

import { version as coreVersion } from "../../";
import {
assertSimpleType,
Expand All @@ -20,7 +22,9 @@ type EnvFunction = {

type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType;

export type PluginAPI = {|
type TargetsFunction = () => Targets;

export type ConfigAPI = {|
version: string,
cache: SimpleCacheConfigurator,
env: EnvFunction,
Expand All @@ -29,9 +33,14 @@ export type PluginAPI = {|
caller?: CallerFactory,
|};

export default function makeAPI(
cache: CacheConfigurator<{ envName: string, caller: CallerMetadata | void }>,
): PluginAPI {
export type PluginAPI = {|
...ConfigAPI,
targets: TargetsFunction,
|};

export function makeConfigAPI<
SideChannel: { envName: string, caller: CallerMetadata | void },
>(cache: CacheConfigurator<SideChannel>): ConfigAPI {
const env: any = value =>
cache.using(data => {
if (typeof value === "undefined") return data.envName;
Expand Down Expand Up @@ -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 {
if (typeof range === "number") {
if (!Number.isInteger(range)) {
Expand Down
56 changes: 32 additions & 24 deletions packages/babel-core/src/config/partial.js
Expand Up @@ -14,6 +14,7 @@ import { getEnv } from "./helpers/environment";
import {
validate,
type ValidatedOptions,
type NormalizedOptions,
type RootMode,
} from "./validation/options";

Expand All @@ -24,6 +25,7 @@ import {
type ConfigFile,
type IgnoreFile,
} from "./files";
import { resolveTargets } from "./resolve-targets";

function* resolveRootMode(
rootDir: string,
Expand Down Expand Up @@ -61,7 +63,7 @@ function* resolveRootMode(
}

type PrivPartialConfig = {
options: ValidatedOptions,
options: NormalizedOptions,
context: ConfigContext,
fileHandling: FileHandling,
ignore: IgnoreFile | void,
Expand Down Expand Up @@ -115,30 +117,36 @@ export default function* loadPrivatePartialConfig(
const configChain = yield* buildRootChain(args, context);
if (!configChain) return null;

const options = {};
const merged: ValidatedOptions = {};
configChain.options.forEach(opts => {
mergeOptions(options, opts);
mergeOptions((merged: any), opts);
});

// 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
// to not change behavior.
options.cloneInputAst = cloneInputAst;
options.babelrc = false;
options.configFile = false;
options.passPerPreset = false;
options.envName = context.envName;
options.cwd = context.cwd;
options.root = context.root;
options.filename =
typeof context.filename === "string" ? context.filename : undefined;

options.plugins = configChain.plugins.map(descriptor =>
createItemFromDescriptor(descriptor),
);
options.presets = configChain.presets.map(descriptor =>
createItemFromDescriptor(descriptor),
);
const options: NormalizedOptions = {
...merged,
targets: resolveTargets(merged, absoluteRootDir, filename),

// 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
// to not change behavior.
cloneInputAst,
babelrc: false,
configFile: false,
browserslistConfigFile: false,
passPerPreset: false,
envName: context.envName,
cwd: context.cwd,
root: context.root,
filename:
typeof context.filename === "string" ? context.filename : undefined,

plugins: configChain.plugins.map(descriptor =>
createItemFromDescriptor(descriptor),
),
presets: configChain.presets.map(descriptor =>
createItemFromDescriptor(descriptor),
),
};

return {
options,
Expand Down Expand Up @@ -201,15 +209,15 @@ class PartialConfig {
* These properties are public, so any changes to them should be considered
* a breaking change to Babel's API.
*/
options: ValidatedOptions;
options: NormalizedOptions;
babelrc: string | void;
babelignore: string | void;
config: string | void;
fileHandling: FileHandling;
files: Set<string>;

constructor(
options: ValidatedOptions,
options: NormalizedOptions,
babelrc: string | void,
ignore: string | void,
config: string | void,
Expand Down
26 changes: 26 additions & 0 deletions packages/babel-core/src/config/resolve-targets-browser.js
@@ -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 changes: 39 additions & 0 deletions packages/babel-core/src/config/resolve-targets.js
@@ -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,
});
}
4 changes: 2 additions & 2 deletions packages/babel-core/src/config/util.js
@@ -1,10 +1,10 @@
// @flow

import type { ValidatedOptions } from "./validation/options";
import type { ValidatedOptions, NormalizedOptions } from "./validation/options";

export function mergeOptions(
target: ValidatedOptions,
source: ValidatedOptions,
source: ValidatedOptions | NormalizedOptions,
): void {
for (const k of Object.keys(source)) {
if (k === "parserOpts" && source.parserOpts) {
Expand Down