Skip to content

Commit

Permalink
Fix a race condition in @babel/core (#14843)
Browse files Browse the repository at this point in the history
Co-authored-by: Nicol貌 Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
JLHwung and nicolo-ribaudo committed Aug 16, 2022
1 parent a32cf83 commit c45da3d
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 85 deletions.
2 changes: 1 addition & 1 deletion Gulpfile.mjs
Expand Up @@ -265,7 +265,7 @@ async function buildBabel(useWorker, ignore = []) {
const dest = "./" + mapSrcToLib(file.slice(2));
promises.push(worker.transform(file, dest));
}
return Promise.all(promises).finally(() => {
return Promise.allSettled(promises).finally(() => {
if (worker.end !== undefined) {
worker.end();
}
Expand Down
4 changes: 2 additions & 2 deletions babel-worker.cjs
@@ -1,4 +1,4 @@
const { transformSync } = require("@babel/core");
const { transformAsync } = require("@babel/core");
const { mkdirSync, statSync, readFileSync, writeFileSync } = require("fs");
const { dirname } = require("path");
const { log } = require("./scripts/utils/logger.cjs");
Expand Down Expand Up @@ -32,7 +32,7 @@ exports.transform = async function transform(src, dest) {
}
log(`Compiling '${chalk.cyan(src)}'...`);
const content = readFileSync(src, { encoding: "utf8" });
const { code } = transformSync(content, {
const { code } = await transformAsync(content, {
filename: src,
caller: {
// We have wrapped packages/babel-core/src/config/files/configuration.js with feature detection
Expand Down
46 changes: 16 additions & 30 deletions packages/babel-core/src/config/config-descriptors.ts
@@ -1,6 +1,5 @@
import gensync from "gensync";

import type { Handler } from "gensync";
import gensync, { type Handler } from "gensync";
import { once } from "../gensync-utils/functional";

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

Expand Down Expand Up @@ -123,35 +122,22 @@ export function createUncachedDescriptors(
options: ValidatedOptions,
alias: string,
): OptionsAndDescriptors {
// The returned result here is cached to represent a config object in
// memory, so we build and memoize the descriptors to ensure the same
// values are returned consistently.
let plugins: UnloadedDescriptor[];
let presets: UnloadedDescriptor[];

return {
options: optionsWithResolvedBrowserslistConfigFile(options, dirname),
*plugins() {
if (!plugins) {
plugins = yield* createPluginDescriptors(
options.plugins || [],
dirname,
alias,
);
}
return plugins;
},
*presets() {
if (!presets) {
presets = yield* createPresetDescriptors(
options.presets || [],
dirname,
alias,
!!options.passPerPreset,
);
}
return presets;
},
// The returned result here is cached to represent a config object in
// memory, so we build and memoize the descriptors to ensure the same
// values are returned consistently.
plugins: once(() =>
createPluginDescriptors(options.plugins || [], dirname, alias),
),
presets: once(() =>
createPresetDescriptors(
options.presets || [],
dirname,
alias,
!!options.passPerPreset,
),
),
};
}

Expand Down
31 changes: 31 additions & 0 deletions packages/babel-core/src/gensync-utils/functional.ts
@@ -0,0 +1,31 @@
import type { Handler } from "gensync";

import { isAsync, waitFor } from "./async";

export function once<R>(fn: () => Handler<R>): () => Handler<R> {
let result: R;
let resultP: Promise<R>;
return function* () {
if (result) return result;
if (!(yield* isAsync())) return (result = yield* fn());
if (resultP) return yield* waitFor(resultP);

let resolve: (result: R) => void, reject: (error: unknown) => void;
resultP = new Promise((res, rej) => {
resolve = res;
reject = rej;
});

try {
result = yield* fn();
// Avoid keeping the promise around
// now that we have the result.
resultP = null;
resolve(result);
return result;
} catch (error) {
reject(error);
throw error;
}
};
}

0 comments on commit c45da3d

Please sign in to comment.