-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Fix Bun.serve to respect development flag for sourcemap generation #24542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| import type { Subprocess } from "bun"; | ||
| import { describe, expect, test } from "bun:test"; | ||
| import { bunEnv, bunExe, tempDirWithFiles } from "harness"; | ||
| import { join } from "path"; | ||
|
|
||
| describe("Bun.serve sourcemap generation", () => { | ||
| test("development: false should not include sourcemap comments in JS", async () => { | ||
| const dir = tempDirWithFiles("html-no-sourcemaps", { | ||
| "index.html": /*html*/ ` | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Production Build</title> | ||
| <script type="module" src="script.js"></script> | ||
| </head> | ||
| <body> | ||
| <h1>Production</h1> | ||
| </body> | ||
| </html> | ||
| `, | ||
| "script.js": /*js*/ ` | ||
| console.log("Hello from production"); | ||
| const foo = () => { | ||
| return "bar"; | ||
| }; | ||
| foo(); | ||
| `, | ||
| }); | ||
|
|
||
| const { subprocess, port, hostname } = await waitForProductionServer(dir, { | ||
| "/": join(dir, "index.html"), | ||
| }); | ||
| await using server = subprocess; | ||
|
|
||
| const html = await (await fetch(`http://${hostname}:${port}/`)).text(); | ||
| const jsSrc = html.match(/<script type="module" crossorigin src="([^"]+)"/)?.[1]; | ||
|
|
||
| if (!jsSrc) { | ||
| throw new Error("No script src found in HTML"); | ||
| } | ||
|
|
||
| const response = await fetch(new URL(jsSrc, `http://${hostname}:${port}`)); | ||
| const js = await response.text(); | ||
| const headers = response.headers; | ||
|
|
||
| // Should NOT contain sourceMappingURL comment | ||
| expect(js).not.toContain("sourceMappingURL"); | ||
|
|
||
| // Should NOT have SourceMap header | ||
| expect(headers.get("SourceMap")).toBeNull(); | ||
| }); | ||
|
|
||
| test("development: true should include sourcemap comments in JS", async () => { | ||
| const dir = tempDirWithFiles("html-with-sourcemaps", { | ||
| "index.html": /*html*/ ` | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Development Build</title> | ||
| <script type="module" src="script.js"></script> | ||
| </head> | ||
| <body> | ||
| <h1>Development</h1> | ||
| </body> | ||
| </html> | ||
| `, | ||
| "script.js": /*js*/ ` | ||
| console.log("Hello from development"); | ||
| const foo = () => { | ||
| return "bar"; | ||
| }; | ||
| foo(); | ||
| `, | ||
| }); | ||
|
|
||
| const { subprocess, port, hostname } = await waitForDevelopmentServer(dir, { | ||
| "/": join(dir, "index.html"), | ||
| }); | ||
| await using server = subprocess; | ||
|
|
||
| const html = await (await fetch(`http://${hostname}:${port}/`)).text(); | ||
| const jsSrc = html.match(/<script type="module" crossorigin src="([^"]+)"/)?.[1]; | ||
|
|
||
| if (!jsSrc) { | ||
| throw new Error("No script src found in HTML"); | ||
| } | ||
|
|
||
| const response = await fetch(new URL(jsSrc, `http://${hostname}:${port}`)); | ||
| const js = await response.text(); | ||
| const headers = response.headers; | ||
|
|
||
| // SHOULD contain sourceMappingURL comment | ||
| expect(js).toContain("sourceMappingURL"); | ||
|
|
||
| // SHOULD have SourceMap header | ||
| expect(headers.get("SourceMap")).toBeTruthy(); | ||
| }); | ||
|
|
||
| test("development: { hmr: false } should not include sourcemap comments", async () => { | ||
| const dir = tempDirWithFiles("html-dev-no-hmr", { | ||
| "index.html": /*html*/ ` | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Development without HMR</title> | ||
| <script type="module" src="script.js"></script> | ||
| </head> | ||
| <body> | ||
| <h1>Development without HMR</h1> | ||
| </body> | ||
| </html> | ||
| `, | ||
| "script.js": /*js*/ ` | ||
| console.log("Hello from dev no hmr"); | ||
| `, | ||
| }); | ||
|
|
||
| const { subprocess, port, hostname } = await waitForDevNoHMRServer(dir, { | ||
| "/": join(dir, "index.html"), | ||
| }); | ||
| await using server = subprocess; | ||
|
|
||
| const html = await (await fetch(`http://${hostname}:${port}/`)).text(); | ||
| const jsSrc = html.match(/<script type="module" crossorigin src="([^"]+)"/)?.[1]; | ||
|
|
||
| if (!jsSrc) { | ||
| throw new Error("No script src found in HTML"); | ||
| } | ||
|
|
||
| const response = await fetch(new URL(jsSrc, `http://${hostname}:${port}`)); | ||
| const js = await response.text(); | ||
| const headers = response.headers; | ||
|
|
||
| // In development mode (even without HMR), sourcemaps SHOULD be included | ||
| expect(js).toContain("sourceMappingURL"); | ||
| expect(headers.get("SourceMap")).toBeTruthy(); | ||
| }); | ||
| }); | ||
|
|
||
| async function waitForProductionServer( | ||
| dir: string, | ||
| entryPoints: Record<string, string>, | ||
| ): Promise<{ | ||
| subprocess: Subprocess; | ||
| port: number; | ||
| hostname: string; | ||
| }> { | ||
| let defer = Promise.withResolvers<{ | ||
| subprocess: Subprocess; | ||
| port: number; | ||
| hostname: string; | ||
| }>(); | ||
|
|
||
| const process = Bun.spawn({ | ||
| cmd: [bunExe(), join(import.meta.dir, "fixtures", "serve-production.js")], | ||
| env: { | ||
| ...bunEnv, | ||
| NODE_ENV: undefined, | ||
| }, | ||
| cwd: dir, | ||
| stdio: ["inherit", "inherit", "inherit"], | ||
| ipc(message, subprocess) { | ||
| subprocess.send({ | ||
| files: entryPoints, | ||
| }); | ||
| defer.resolve({ | ||
| subprocess, | ||
| port: message.port, | ||
| hostname: message.hostname, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| return defer.promise; | ||
| } | ||
|
|
||
| async function waitForDevelopmentServer( | ||
| dir: string, | ||
| entryPoints: Record<string, string>, | ||
| ): Promise<{ | ||
| subprocess: Subprocess; | ||
| port: number; | ||
| hostname: string; | ||
| }> { | ||
| let defer = Promise.withResolvers<{ | ||
| subprocess: Subprocess; | ||
| port: number; | ||
| hostname: string; | ||
| }>(); | ||
|
|
||
| const process = Bun.spawn({ | ||
| cmd: [bunExe(), join(import.meta.dir, "fixtures", "serve-development.js")], | ||
| env: { | ||
| ...bunEnv, | ||
| NODE_ENV: undefined, | ||
| }, | ||
| cwd: dir, | ||
| stdio: ["inherit", "inherit", "inherit"], | ||
| ipc(message, subprocess) { | ||
| subprocess.send({ | ||
| files: entryPoints, | ||
| }); | ||
| defer.resolve({ | ||
| subprocess, | ||
| port: message.port, | ||
| hostname: message.hostname, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| return defer.promise; | ||
| } | ||
|
|
||
| async function waitForDevNoHMRServer( | ||
| dir: string, | ||
| entryPoints: Record<string, string>, | ||
| ): Promise<{ | ||
| subprocess: Subprocess; | ||
| port: number; | ||
| hostname: string; | ||
| }> { | ||
| let defer = Promise.withResolvers<{ | ||
| subprocess: Subprocess; | ||
| port: number; | ||
| hostname: string; | ||
| }>(); | ||
|
|
||
| const process = Bun.spawn({ | ||
| cmd: [bunExe(), join(import.meta.dir, "fixtures", "serve-dev-no-hmr.js")], | ||
| env: { | ||
| ...bunEnv, | ||
| NODE_ENV: undefined, | ||
| }, | ||
| cwd: dir, | ||
| stdio: ["inherit", "inherit", "inherit"], | ||
| ipc(message, subprocess) { | ||
| subprocess.send({ | ||
| files: entryPoints, | ||
| }); | ||
| defer.resolve({ | ||
| subprocess, | ||
| port: message.port, | ||
| hostname: message.hostname, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| return defer.promise; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| let server = Bun.serve({ | ||
| port: 0, | ||
| development: { | ||
| hmr: false, | ||
| }, | ||
| async fetch(req) { | ||
| return new Response("Hello World", { | ||
| status: 404, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| process.on("message", async message => { | ||
| const files = message.files || {}; | ||
| const routes = {}; | ||
| for (const [key, value] of Object.entries(files)) { | ||
| routes[key] = (await import(value)).default; | ||
| } | ||
|
|
||
| server.reload({ | ||
| static: routes, | ||
| development: { | ||
| hmr: false, | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| process.send({ | ||
| port: server.port, | ||
| hostname: server.hostname, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| let server = Bun.serve({ | ||
| port: 0, | ||
| development: true, | ||
| async fetch(req) { | ||
| return new Response("Hello World", { | ||
| status: 404, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| process.on("message", async message => { | ||
| const files = message.files || {}; | ||
| const routes = {}; | ||
| for (const [key, value] of Object.entries(files)) { | ||
| routes[key] = (await import(value)).default; | ||
| } | ||
|
|
||
| server.reload({ | ||
| static: routes, | ||
| development: true, | ||
| }); | ||
| }); | ||
|
|
||
| process.send({ | ||
| port: server.port, | ||
| hostname: server.hostname, | ||
| }); | ||
|
Comment on lines
+1
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial LGTM! Development server fixture is configured correctly. The fixture properly sets Note: This is the third nearly-identical fixture file. The three fixtures (serve-production.js, serve-development.js, serve-dev-no-hmr.js) differ only in their // fixtures/serve-factory.js
export function createServerFixture(developmentConfig) {
let server = Bun.serve({
port: 0,
development: developmentConfig,
async fetch(req) {
return new Response("Hello World", { status: 404 });
},
});
process.on("message", async message => {
const files = message.files || {};
const routes = {};
for (const [key, value] of Object.entries(files)) {
routes[key] = (await import(value)).default;
}
server.reload({
static: routes,
development: developmentConfig,
});
});
process.send({
port: server.port,
hostname: server.hostname,
});
}🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| let server = Bun.serve({ | ||
| port: 0, | ||
| development: false, | ||
| async fetch(req) { | ||
| return new Response("Hello World", { | ||
| status: 404, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| process.on("message", async message => { | ||
| const files = message.files || {}; | ||
| const routes = {}; | ||
| for (const [key, value] of Object.entries(files)) { | ||
| routes[key] = (await import(value)).default; | ||
| } | ||
|
|
||
| server.reload({ | ||
| static: routes, | ||
| development: false, | ||
| }); | ||
| }); | ||
|
|
||
| process.send({ | ||
| port: server.port, | ||
| hostname: server.hostname, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
LGTM! Dev server with HMR disabled is configured correctly.
Using
development: { hmr: false }correctly tests the scenario where development mode is enabled but HMR is disabled.Note: This fixture file is nearly identical to serve-production.js and serve-development.js. Consider extracting a shared factory function to reduce duplication if more fixtures are added in the future.
🤖 Prompt for AI Agents