Skip to content

Commit 7aa23aa

Browse files
Support configuring cache in ESM configs (#15850)
1 parent e7b83da commit 7aa23aa

5 files changed

Lines changed: 91 additions & 36 deletions

File tree

packages/babel-core/src/config/files/configuration.ts

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import path from "path";
44
import json5 from "json5";
55
import gensync from "gensync";
66
import type { Handler } from "gensync";
7-
import { makeStrongCache, makeWeakCacheSync } from "../caching";
7+
import { makeWeakCache, makeWeakCacheSync } from "../caching";
88
import type { CacheConfigurator } from "../caching";
99
import { makeConfigAPI } from "../helpers/config-api";
1010
import type { ConfigAPI } from "../helpers/config-api";
1111
import { makeStaticFileCache } from "./utils";
1212
import loadCodeDefault from "./module-types";
1313
import pathPatternToRegex from "../pattern-to-regex";
1414
import type { FilePackageData, RelativeConfig, ConfigFile } from "./types";
15-
import type { CallerMetadata } from "../validation/options";
15+
import type { CallerMetadata, InputOptions } from "../validation/options";
1616
import ConfigError from "../../errors/config-error";
1717

1818
import * as fs from "../../gensync-utils/fs";
@@ -43,30 +43,41 @@ const BABELIGNORE_FILENAME = ".babelignore";
4343

4444
const LOADING_CONFIGS = new Set();
4545

46-
const readConfigCode = makeStrongCache(function* readConfigCode(
46+
type ConfigCacheData = {
47+
envName: string;
48+
caller: CallerMetadata | undefined;
49+
};
50+
51+
const runConfig = makeWeakCache(function* runConfig(
52+
options: Function,
53+
cache: CacheConfigurator<ConfigCacheData>,
54+
): Handler<{
55+
options: InputOptions | null;
56+
cacheNeedsConfiguration: boolean;
57+
}> {
58+
// @ts-expect-error - if we want to make it possible to use async configs
59+
yield* [];
60+
61+
return {
62+
options: endHiddenCallStack(options as any as (api: ConfigAPI) => {})(
63+
makeConfigAPI(cache),
64+
),
65+
cacheNeedsConfiguration: !cache.configured(),
66+
};
67+
});
68+
69+
function* readConfigCode(
4770
filepath: string,
48-
cache: CacheConfigurator<{
49-
envName: string;
50-
caller: CallerMetadata | undefined;
51-
}>,
71+
data: ConfigCacheData,
5272
): Handler<ConfigFile | null> {
53-
if (!nodeFs.existsSync(filepath)) {
54-
cache.never();
55-
return null;
56-
}
73+
if (!nodeFs.existsSync(filepath)) return null;
5774

5875
// The `require()` call below can make this code reentrant if a require hook like @babel/register has been
5976
// loaded into the system. That would cause Babel to attempt to compile the `.babelrc.js` file as it loads
6077
// below. To cover this case, we auto-ignore re-entrant config processing.
6178
if (LOADING_CONFIGS.has(filepath)) {
62-
cache.never();
63-
6479
debug("Auto-ignoring usage of config %o.", filepath);
65-
return {
66-
filepath,
67-
dirname: path.dirname(filepath),
68-
options: {},
69-
};
80+
return buildConfigFileObject({}, filepath);
7081
}
7182

7283
let options: unknown;
@@ -81,16 +92,9 @@ const readConfigCode = makeStrongCache(function* readConfigCode(
8192
LOADING_CONFIGS.delete(filepath);
8293
}
8394

84-
let assertCache = false;
95+
let cacheNeedsConfiguration = false;
8596
if (typeof options === "function") {
86-
// @ts-expect-error - if we want to make it possible to use async configs
87-
yield* [];
88-
89-
options = endHiddenCallStack(options as any as (api: ConfigAPI) => {})(
90-
makeConfigAPI(cache),
91-
);
92-
93-
assertCache = true;
97+
({ options, cacheNeedsConfiguration } = yield* runConfig(options, data));
9498
}
9599

96100
if (!options || typeof options !== "object" || Array.isArray(options)) {
@@ -102,6 +106,10 @@ const readConfigCode = makeStrongCache(function* readConfigCode(
102106

103107
// @ts-expect-error todo(flow->ts)
104108
if (typeof options.then === "function") {
109+
// @ts-expect-error We use ?. in case options is a thenable
110+
// but not a promise
111+
options.catch?.(() => {});
112+
105113
throw new ConfigError(
106114
`You appear to be using an async configuration, ` +
107115
`which your current version of Babel does not support. ` +
@@ -112,14 +120,38 @@ const readConfigCode = makeStrongCache(function* readConfigCode(
112120
);
113121
}
114122

115-
if (assertCache && !cache.configured()) throwConfigError(filepath);
123+
if (cacheNeedsConfiguration) throwConfigError(filepath);
116124

117-
return {
118-
filepath,
119-
dirname: path.dirname(filepath),
120-
options,
121-
};
122-
});
125+
return buildConfigFileObject(options, filepath);
126+
}
127+
128+
// We cache the generated ConfigFile object rather than creating a new one
129+
// every time, so that it can be used as a cache key in other functions.
130+
const cfboaf /* configFilesByOptionsAndFilepath */ = new WeakMap<
131+
InputOptions,
132+
Map<string, ConfigFile>
133+
>();
134+
function buildConfigFileObject(
135+
options: InputOptions,
136+
filepath: string,
137+
): ConfigFile {
138+
let configFilesByFilepath = cfboaf.get(options);
139+
if (!configFilesByFilepath) {
140+
cfboaf.set(options, (configFilesByFilepath = new Map()));
141+
}
142+
143+
let configFile = configFilesByFilepath.get(filepath);
144+
if (!configFile) {
145+
configFile = {
146+
filepath,
147+
dirname: path.dirname(filepath),
148+
options,
149+
};
150+
configFilesByFilepath.set(filepath, configFile);
151+
}
152+
153+
return configFile;
154+
}
123155

124156
const packageToBabelConfig = makeWeakCacheSync(
125157
(file: ConfigFile): ConfigFile | null => {

packages/babel-core/test/async.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
spawnTransformAsync,
77
spawnTransformSync,
88
supportsESM,
9+
itESM,
910
} from "./helpers/esm.js";
1011

1112
const nodeGte8 = (...args) => {
@@ -115,6 +116,14 @@ describe("asynchronicity", () => {
115116
);
116117
});
117118
});
119+
120+
itESM("mjs configuring cache", async () => {
121+
process.chdir("config-file-mjs-cache");
122+
123+
const { code } = await babel.transformAsync("");
124+
125+
expect(code).toBe(`"success"`);
126+
});
118127
});
119128

120129
describe("plugin", () => {

packages/babel-core/test/fixtures/async/config-file-async-function/babel.config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const wait = t => new Promise(r => setTimeout(r, t));
33
module.exports = async function(api) {
44
await wait(50);
55

6-
api.cache.never();
7-
86
return {
97
plugins: ["./plugin"],
108
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function (api) {
2+
api.cache.never();
3+
4+
return {
5+
plugins: ["./plugin"],
6+
};
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = function plugin({ types: t }) {
2+
return {
3+
visitor: {
4+
Program(path) {
5+
path.pushContainer("body", t.stringLiteral("success"));
6+
},
7+
},
8+
};
9+
};

0 commit comments

Comments
 (0)