Skip to content

Commit

Permalink
Support .mjs plugins/presets and async factories (#12266)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Dec 10, 2020
1 parent 4f3fcf1 commit 961dff6
Show file tree
Hide file tree
Showing 38 changed files with 512 additions and 163 deletions.
2 changes: 1 addition & 1 deletion packages/babel-core/package.json
Expand Up @@ -53,7 +53,7 @@
"@babel/types": "workspace:^7.12.10",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"gensync": "^1.0.0-beta.2",
"json5": "^2.1.2",
"lodash": "^4.17.19",
"semver": "^5.4.1",
Expand Down
16 changes: 8 additions & 8 deletions packages/babel-core/src/config/config-chain.js
Expand Up @@ -154,7 +154,7 @@ export function* buildRootChain(
programmaticLogger,
);
if (!programmaticChain) return null;
const programmaticReport = programmaticLogger.output();
const programmaticReport = yield* programmaticLogger.output();

let configFile;
if (typeof opts.configFile === "string") {
Expand Down Expand Up @@ -186,7 +186,7 @@ export function* buildRootChain(
configFileLogger,
);
if (!result) return null;
configReport = configFileLogger.output();
configReport = yield* configFileLogger.output();

// Allow config files to toggle `.babelrc` resolution on and off and
// specify where the roots are.
Expand Down Expand Up @@ -244,7 +244,7 @@ export function* buildRootChain(
if (!result) {
isIgnored = true;
} else {
babelRcReport = babelrcLogger.output();
babelRcReport = yield* babelrcLogger.output();
mergeChain(fileChain, result);
}
}
Expand Down Expand Up @@ -599,7 +599,7 @@ function makeChainWalker<ArgT: { options: ValidatedOptions, dirname: string }>({
}

logger(config, index, envName);
mergeChainOpts(chain, config);
yield* mergeChainOpts(chain, config);
}
return chain;
};
Expand Down Expand Up @@ -657,13 +657,13 @@ function mergeChain(target: ConfigChain, source: ConfigChain): ConfigChain {
return target;
}

function mergeChainOpts(
function* mergeChainOpts(
target: ConfigChain,
{ options, plugins, presets }: OptionsAndDescriptors,
): ConfigChain {
): Handler<ConfigChain> {
target.options.push(options);
target.plugins.push(...plugins());
target.presets.push(...presets());
target.plugins.push(...(yield* plugins()));
target.presets.push(...(yield* presets()));

return target;
}
Expand Down
93 changes: 60 additions & 33 deletions packages/babel-core/src/config/config-descriptors.js
@@ -1,12 +1,15 @@
// @flow

import gensync, { type Handler } from "gensync";

import { loadPlugin, loadPreset } from "./files";

import { getItemDescriptor } from "./item";

import {
makeWeakCacheSync,
makeStrongCacheSync,
makeStrongCache,
type CacheConfigurator,
} from "./caching";

Expand All @@ -21,8 +24,8 @@ import type {
// the options object actually ends up being applicable.
export type OptionsAndDescriptors = {
options: ValidatedOptions,
plugins: () => Array<UnloadedDescriptor>,
presets: () => Array<UnloadedDescriptor>,
plugins: () => Handler<Array<UnloadedDescriptor>>,
presets: () => Handler<Array<UnloadedDescriptor>>,
};

// Represents a plugin or presets at a given location in a config object.
Expand Down Expand Up @@ -63,6 +66,11 @@ export type ValidatedFile = {
options: ValidatedOptions,
};

// eslint-disable-next-line require-yield
function* handlerOf<T>(value: T): Handler<T> {
return value;
}

/**
* Create a set of descriptors from a given options object, preserving
* descriptor identity based on the identity of the plugin/preset arrays
Expand All @@ -78,13 +86,13 @@ export function createCachedDescriptors(
options,
plugins: plugins
? () => createCachedPluginDescriptors(plugins, dirname)(alias)
: () => [],
: () => handlerOf([]),
presets: presets
? () =>
createCachedPresetDescriptors(presets, dirname)(alias)(
!!passPerPreset,
)
: () => [],
: () => handlerOf([]),
};
}

Expand All @@ -105,19 +113,19 @@ export function createUncachedDescriptors(

return {
options,
plugins: () => {
*plugins() {
if (!plugins) {
plugins = createPluginDescriptors(
plugins = yield* createPluginDescriptors(
options.plugins || [],
dirname,
alias,
);
}
return plugins;
},
presets: () => {
*presets() {
if (!presets) {
presets = createPresetDescriptors(
presets = yield* createPresetDescriptors(
options.presets || [],
dirname,
alias,
Expand All @@ -134,14 +142,22 @@ const createCachedPresetDescriptors = makeWeakCacheSync(
(items: PluginList, cache: CacheConfigurator<string>) => {
const dirname = cache.using(dir => dir);
return makeStrongCacheSync((alias: string) =>
makeStrongCacheSync((passPerPreset: boolean) =>
createPresetDescriptors(items, dirname, alias, passPerPreset).map(
makeStrongCache(function* (
passPerPreset: boolean,
): Handler<Array<UnloadedDescriptor>> {
const descriptors = yield* createPresetDescriptors(
items,
dirname,
alias,
passPerPreset,
);
return descriptors.map(
// Items are cached using the overall preset array identity when
// possibly, but individual descriptors are also cached if a match
// can be found in the previously-used descriptor lists.
desc => loadCachedDescriptor(PRESET_DESCRIPTOR_CACHE, desc),
),
),
);
}),
);
},
);
Expand All @@ -150,14 +166,17 @@ const PLUGIN_DESCRIPTOR_CACHE = new WeakMap();
const createCachedPluginDescriptors = makeWeakCacheSync(
(items: PluginList, cache: CacheConfigurator<string>) => {
const dirname = cache.using(dir => dir);
return makeStrongCacheSync((alias: string) =>
createPluginDescriptors(items, dirname, alias).map(
return makeStrongCache(function* (
alias: string,
): Handler<Array<UnloadedDescriptor>> {
const descriptors = yield* createPluginDescriptors(items, dirname, alias);
return descriptors.map(
// Items are cached using the overall plugin array identity when
// possibly, but individual descriptors are also cached if a match
// can be found in the previously-used descriptor lists.
desc => loadCachedDescriptor(PLUGIN_DESCRIPTOR_CACHE, desc),
),
);
);
});
},
);

Expand Down Expand Up @@ -205,36 +224,44 @@ function loadCachedDescriptor(
return desc;
}

function createPresetDescriptors(
function* createPresetDescriptors(
items: PluginList,
dirname: string,
alias: string,
passPerPreset: boolean,
): Array<UnloadedDescriptor> {
return createDescriptors("preset", items, dirname, alias, passPerPreset);
): Handler<Array<UnloadedDescriptor>> {
return yield* createDescriptors(
"preset",
items,
dirname,
alias,
passPerPreset,
);
}

function createPluginDescriptors(
function* createPluginDescriptors(
items: PluginList,
dirname: string,
alias: string,
): Array<UnloadedDescriptor> {
return createDescriptors("plugin", items, dirname, alias);
): Handler<Array<UnloadedDescriptor>> {
return yield* createDescriptors("plugin", items, dirname, alias);
}

function createDescriptors(
function* createDescriptors(
type: "plugin" | "preset",
items: PluginList,
dirname: string,
alias: string,
ownPass?: boolean,
): Array<UnloadedDescriptor> {
const descriptors = items.map((item, index) =>
createDescriptor(item, dirname, {
type,
alias: `${alias}$${index}`,
ownPass: !!ownPass,
}),
): Handler<Array<UnloadedDescriptor>> {
const descriptors = yield* gensync.all(
items.map((item, index) =>
createDescriptor(item, dirname, {
type,
alias: `${alias}$${index}`,
ownPass: !!ownPass,
}),
),
);

assertNoDuplicates(descriptors);
Expand All @@ -245,7 +272,7 @@ function createDescriptors(
/**
* Given a plugin/preset item, resolve it into a standard format.
*/
export function createDescriptor(
export function* createDescriptor(
pair: PluginItem,
dirname: string,
{
Expand All @@ -257,7 +284,7 @@ export function createDescriptor(
alias: string,
ownPass?: boolean,
},
): UnloadedDescriptor {
): Handler<UnloadedDescriptor> {
const desc = getItemDescriptor(pair);
if (desc) {
return desc;
Expand Down Expand Up @@ -285,7 +312,7 @@ export function createDescriptor(
const resolver = type === "plugin" ? loadPlugin : loadPreset;
const request = value;

({ filepath, value } = resolver(value, dirname));
({ filepath, value } = yield* resolver(value, dirname));

file = {
request,
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-core/src/config/files/index-browser.js
Expand Up @@ -80,7 +80,7 @@ export function resolvePreset(name: string, dirname: string): string | null {
export function loadPlugin(
name: string,
dirname: string,
): { filepath: string, value: mixed } {
): Handler<{ filepath: string, value: mixed }> {
throw new Error(
`Cannot load plugin ${name} relative to ${dirname} in a browser`,
);
Expand All @@ -89,7 +89,7 @@ export function loadPlugin(
export function loadPreset(
name: string,
dirname: string,
): { filepath: string, value: mixed } {
): Handler<{ filepath: string, value: mixed }> {
throw new Error(
`Cannot load preset ${name} relative to ${dirname} in a browser`,
);
Expand Down
14 changes: 9 additions & 5 deletions packages/babel-core/src/config/files/module-types.js
Expand Up @@ -12,13 +12,15 @@ try {
export default function* loadCjsOrMjsDefault(
filepath: string,
asyncError: string,
// TODO(Babel 8): Remove this
fallbackToTranspiledModule: boolean = false,
): Handler<mixed> {
switch (guessJSModuleType(filepath)) {
case "cjs":
return loadCjsDefault(filepath);
return loadCjsDefault(filepath, fallbackToTranspiledModule);
case "unknown":
try {
return loadCjsDefault(filepath);
return loadCjsDefault(filepath, fallbackToTranspiledModule);
} catch (e) {
if (e.code !== "ERR_REQUIRE_ESM") throw e;
}
Expand All @@ -42,10 +44,12 @@ function guessJSModuleType(filename: string): "cjs" | "mjs" | "unknown" {
}
}

function loadCjsDefault(filepath: string) {
function loadCjsDefault(filepath: string, fallbackToTranspiledModule: boolean) {
const module = (require(filepath): mixed);
// TODO (Babel 8): Remove "undefined" fallback
return module?.__esModule ? module.default || undefined : module;
return module?.__esModule
? // TODO (Babel 8): Remove "module" and "undefined" fallback
module.default || (fallbackToTranspiledModule ? module : undefined)
: module;
}

async function loadMjsDefault(filepath: string) {
Expand Down

0 comments on commit 961dff6

Please sign in to comment.