Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion test/js/bun/resolve/concurrent-dynamic-import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { bunEnv, bunExe, tempDir } from "harness";
// each call gets its own embedder fetch promise (the registry entry is created
// only after the first fetch settles), so the loser of that race must still be
// resolved by Bun__onFulfillAsyncModule rather than left pending forever.
test("concurrent dynamic imports of the same module both resolve", async () => {
test.concurrent("concurrent dynamic imports of the same module both resolve", async () => {
using dir = tempDir("concurrent-dyn-import", {
"shared.ts": `export const heavy = "H";`,
"modules.ts": `import { heavy } from "./shared";\nexport const lazy = heavy + "-lazy";`,
Expand All @@ -30,3 +30,41 @@ test("concurrent dynamic imports of the same module both resolve", async () => {
expect(stdout.trim()).toBe("ok");
expect(exitCode).toBe(0);
});

// A top-level dynamic import whose fetch rejects with a non-Error value (a
// single-message transpile failure is a BuildMessage, not an ErrorInstance)
// must record a fetch failure on its registry entry. If it is recorded as an
// evaluation error instead, the entry is left with no fetch/module/load
// promise, and every later importer of the same file that goes through
// hostLoadImportedModule parks forever on a promise nothing settles.
test.concurrent("a failed dynamic import does not strand later static importers of the same file", async () => {
using dir = tempDir("dyn-import-fetch-error", {
// Exactly one parser error so the rejection value is a BuildMessage
// rather than an AggregateError (which is an ErrorInstance).
"bad.ts": `import {\n`,
"other.ts": `import "./bad.ts";\nconsole.log("other loaded");\n`,
"entry.mjs": `
const d = import.meta.dir;
const names = [];
try { await import(d + "/bad.ts"); names.push("resolved"); } catch (e) { names.push(e?.name); }
try { await import(d + "/other.ts"); names.push("resolved"); } catch (e) { names.push(e?.name); }
console.log(JSON.stringify(names));
`,
});

await using proc = Bun.spawn({
cmd: [bunExe(), "entry.mjs"],
cwd: String(dir),
env: bunEnv,
stdio: ["ignore", "pipe", "pipe"],
});
// Without the fix the second import() never settles, so the child never
// exits and this test times out.
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect({ stdout: stdout.trim(), stderr, exitCode, signalCode: proc.signalCode }).toEqual({
stdout: JSON.stringify(["BuildMessage", "BuildMessage"]),
stderr: "",
exitCode: 0,
signalCode: null,
});
});
Loading