Skip to content
Open
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
7 changes: 6 additions & 1 deletion src/js/internal/bake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,11 @@ async function start() {
// An empty one is no hostname: `Bun.serve()` would listen on every interface.
const exportedHostname = (exported?.hostname ?? exported?.host) || undefined;
const exportedPort = exported?.port;
const exportedUnix = exported?.unix;
// `null`, `false` and an empty one are no socket: `Bun.serve()` would listen on every interface.
const hasUnix = exportedUnix != null && exportedUnix !== false && exportedUnix !== "";
Comment thread
robobun marked this conversation as resolved.
Resolved
// A `unix` socket in the export is kept unless a flag asks for a TCP address.
const useUnix = exported?.unix !== undefined && flags.hostname === undefined && flags.port === undefined;
const useUnix = hasUnix && flags.hostname === undefined && flags.port === undefined;
let server: Server<undefined>;
try {
server = serveOnFreePort(
Expand All @@ -475,6 +478,8 @@ async function start() {
own("hostname", undefined);
own("host", undefined);
own("port", undefined);
// The value that was tested: a second read by `Bun.serve()` can return another one.
own("unix", exportedUnix);
} else {
own("unix", undefined);
own("hostname", flags.hostname ?? exportedHostname ?? "localhost");
Expand Down
65 changes: 63 additions & 2 deletions test/cli/bake/bake.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ async function get(url: string, path: string) {
return `${res.status} ${await res.text()}`;
}

function spawnBake(dir: string, args: string[]) {
function spawnBake(dir: string, args: string[], extraEnv?: Record<string, string>) {
return Bun.spawn({
cmd: [bunExe(), ...args],
env,
env: { ...env, ...extraEnv },
cwd: dir,
// Some fixtures catch SIGTERM, so the cleanup of `await using` would wait for an exit that never comes.
killSignal: "SIGKILL",
Expand Down Expand Up @@ -689,6 +689,67 @@ test.concurrent("bun bake listens on localhost when the export of app/server.ts
expect(new URL(url).hostname).toBe("localhost");
});

// `Bun.serve()` reads none of these as a socket. Then `bun bake` must listen where it does without `unix`.
test.concurrent.each([
["unix: null", ["127.0.0.1", "::1"]],
[`unix: ""`, ["127.0.0.1", "::1"]],
["unix: false", ["127.0.0.1", "::1"]],
[`unix: "", hostname: "127.0.0.1"`, ["127.0.0.1"]],
])("bun bake does not listen on every interface when app/server.ts exports %s", async (options, loopback) => {
using dir = tempDir("bake-command-no-unix", {
"app/server.ts": `export default { ${options}, fetch: (_request, server) => Response.json(server.address) };`,
});

// No `--port` and no `--host`: with a flag, `bun bake` does not use the `unix` of the export.
await using proc = spawnBake(String(dir), ["bake"], { PORT: "0" });
const url = await getServerUrl(proc);

// The banner is no proof: it says localhost when the server listens on every interface.
const { address } = await (await fetch(url)).json();
expect(loopback).toContain(address);
});

// Bun.serve's `unix` option takes a socket path, which this test only makes on POSIX.
test.concurrent.skipIf(isWindows)("bun bake reads the unix socket of the export once", async () => {
using dir = tempDir("bake-unix-once", {});
const socket = join(String(dir), "s.sock");
await Bun.write(
join(String(dir), "app/server.ts"),
`
let reads = 0;
export default {
get unix() { reads++; return ${JSON.stringify(socket)}; },
fetch: () => new Response(String(reads)),
};
`,
);

await using proc = spawnBake(String(dir), ["bake"]);
const output = collectOutput(proc);
await output.waitFor(/url: unix:/);

// A second read is `Bun.serve()` reading the export again, after `bun bake` chose the socket.
const res = await fetch("http://localhost/", { unix: socket });
expect(await res.text()).toBe("1");
});

// The flag comes before the socket of the export, so no socket is made, on any platform.
test.concurrent.each(["--port=0", "--host=localhost"])(
"bun bake %s serves TCP when app/server.ts exports a unix socket",
async flag => {
using dir = tempDir("bake-command-unix-flag", {
"app/server.ts": `export default { unix: "s.sock", fetch: () => new Response("ok") };`,
});

await using proc = spawnBake(String(dir), ["bake", flag], { PORT: "0" });
const output = collectOutput(proc);
const [, url] = await output.waitFor(/url: (\S+)/);

expect(url).toStartWith("http://");
expect(await get(url, "/")).toBe("200 ok");
},
);

test.concurrent("bun bake runs a plugin function from bake.config.ts, with the project as its root", async () => {
using dir = tempDir("bake-command-config-function", {
"bake.config.ts": `
Expand Down
Loading