diff --git a/src/js/builtins/CommonJS.ts b/src/js/builtins/CommonJS.ts index 4fe14e23eb..3835835d9f 100644 --- a/src/js/builtins/CommonJS.ts +++ b/src/js/builtins/CommonJS.ts @@ -215,6 +215,8 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) { if (state < $ModuleLink && $isPromise(fetch)) { // This will probably never happen, but just in case if (($getPromiseInternalField(fetch, $promiseFieldFlags) & $promiseStateMask) === $promiseStatePending) { + registry.$delete(resolvedSpecifier); + throw new TypeError(`require() async module "${key}" is unsupported. use "await import()" instead.`); } @@ -231,6 +233,8 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) { let state = flags & $promiseStateMask; // this branch should never happen, but just to be safe if (state === $promiseStatePending || (reactionsOrResult && $isPromise(reactionsOrResult))) { + registry.$delete(resolvedSpecifier); + throw new TypeError(`require() async module "${key}" is unsupported. use "await import()" instead.`); } else if (state === $promiseStateRejected) { if (!reactionsOrResult?.message) { @@ -282,6 +286,8 @@ export function loadEsmIntoCjs(resolvedSpecifier: string) { var linkAndEvaluateResult = loader.linkAndEvaluateModule(resolvedSpecifier, undefined); if (linkAndEvaluateResult && $isPromise(linkAndEvaluateResult)) { + registry.$delete(resolvedSpecifier); + // if you use top-level await, or any dependencies use top-level await, then we throw here // this means the module will still actually load eventually, but that's okay. throw new TypeError( diff --git a/test/regression/issue/24387.test.ts b/test/regression/issue/24387.test.ts new file mode 100644 index 0000000000..388c3c2ab5 --- /dev/null +++ b/test/regression/issue/24387.test.ts @@ -0,0 +1,23 @@ +import { bunEnv, bunExe } from "harness"; +import { join } from "path"; + +test("regression: require()ing a module with TLA should error and then wipe the module cache, so that importing it again works", async () => { + const proc = Bun.spawn({ + cmd: [bunExe(), "run", "--smol", join(import.meta.dir, "24387", "entry.js")], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const { stdout, stderr } = proc; + + expect(await stderr.text()).toBe(""); + expect(await stdout.text()).toMatchInlineSnapshot(` + "require() async module "" is unsupported. use "await import()" instead. + Module { + foo: 67, + } + " + `); + expect(await proc.exited).toBe(0); +}); diff --git a/test/regression/issue/24387/entry.js b/test/regression/issue/24387/entry.js new file mode 100644 index 0000000000..dccd224e36 --- /dev/null +++ b/test/regression/issue/24387/entry.js @@ -0,0 +1,12 @@ +// @bun +var { require } = import.meta; +const entrypointPath = "./p.js"; +let listener; +try { + listener = await require(entrypointPath); +} catch (e) { + console.log(e.message.replace(require.resolve(entrypointPath), "")); + listener = await import(entrypointPath); +} +for (let i = 0; i < 5; i++) if (listener.default) listener = listener.default; +console.log(listener); diff --git a/test/regression/issue/24387/p.js b/test/regression/issue/24387/p.js new file mode 100644 index 0000000000..5677921673 --- /dev/null +++ b/test/regression/issue/24387/p.js @@ -0,0 +1,2 @@ +await new Promise(resolve => setTimeout(resolve, 100)); +export const foo = 67;