From 4709e15d020bd918c324cd37761fff29220da6a8 Mon Sep 17 00:00:00 2001 From: water <672684719@qq.com> Date: Sun, 16 Aug 2026 23:12:19 +0800 Subject: [PATCH 1/3] fix(dev-server): delegate preview to Nitro when building with Nitro v3 When Nitro controls the production server, its `configurePreviewServer` hook handles requests. SolidStart's hook runs first and throws because `dist/server/entry-server.*` does not exist after a Nitro build, which stops Vite's hook loop before reaching the `nitro:preview` hook. Detect this by checking for the Nitro build output marker (`.output/nitro.json`). If present, skip the SolidStart preview middleware so the Nitro hook can handle preview instead. Fixes #2291 --- packages/start/src/config/dev-server.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/start/src/config/dev-server.ts b/packages/start/src/config/dev-server.ts index 3a5ac418a..cb86d7a89 100644 --- a/packages/start/src/config/dev-server.ts +++ b/packages/start/src/config/dev-server.ts @@ -1,4 +1,4 @@ -import { existsSync } from "node:fs"; +import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; import { NodeRequest, sendNodeResponse } from "srvx/node"; @@ -15,6 +15,22 @@ export function devServer(serverEntryPath: string): Array { { name: "solid-start-dev-server", configurePreviewServer(server) { + // When Nitro controls the production server, its preview hook + // handles requests. Detect this by checking for the Nitro build + // output marker — if present, skip our middleware so the + // nitro:preview hook runs instead. + const nitroJsonPath = join(server.config.root, ".output", "nitro.json"); + if (existsSync(nitroJsonPath)) { + try { + const nitroConfig = JSON.parse(readFileSync(nitroJsonPath, "utf8")); + if (nitroConfig.serverEntry) { + return; + } + } catch { + // Malformed nitro.json — fall through to the default path. + } + } + const serverEntryUrl = pathToFileURL(resolvePreviewServerEntry(server.config.root)).href; return () => { From 01622c96eab7e8f5516e557fb9672ef33c6140e0 Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Tue, 18 Aug 2026 12:13:54 +0200 Subject: [PATCH 2/3] fix(dev-server): detect Nitro preview ownership --- .changeset/calm-preview-owners.md | 5 +++++ packages/start/src/config/dev-server.ts | 21 +++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) create mode 100644 .changeset/calm-preview-owners.md diff --git a/.changeset/calm-preview-owners.md b/.changeset/calm-preview-owners.md new file mode 100644 index 000000000..43c185d7c --- /dev/null +++ b/.changeset/calm-preview-owners.md @@ -0,0 +1,5 @@ +--- +"@solidjs/start": patch +--- + +Delegate `vite preview` to Nitro when its preview plugin is active, including for static builds that intentionally have no server entry. diff --git a/packages/start/src/config/dev-server.ts b/packages/start/src/config/dev-server.ts index cb86d7a89..fd6493b8a 100644 --- a/packages/start/src/config/dev-server.ts +++ b/packages/start/src/config/dev-server.ts @@ -1,4 +1,4 @@ -import { existsSync, readFileSync } from "node:fs"; +import { existsSync } from "node:fs"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; import { NodeRequest, sendNodeResponse } from "srvx/node"; @@ -15,21 +15,10 @@ export function devServer(serverEntryPath: string): Array { { name: "solid-start-dev-server", configurePreviewServer(server) { - // When Nitro controls the production server, its preview hook - // handles requests. Detect this by checking for the Nitro build - // output marker — if present, skip our middleware so the - // nitro:preview hook runs instead. - const nitroJsonPath = join(server.config.root, ".output", "nitro.json"); - if (existsSync(nitroJsonPath)) { - try { - const nitroConfig = JSON.parse(readFileSync(nitroJsonPath, "utf8")); - if (nitroConfig.serverEntry) { - return; - } - } catch { - // Malformed nitro.json — fall through to the default path. - } - } + // Nitro's preview hook handles both server and static builds. Static + // builds intentionally have no server entry, so use the active plugin + // as the ownership signal instead of inspecting generated output. + if (server.config.plugins.some(plugin => plugin.name === "nitro:preview")) return; const serverEntryUrl = pathToFileURL(resolvePreviewServerEntry(server.config.root)).href; From 76ad73d18049baccf034343467cb258b75f20d33 Mon Sep 17 00:00:00 2001 From: Birk Skyum Date: Tue, 18 Aug 2026 12:26:09 +0200 Subject: [PATCH 3/3] style: clarify Nitro preview comment --- packages/start/src/config/dev-server.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/start/src/config/dev-server.ts b/packages/start/src/config/dev-server.ts index fd6493b8a..93998f807 100644 --- a/packages/start/src/config/dev-server.ts +++ b/packages/start/src/config/dev-server.ts @@ -15,9 +15,8 @@ export function devServer(serverEntryPath: string): Array { { name: "solid-start-dev-server", configurePreviewServer(server) { - // Nitro's preview hook handles both server and static builds. Static - // builds intentionally have no server entry, so use the active plugin - // as the ownership signal instead of inspecting generated output. + // Nitro's preview hook handles both server and static builds. + // Static builds have no server entry, so detect the active plugin instead. if (server.config.plugins.some(plugin => plugin.name === "nitro:preview")) return; const serverEntryUrl = pathToFileURL(resolvePreviewServerEntry(server.config.root)).href;