Compare commits

...

4 Commits

Author SHA1 Message Date
Claude Bot
ff24fe3f94 test: use using for server cleanup and resolve map URL against script URL
Use `using server = Bun.serve(...)` in fixture scripts for automatic
cleanup on error. Resolve source map URL against jsResp.url instead
of server.url for correctness with subpath routes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-11 09:01:39 +00:00
Claude Bot
feb6480ebb ci: retrigger build
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-11 08:48:45 +00:00
Claude Bot
74a5f6a131 test: strengthen source map regression test assertions
Add explicit assertions that JS bundles are served (200 + non-empty)
in both test cases to prevent false passes. In the development mode
test, also fetch the actual .map URL to verify it returns valid
source map JSON.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-11 08:46:46 +00:00
Claude Bot
c0f3340883 fix(html): disable source maps in production mode for HTML bundles
Source maps were unconditionally set to `.linked` in HTMLBundle.zig,
causing `//# sourceMappingURL=...` to be appended to JS output and
`.map` files to be publicly accessible even with `development: false`.

Closes #28001

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-11 08:33:36 +00:00
2 changed files with 156 additions and 1 deletions

View File

@@ -286,7 +286,7 @@ pub const Route = struct {
config.force_node_env = .development;
config.jsx.development = true;
}
config.source_map = .linked;
config.source_map = if (is_development) .linked else .none;
const completion_task = try bun.BundleV2.createAndScheduleCompletionTask(
config,
@@ -360,6 +360,9 @@ pub const Route = struct {
// Create static routes for each output file
for (output_files) |*output_file| {
// Don't serve source map files in production mode.
if (!server.config().isDevelopment() and output_file.output_kind == .sourcemap)
continue;
const blob = jsc.WebCore.Blob.Any{ .Blob = bun.handleOom(output_file.toBlob(bun.default_allocator, globalThis)) };
var headers = bun.http.Headers{ .allocator = bun.default_allocator };
const content_type = blob.Blob.contentTypeOrMimeType() orelse brk: {

View File

@@ -0,0 +1,152 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
import { join } from "path";
// https://github.com/oven-sh/bun/issues/28001
test("source maps should not be served in production mode", async () => {
using dir = tempDir("issue-28001", {
"index.html": `<!doctype html>
<title>Source map test</title>
<script type="module" src="script.js"></script>`,
"script.js": `blah()
function blah () {
let something = 'yes'
console.log(something)
}`,
"server.js": `
import homepage from './index.html'
using server = Bun.serve({
port: 0,
routes: { '/': homepage },
development: false
})
// Wait for bundle to be ready
const resp = await fetch(server.url);
const htmlText = await resp.text();
const jsSrc = htmlText.match(/<script[^>]+src="([^"]+)"/)?.[1];
const jsResp = await fetch(new URL(jsSrc, server.url));
const jsText = await jsResp.text();
const mapUrl = jsSrc.replace(/\\.js$/, ".js.map");
const mapResp = await fetch(new URL(mapUrl, server.url));
console.log(JSON.stringify({
jsStatus: jsResp.status,
jsLength: jsText.length,
hasSourceMappingURL: jsText.includes("sourceMappingURL"),
sourceMapHeader: jsResp.headers.get("sourcemap"),
mapStatus: mapResp.status,
}));
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), join(String(dir), "server.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]);
const result = JSON.parse(stdout.trim());
// Verify the JS bundle was actually served
expect(result.jsStatus).toBe(200);
expect(result.jsLength).toBeGreaterThan(0);
// In production mode, the JS should NOT contain a sourceMappingURL
expect(result.hasSourceMappingURL).toBe(false);
// The SourceMap header should not be present
expect(result.sourceMapHeader).toBeNull();
// Source map file should not be accessible
expect(result.mapStatus).toBe(404);
expect(exitCode).toBe(0);
});
test("source maps should still be served in development mode", async () => {
using dir = tempDir("issue-28001-dev", {
"index.html": `<!doctype html>
<title>Source map test</title>
<script type="module" src="script.js"></script>`,
"script.js": `blah()
function blah () {
let something = 'yes'
console.log(something)
}`,
"server.js": `
import homepage from './index.html'
using server = Bun.serve({
port: 0,
routes: { '/': homepage },
development: { hmr: false }
})
// Wait for bundle to be ready
const resp = await fetch(server.url);
const htmlText = await resp.text();
const jsSrc = htmlText.match(/<script[^>]+src="([^"]+)"/)?.[1];
const jsResp = await fetch(new URL(jsSrc, server.url));
const jsText = await jsResp.text();
// Fetch the source map to verify it's actually served
const sourceMapHeader = jsResp.headers.get("sourcemap");
let mapStatus = 0;
let mapHasVersion = false;
if (sourceMapHeader) {
const mapResp = await fetch(new URL(sourceMapHeader, jsResp.url));
mapStatus = mapResp.status;
if (mapResp.ok) {
const mapJson = await mapResp.json();
mapHasVersion = mapJson.version === 3;
}
}
console.log(JSON.stringify({
jsStatus: jsResp.status,
jsLength: jsText.length,
hasSourceMappingURL: jsText.includes("sourceMappingURL"),
sourceMapHeader,
mapStatus,
mapHasVersion,
}));
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), join(String(dir), "server.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]);
const result = JSON.parse(stdout.trim());
// Verify the JS bundle was actually served
expect(result.jsStatus).toBe(200);
expect(result.jsLength).toBeGreaterThan(0);
// In development mode, the JS SHOULD contain a sourceMappingURL
expect(result.hasSourceMappingURL).toBe(true);
// The SourceMap header should be present
expect(result.sourceMapHeader).not.toBeNull();
// The source map file should be accessible and valid
expect(result.mapStatus).toBe(200);
expect(result.mapHasVersion).toBe(true);
expect(exitCode).toBe(0);
});