Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
2c79dde82e fix(plugins): onLoad returning CJS contents works with require()
When a plugin's onLoad handler returned CommonJS-style contents
(module.exports), the transpiler correctly detected CJS and wrapped
the code, but handleVirtualModuleResult always routed through the ESM
loader, ignoring the isCommonJSModule flag. The CJS wrapper function
was loaded as ESM where it was never called, resulting in empty exports.

Now check isCommonJSModule after transpiling virtual module contents
and evaluate through the CJS path when require() is the consumer.

Closes #27799

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-04 19:53:34 +00:00
2 changed files with 57 additions and 0 deletions

View File

@@ -390,6 +390,13 @@ static JSValue handleVirtualModuleResult(
RELEASE_AND_RETURN(scope, reject(JSValue::decode(res->result.err.value)));
}
if (commonJSModule && res->result.value.isCommonJSModule) {
auto specifierString = specifier->toWTFString(BunString::ZeroCopy);
commonJSModule->evaluate(globalObject, specifierString, res->result.value);
RETURN_IF_EXCEPTION(scope, {});
return commonJSModule;
}
auto provider = Zig::SourceProvider::create(globalObject, res->result.value);
return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
}

View File

@@ -0,0 +1,50 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
test("onLoad plugin returning CJS contents works with require()", async () => {
using dir = tempDir("issue-27799", {
"plugin.js": `
Bun.plugin({
name: "cjs-patch",
setup(build) {
build.onLoad({ filter: /tiny-module\\.js$/ }, () => {
return {
loader: "js",
contents: \`
module.exports.greet = function greet(name) { return "patched hello " + name; }
module.exports.add = function add(a, b) { return a + b + a; }
\`,
};
});
},
});
`,
"tiny-module.js": `
module.exports.greet = function greet(name) {
return "hello " + name;
}
module.exports.add = function add(a, b) {
return a + b;
}
`,
"run-demo.js": `
const { greet, add } = require('./tiny-module');
console.log(greet('world'));
console.log(add(1, 2));
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "--preload=./plugin.js", "run-demo.js"],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toBe("patched hello world\n4\n");
expect(stderr).toBe("");
expect(exitCode).toBe(0);
});