mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
## Summary
- Legal comments (`/*! ... */`) were preventing the `module.exports =
require()` redirect optimization from being applied to CommonJS wrapper
modules
- The fix scans all parts to find a single meaningful statement,
skipping comments, directives, and empty statements
- If exactly one such statement exists and matches the `module.exports =
require()` pattern, the redirect optimization is now applied
This fixes an issue where wrapper modules like Express's `index.js`:
```js
/*!
* express
* MIT Licensed
*/
'use strict';
module.exports = require('./lib/express');
```
Were generating unnecessary wrapper functions instead of being
redirected directly to the target module.
## Test plan
- [x] Added regression test in `test/regression/issue/3179.test.ts`
- [x] Verified test fails with system bun and passes with the fix
- [x] Tested manual reproduction scenario
Fixes #3179
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
4.4 KiB
TypeScript
160 lines
4.4 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { bunEnv, bunExe, tempDir } from "harness";
|
|
|
|
// https://github.com/oven-sh/bun/issues/3179
|
|
// Legal comments (/*!...*/) should not prevent the module.exports = require() redirect optimization
|
|
|
|
test("legal comments do not break module.exports = require() redirect", async () => {
|
|
using dir = tempDir("issue-3179", {
|
|
// Wrapper module WITH legal comment
|
|
"with-comment.js": `/*!
|
|
* express
|
|
* MIT Licensed
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
module.exports = require('./lib/express');
|
|
`,
|
|
// Wrapper module WITHOUT legal comment
|
|
"without-comment.js": `'use strict';
|
|
|
|
module.exports = require('./lib/express');
|
|
`,
|
|
// The actual module being wrapped
|
|
"lib/express.js": `function createApp() {
|
|
return { name: 'app' };
|
|
}
|
|
module.exports = createApp;
|
|
`,
|
|
// Entry point using the module with legal comment
|
|
"entry-with.js": `const express = require('./with-comment');
|
|
console.log(express());
|
|
`,
|
|
// Entry point using the module without legal comment
|
|
"entry-without.js": `const express = require('./without-comment');
|
|
console.log(express());
|
|
`,
|
|
});
|
|
|
|
// Bundle both variants
|
|
await using procWith = Bun.spawn({
|
|
cmd: [bunExe(), "build", "entry-with.js", "--outfile=out-with.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
await using procWithout = Bun.spawn({
|
|
cmd: [bunExe(), "build", "entry-without.js", "--outfile=out-without.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdoutWith, stderrWith, exitCodeWith] = await Promise.all([
|
|
procWith.stdout.text(),
|
|
procWith.stderr.text(),
|
|
procWith.exited,
|
|
]);
|
|
|
|
const [stdoutWithout, stderrWithout, exitCodeWithout] = await Promise.all([
|
|
procWithout.stdout.text(),
|
|
procWithout.stderr.text(),
|
|
procWithout.exited,
|
|
]);
|
|
|
|
expect(exitCodeWith).toBe(0);
|
|
expect(exitCodeWithout).toBe(0);
|
|
|
|
// Read the generated bundles
|
|
const outWith = await Bun.file(`${dir}/out-with.js`).text();
|
|
const outWithout = await Bun.file(`${dir}/out-without.js`).text();
|
|
|
|
// Both bundles should have the same number of "// " module comment markers
|
|
// If the redirect optimization is working, neither should create a wrapper function
|
|
const moduleCountWith = (outWith.match(/\/\/ /g) || []).length;
|
|
const moduleCountWithout = (outWithout.match(/\/\/ /g) || []).length;
|
|
|
|
// The key test: both should have the same number of modules bundled
|
|
// Before the fix, "with-comment" would bundle 3 modules instead of 2
|
|
expect(moduleCountWith).toBe(moduleCountWithout);
|
|
|
|
// Both should NOT contain a require_with_comment or require_without_comment wrapper
|
|
expect(outWith).not.toContain("require_with_comment");
|
|
expect(outWithout).not.toContain("require_without_comment");
|
|
});
|
|
|
|
test("legal comment with only module.exports = require()", async () => {
|
|
using dir = tempDir("issue-3179-simple", {
|
|
"wrapper.js": `/*!
|
|
* Legal comment
|
|
*/
|
|
module.exports = require('./target');
|
|
`,
|
|
"target.js": `module.exports = { foo: 'bar' };
|
|
`,
|
|
"entry.js": `const wrapper = require('./wrapper');
|
|
console.log(wrapper.foo);
|
|
`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "build", "entry.js", "--outfile=out.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(exitCode).toBe(0);
|
|
|
|
const out = await Bun.file(`${dir}/out.js`).text();
|
|
|
|
// Should not contain a wrapper function - the redirect optimization should work
|
|
expect(out).not.toContain("require_wrapper");
|
|
});
|
|
|
|
test("multiple legal comments and directives do not break redirect", async () => {
|
|
using dir = tempDir("issue-3179-multiple", {
|
|
"wrapper.js": `/*!
|
|
* First legal comment
|
|
*/
|
|
|
|
/*!
|
|
* Second legal comment
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
module.exports = require('./target');
|
|
`,
|
|
"target.js": `module.exports = function() { return 42; };
|
|
`,
|
|
"entry.js": `const fn = require('./wrapper');
|
|
console.log(fn());
|
|
`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "build", "entry.js", "--outfile=out.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(exitCode).toBe(0);
|
|
|
|
const out = await Bun.file(`${dir}/out.js`).text();
|
|
|
|
// Should not contain a wrapper function
|
|
expect(out).not.toContain("require_wrapper");
|
|
});
|