diff --git a/.changeset/fruity-banks-sit.md b/.changeset/fruity-banks-sit.md new file mode 100644 index 000000000..bc726eaf1 --- /dev/null +++ b/.changeset/fruity-banks-sit.md @@ -0,0 +1,5 @@ +--- +"@solidjs/start": minor +--- + +improve performance by ~38% diff --git a/packages/start/src/config/index.ts b/packages/start/src/config/index.ts index 4b1c82179..a7fad0d78 100644 --- a/packages/start/src/config/index.ts +++ b/packages/start/src/config/index.ts @@ -57,12 +57,17 @@ export function solidStart(options?: SolidStartOptions): Array { configEnvironment(name) { return { resolve: { + noExternal: ["h3"], // remove when https://github.com/solidjs/vite-plugin-solid/pull/228 is released externalConditions: ["solid", "node"], }, }; }, async config(_, env) { + _.ssr ??= {}; + _.ssr.resolve ??= {}; + _.ssr.resolve.conditions ??= []; + _.ssr.resolve.conditions.push("generic"); const clientInput = [handlers.client]; if (env.command === "build") { const clientRouter: BaseFileSystemRouter = (globalThis as any).ROUTERS.client; diff --git a/packages/start/src/server/handler.ts b/packages/start/src/server/handler.ts index d91453d93..e7f76e386 100644 --- a/packages/start/src/server/handler.ts +++ b/packages/start/src/server/handler.ts @@ -12,6 +12,7 @@ import { matchAPIRoute } from "./routes.ts"; import { handleServerFunction } from "./server-functions-handler.ts"; import type { APIEvent, FetchEvent, HandlerOptions, PageEvent } from "./types.ts"; import { getExpectedRedirectStatus } from "./util.ts"; +import { FastResponse } from "srvx/node"; const SERVER_FN_BASE = "/_server"; @@ -24,8 +25,7 @@ export function createBaseHandler( middleware: middleware.length ? middleware.map(decorateMiddleware) : undefined, handler: decorateHandler(async (e: H3Event) => { const event = getRequestEvent()!; - const url = new URL(event.request.url); - const pathname = stripBaseUrl(url.pathname); + const pathname = stripBaseUrl(e.url.pathname); if (pathname.startsWith(SERVER_FN_BASE)) { const serverFnResponse = await handleServerFunction(e); @@ -114,7 +114,9 @@ export function createBaseHandler( // using TransformStream in dev can cause solid-start-dev-server to crash // when stream is cancelled - if (globalThis.USING_SOLID_START_DEV_SERVER) return stream; + // send fast node streams + // @ts-expect-error + if (e.runtime?.name === "node") return new FastResponse(stream); // returning stream directly breaks cloudflare workers const { writable, readable } = new TransformStream(); diff --git a/packages/start/src/server/server-functions-handler.ts b/packages/start/src/server/server-functions-handler.ts index 160672684..994a29325 100644 --- a/packages/start/src/server/server-functions-handler.ts +++ b/packages/start/src/server/server-functions-handler.ts @@ -76,7 +76,7 @@ export async function handleServerFunction(h3Event: H3Event) { const serverReference = request.headers.get("X-Server-Id"); const instance = request.headers.get("X-Server-Instance"); const singleFlight = request.headers.has("X-Single-Flight"); - const url = new URL(request.url); + const url = h3Event.url; let functionId: string | undefined | null; if (serverReference) { // invariant(typeof serverReference === "string", "Invalid server function"); @@ -96,7 +96,7 @@ export async function handleServerFunction(h3Event: H3Event) { let parsed: any[] = []; // grab bound arguments from url when no JS - if (!instance || h3Event.method === "GET") { + if (!instance || request.method === "GET") { const args = url.searchParams.get("args"); if (args) { const json = JSON.parse(args); @@ -121,7 +121,7 @@ export async function handleServerFunction(h3Event: H3Event) { }); } } - if (h3Event.method === "POST") { + if (request.method === "POST") { const contentType = request.headers.get("content-type"); if ( @@ -176,7 +176,7 @@ export async function handleServerFunction(h3Event: H3Event) { } // handle no JS success case - if (!instance) return handleNoJS(result, request, parsed); + if (!instance) return handleNoJS(result, h3Event, parsed); h3Event.res.headers.set("content-type", "text/javascript"); @@ -200,7 +200,7 @@ export async function handleServerFunction(h3Event: H3Event) { h3Event.res.headers.set("X-Error", error.replace(/[\r\n]+/g, "")); } else { - x = handleNoJS(x, request, parsed, true); + x = handleNoJS(x, h3Event, parsed, true); } if (instance) { h3Event.res.headers.set("content-type", "text/javascript"); @@ -210,8 +210,8 @@ export async function handleServerFunction(h3Event: H3Event) { } } -function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boolean) { - const url = new URL(request.url); +function handleNoJS(result: any, h3Event: H3Event, parsed: any[], thrown?: boolean) { + const url = h3Event.url; const isError = result instanceof Error; let statusCode = 302; let headers: Headers; @@ -226,7 +226,7 @@ function handleNoJS(result: any, request: Request, parsed: any[], thrown?: boole } } else headers = new Headers({ - Location: new URL(request.headers.get("referer")!).toString(), + Location: new URL(h3Event.req.headers.get("referer")!).toString(), }); if (result) { headers.append( @@ -293,7 +293,7 @@ async function handleSingleFlight(sourceEvent: FetchEvent, result: any): Promise if (result.headers.has("Location")) url = new URL( result.headers.get("Location")!, - new URL(sourceEvent.request.url).origin + import.meta.env.BASE_URL, + sourceEvent.nativeEvent.url.origin + import.meta.env.BASE_URL, ).toString(); } const event = { ...sourceEvent } as PageEvent;