Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
9068887824 fix(bundler): use root option for filename comments in output
The filename comments in bundler output (e.g., `// src/entry.js`) were
computed relative to cwd instead of the configured `root` option. This
caused inconsistent output when running builds from different directories.

Changed `pathWithPrettyInitialized` and related path computations to
use `root_dir` when set, falling back to `top_level_dir` (cwd).

Fixes #3039

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 01:04:49 +00:00
3 changed files with 72 additions and 3 deletions

View File

@@ -53,7 +53,8 @@ pub const LinkerContext = struct {
}
pub fn pathWithPrettyInitialized(this: *LinkerContext, path: Fs.Path) !Fs.Path {
return bundler.genericPathWithPrettyInitialized(path, this.options.target, this.resolver.fs.top_level_dir, this.allocator());
const root_dir = if (this.options.root_dir.len > 0) this.options.root_dir else this.resolver.fs.top_level_dir;
return bundler.genericPathWithPrettyInitialized(path, this.options.target, root_dir, this.allocator());
}
pub const LinkerOptions = struct {
@@ -75,6 +76,7 @@ pub const LinkerContext = struct {
mode: Mode = .bundle,
public_path: []const u8 = "",
root_dir: []const u8 = "",
pub const Mode = enum {
passthrough,

View File

@@ -688,7 +688,8 @@ pub const BundleV2 = struct {
if (path.pretty.ptr == path.text.ptr) {
// TODO: outbase
const rel = bun.path.relativePlatform(transpiler.fs.top_level_dir, path.text, .loose, false);
const root_dir = if (transpiler.options.root_dir.len > 0) transpiler.options.root_dir else transpiler.fs.top_level_dir;
const rel = bun.path.relativePlatform(root_dir, path.text, .loose, false);
path.pretty = bun.handleOom(this.allocator().dupe(u8, rel));
}
path.assertPrettyIsValid();
@@ -972,6 +973,7 @@ pub const BundleV2 = struct {
this.linker.options.output_format = transpiler.options.output_format;
this.linker.options.generate_bytecode_cache = transpiler.options.bytecode;
this.linker.options.metafile = transpiler.options.metafile;
this.linker.options.root_dir = transpiler.options.root_dir;
this.linker.dev_server = transpiler.options.dev_server;
@@ -3138,7 +3140,8 @@ pub const BundleV2 = struct {
}
fn pathWithPrettyInitialized(this: *BundleV2, path: Fs.Path, target: options.Target) !Fs.Path {
return genericPathWithPrettyInitialized(path, target, this.transpiler.fs.top_level_dir, this.allocator());
const root_dir = if (this.transpiler.options.root_dir.len > 0) this.transpiler.options.root_dir else this.transpiler.fs.top_level_dir;
return genericPathWithPrettyInitialized(path, target, root_dir, this.allocator());
}
fn reserveSourceIndexesForBake(this: *BundleV2) !void {

View File

@@ -0,0 +1,64 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
// Issue #3039: Filename comments in bundler output (e.g., `// src/entry.js`)
// should be relative to the configured `root` option, not the current working directory.
test("bundler output comments should be relative to root, not cwd", async () => {
using dir = tempDir("bundler-root-3039", {
"src/entry.js": `export const hello = "world";`,
"subdir/.gitkeep": "",
});
// Create build script with absolute paths baked in
const buildScript = `
const result = await Bun.build({
entrypoints: ["${String(dir)}/src/entry.js"],
root: "${String(dir)}",
minify: false,
});
console.log(await result.outputs[0].text());
`;
await Bun.write(String(dir) + "/build.js", buildScript);
// Run from root directory
await using procRoot = Bun.spawn({
cmd: [bunExe(), "build.js"],
cwd: String(dir),
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdoutRoot, stderrRoot, exitCodeRoot] = await Promise.all([
procRoot.stdout.text(),
procRoot.stderr.text(),
procRoot.exited,
]);
expect(stderrRoot).toBe("");
expect(stdoutRoot).toContain("// src/entry.js");
expect(exitCodeRoot).toBe(0);
// Run from subdir - should have the same output comment since root is set
await using procSubdir = Bun.spawn({
cmd: [bunExe(), "../build.js"],
cwd: String(dir) + "/subdir",
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [stdoutSubdir, stderrSubdir, exitCodeSubdir] = await Promise.all([
procSubdir.stdout.text(),
procSubdir.stderr.text(),
procSubdir.exited,
]);
expect(stderrSubdir).toBe("");
// The key assertion: comment should be relative to root, not cwd
// Before fix: would show "// ../src/entry.js" when run from subdir
// After fix: should show "// src/entry.js" in both cases
expect(stdoutSubdir).toContain("// src/entry.js");
expect(stdoutSubdir).not.toContain("// ../src/entry.js");
expect(exitCodeSubdir).toBe(0);
});