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
5 changes: 5 additions & 0 deletions .changeset/sveltekit-dev-server-ready-line.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eve": patch
---

The SvelteKit plugin now proxies `/eve/v1/**` to the URL on eve's `server listening at ...` line. Before, it took the first URL in the dev server's output, so a documentation link in a dependency warning could become the proxy target and serve 404s.
42 changes: 41 additions & 1 deletion packages/eve/src/public/sveltekit/dev-server.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
import { describe, expect, it } from "vitest";

import { normalizeDevServerRegistry } from "./dev-server.js";
import { extractEveDevServerOrigin, normalizeDevServerRegistry } from "./dev-server.js";

describe("extractEveDevServerOrigin", () => {
it("returns undefined when the chunk has no readiness line", () => {
expect(
extractEveDevServerOrigin("resolving rolldown... https://rolldown.rs/guide"),
).toBeUndefined();
});

it("returns the origin from the readiness line", () => {
expect(extractEveDevServerOrigin("[DEV] server listening at http://127.0.0.1:50036/\n")).toBe(
"http://127.0.0.1:50036",
);
});

it("does not depend on the CLI tag", () => {
expect(extractEveDevServerOrigin("server listening at http://127.0.0.1:50036/\n")).toBe(
"http://127.0.0.1:50036",
);
});

it("ignores unrelated URLs that appear before the readiness line", () => {
expect(
extractEveDevServerOrigin(
"warn: https://rolldown.rs/guide\n[DEV] server listening at http://127.0.0.1:50036/\n",
),
).toBe("http://127.0.0.1:50036");
});

it("accepts a chunk that ends at the URL without a trailing newline", () => {
expect(extractEveDevServerOrigin("[DEV] server listening at http://127.0.0.1:50036")).toBe(
"http://127.0.0.1:50036",
);
});

it("accepts an IPv6 loopback origin", () => {
expect(extractEveDevServerOrigin("[DEV] server listening at http://[::1]:50036/")).toBe(
"http://[::1]:50036",
);
});
});

describe("normalizeDevServerRegistry", () => {
it("normalizes a well-formed record and canonicalizes the origin", () => {
Expand Down
14 changes: 10 additions & 4 deletions packages/eve/src/public/sveltekit/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const DEV_SERVER_STALE_LOCK_MS = 30_000;
const EVE_CACHE_DIRECTORY_NAME = ".eve";
const EVE_SVELTEKIT_DEV_SERVER_FILE_NAME = "sveltekit-dev-server.json";
const EVE_SVELTEKIT_DEV_SERVER_LOCK_FILE_NAME = "sveltekit-dev-server.lock";
const LOCAL_SERVER_URL_PATTERN = /https?:\/\/(?:\[[^\]\s]+\]|[^\s/:[\]]+)(?::\d+)?/;
const EVE_DEV_SERVER_READY_PATTERN = /server listening at (https?:\/\/[^\s]+)/;

export interface EveProcessHandle {
readonly origin: string;
Expand Down Expand Up @@ -162,6 +162,12 @@ function createEveBinaryPath(): string {
return join(resolvePackageRoot(), "bin", "eve.js");
}

export function extractEveDevServerOrigin(chunk: string): string | undefined {
const url = EVE_DEV_SERVER_READY_PATTERN.exec(chunk)?.[1];
if (url === undefined) return undefined;
return normalizeOrigin(url);
}

function startServerProcess(input: {
readonly args: readonly string[];
readonly command: string;
Expand Down Expand Up @@ -203,11 +209,11 @@ function startServerProcess(input: {
let resolved = false;
const handleOutput = (chunk: Buffer) => {
if (resolved) return;
const match = LOCAL_SERVER_URL_PATTERN.exec(chunk.toString("utf8"));
if (match === null) return;
const origin = extractEveDevServerOrigin(chunk.toString("utf8"));
if (origin === undefined) return;
resolved = true;
cleanup();
resolvePromise({ origin: normalizeOrigin(match[0]), process: child });
resolvePromise({ origin, process: child });
};

child.once("error", handleError);
Expand Down