Skip to content
Closed
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions src/runtime/api/JSBundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ pub mod js_bundler {
pub autoload_bunfig: bool,
pub autoload_tsconfig: bool,
pub autoload_package_json: bool,
pub sourcemap_sidecar: bool,
}

impl Default for CompileOptions {
Expand All @@ -264,6 +265,7 @@ pub mod js_bundler {
autoload_bunfig: true,
autoload_tsconfig: false,
autoload_package_json: false,
sourcemap_sidecar: false,
}
}
}
Expand Down Expand Up @@ -1202,6 +1204,9 @@ pub mod js_bundler {
// When using --compile, only `external` sourcemaps work, as we do not
// look at the source map comment. Override any other sourcemap type.
if this.source_map != options::SourceMapOption::None {
// Only write a .map sidecar when the user asked for it.
compile.sourcemap_sidecar =
this.source_map == options::SourceMapOption::External;
this.source_map = options::SourceMapOption::External;
}

Expand Down
1 change: 1 addition & 0 deletions src/runtime/api/js_bundle_completion_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ impl JSBundleCompletionTask {
let keep_this = if i == entry_point_index {
true
} else if matches!(result, CompileResult::Success)
&& compile_options.sourcemap_sidecar
&& output_files[i].output_kind == OutputKind::Sourcemap
&& matches!(output_files[i].value, OutputFileValue::Buffer { .. })
{
Expand Down
7 changes: 5 additions & 2 deletions src/runtime/cli/build_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ impl BuildCommand {

this_transpiler.options.bytecode = ctx.bundler_options.bytecode;
let mut was_renamed_from_index = false;
let mut compile_sourcemap_sidecar = false;

if ctx.bundler_options.compile {
if ctx.bundler_options.transform_only {
Expand Down Expand Up @@ -310,6 +311,9 @@ impl BuildCommand {
// override it. (Standalone HTML builds keep the user's choice
// because browsers do read the comment.)
if this_transpiler.options.source_map != options::SourceMapOption::None {
// Only write a .map sidecar when the user asked for it.
compile_sourcemap_sidecar =
this_transpiler.options.source_map == options::SourceMapOption::External;
this_transpiler.options.source_map = options::SourceMapOption::External;
}

Expand Down Expand Up @@ -580,7 +584,6 @@ impl BuildCommand {
let opt_minify_syntax = this_transpiler.options.minify_syntax;
let opt_public_path: Box<[u8]> = this_transpiler.options.public_path.clone();
let opt_output_format = this_transpiler.options.output_format;
let opt_source_map = this_transpiler.options.source_map;
let opt_transform_only = this_transpiler.options.transform_only;
let env_ptr = this_transpiler.env;

Expand Down Expand Up @@ -931,7 +934,7 @@ impl BuildCommand {

// Write external sourcemap files next to the compiled executable.
// With --splitting, there can be multiple .map files (one per chunk).
if opt_source_map == options::SourceMapOption::External {
if compile_sourcemap_sidecar {
for f in output_files.iter() {
if f.output_kind == options::OutputKind::Sourcemap
&& matches!(f.value, options::OutputFileValue::Buffer { .. })
Expand Down
100 changes: 100 additions & 0 deletions test/bundler/bun-build-compile-sourcemap.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test";
import { readdirSync } from "fs";
import { bunEnv, bunExe, tempDir } from "harness";
import { join } from "path";

Expand Down Expand Up @@ -210,6 +211,105 @@ export function greet() {
expect(exitCode).toBe(0);
});

// https://github.com/oven-sh/bun/issues/8341
// `--compile --sourcemap[=inline|linked]` used to write an <entry>.js.map
// sidecar even though the map is embedded in the executable. Only
// `--sourcemap=external` should emit a sidecar.
describe.each([
[true, false],
["linked", false],
["inline", false],
["external", true],
] as const)("JS API: compile with sourcemap: %p", (sourcemap, expectSidecar) => {
test(expectSidecar ? "writes a .map sidecar" : "does not write a .map sidecar", async () => {
using dir = tempDir(`build-compile-jsapi-sourcemap-${sourcemap}`, {
"thr.ts": 'function f() {\n throw new Error("x");\n}\nf();\n',
});
const outfile = join(String(dir), "app");

const result = await Bun.build({
entrypoints: [join(String(dir), "thr.ts")],
compile: { outfile },
sourcemap,
});
expect(result.success).toBe(true);

const mapFiles = readdirSync(String(dir)).filter(name => name.endsWith(".map"));
const sourcemapOutputs = result.outputs.filter(o => o.kind === "sourcemap");
if (expectSidecar) {
expect(mapFiles.length).toBe(1);
expect(sourcemapOutputs.length).toBe(1);
const map = JSON.parse(await Bun.file(join(String(dir), mapFiles[0])).text());
expect(map.version).toBe(3);
} else {
expect(mapFiles).toEqual([]);
expect(sourcemapOutputs).toEqual([]);
}

const exe = process.platform === "win32" ? `${outfile}.exe` : outfile;
await using run = Bun.spawn({
cmd: [exe],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [, runStderr, runExit] = await Promise.all([run.stdout.text(), run.stderr.text(), run.exited]);
expect(runStderr).toContain("thr.ts");
expect(runStderr).not.toMatch(/(\$bunfs|~BUN)\/root\//);
expect(runExit).not.toBe(0);
});
});

describe.each([
["--sourcemap", false],
["--sourcemap=linked", false],
["--sourcemap=inline", false],
["--sourcemap=external", true],
])("CLI: --compile %s", (flag, expectSidecar) => {
test.concurrent(expectSidecar ? "writes a .map sidecar" : "does not write a .map sidecar", async () => {
using dir = tempDir(`build-compile-cli-sourcemap-${flag.replace(/[^a-z]/gi, "")}`, {
"thr.ts": 'function f() {\n throw new Error("x");\n}\nf();\n',
});
const outfile = join(String(dir), "app");

await using build = Bun.spawn({
cmd: [bunExe(), "build", "--compile", "thr.ts", "--outfile", outfile, flag],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [, buildStderr, buildExit] = await Promise.all([build.stdout.text(), build.stderr.text(), build.exited]);
expect(buildStderr).toBe("");
expect(buildExit).toBe(0);

const mapFiles = readdirSync(String(dir)).filter(name => name.endsWith(".map"));
if (expectSidecar) {
expect(mapFiles.length).toBe(1);
const map = JSON.parse(await Bun.file(join(String(dir), mapFiles[0])).text());
expect(map.version).toBe(3);
} else {
expect(mapFiles).toEqual([]);
}

// The embedded sourcemap should resolve the stack regardless of
// whether a sidecar was written.
const exe = process.platform === "win32" ? `${outfile}.exe` : outfile;
await using run = Bun.spawn({
cmd: [exe],
env: bunEnv,
cwd: String(dir),
stdout: "pipe",
stderr: "pipe",
});
const [, runStderr, runExit] = await Promise.all([run.stdout.text(), run.stderr.text(), run.exited]);
expect(runStderr).toContain("thr.ts");
expect(runStderr).not.toMatch(/(\$bunfs|~BUN)\/root\//);
expect(runExit).not.toBe(0);
});
});

test("compile with --outfile subdir/myapp writes .map next to executable", async () => {
using dir = tempDir("build-compile-sourcemap-outfile-subdir", helperFiles);

Expand Down
Loading