fix(replay): preserve CommonJS object as ESM default export (#519)

Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Rudy Mizrahi Celekli 2026-10-05 03:05:43 -04:00 committed by GitHub
parent 4200a425d5
commit 2df6b51ebb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 106 additions and 4 deletions

View File

@ -226,7 +226,7 @@ const loadEntry = async (
function () {
this.setExport("default", exports);
for (const name of Object.keys(exports))
this.setExport(name, exports[name]);
if (name !== "default") this.setExport(name, exports[name]);
},
{ context, identifier: `rea:${alias}` },
);

View File

@ -1,9 +1,12 @@
import { spawn } from "node:child_process";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { execFile, spawn } from "node:child_process";
import { readFile, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { promisify } from "node:util";
import { describe, expect, it } from "vitest";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
import type { WorkerModule } from "../../../../src/replay/JavaScriptReplayWorkerTypes.js";
const fixture = async (name: string): Promise<string> =>
@ -228,6 +231,105 @@ describe("disposable JavaScript replay worker", () => {
});
});
describe("ESM imports of CommonJS replay factories", () => {
it.each([false, true])(
"matches native Node default import when __esModule is %s",
async (marked) => {
const directory = await createTestTempDirectory("rea-replay-interop-");
const source = `module.exports={default:'named-default',answer:42,__esModule:${String(marked)}};`;
const path = join(directory, "dependency.cjs");
await writeFile(path, source, "utf8");
const { stdout } = await promisify(execFile)(
process.execPath,
[
"--input-type=module",
"-e",
"const {default:value}=await import(process.argv[1]); console.log(JSON.stringify({kind:typeof value,property:value.default,answer:value.answer}));",
pathToFileURL(path).href,
],
{ timeout: 5000 },
);
const expected: unknown = JSON.parse(stdout);
expect(expected).toEqual({
kind: "object",
property: "named-default",
answer: 42,
});
const workerRequest = await request("parser.mjs", "esm", "default", []);
workerRequest.left.modules = [
{
alias: "entry",
format: "esm",
dependencies: { "./dependency": "dependency" },
source:
"import value from './dependency'; export default function(){ return {kind:typeof value,property:value.default,answer:value.answer}; }",
},
{
alias: "dependency",
format: "commonjs-factory",
dependencies: {},
source: `function(module){ ${source} }`,
},
];
const result = await runWorker(workerRequest);
expect(result.code).toBe(0);
expect(JSON.parse(result.stdout)).toMatchObject({
left: [{ outcome: "return", value: expected }],
});
},
);
it.each([false, true])(
"preserves Rspack require.n behavior when __esModule is %s",
async (marked) => {
const workerRequest = await request(
"clipboard.factory.txt",
"commonjs-factory",
"default",
[],
);
workerRequest.left.modules = [
{
alias: "entry",
format: "commonjs-factory",
dependencies: { "./dependency": "dependency" },
source:
"function(module,exports,require){ const normalized=require.n(require('./dependency'))(); module.exports.default=()=>typeof normalized; }",
},
{
alias: "dependency",
format: "commonjs-factory",
dependencies: {},
source: `function(module){ module.exports={default:'named-default',answer:42,__esModule:${String(marked)}}; }`,
},
];
const result = await runWorker(workerRequest);
expect(JSON.parse(result.stdout)).toMatchObject({
left: [{ outcome: "return", value: marked ? "string" : "object" }],
});
},
);
it("preserves direct factory default-export selection", async () => {
const workerRequest = await request(
"clipboard.factory.txt",
"commonjs-factory",
"default",
[],
);
workerRequest.left.modules[0] = {
alias: "entry",
format: "commonjs-factory",
dependencies: {},
source: "function(module){ module.exports={default:()=>42}; }",
};
const result = await runWorker(workerRequest);
expect(JSON.parse(result.stdout)).toMatchObject({
left: [{ outcome: "return", value: 42 }],
});
});
});
describe("replay ESM graph linking", () => {
const esmModule = (
alias: string,