Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/starter-react-router/tsconfig.tsbuildinfo

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/stats-generator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@react-router/node": "7.10.1",
"react-router": "7.10.1",
"next": "16.1.1",
"react": "19.2.3",
"react-dom": "19.2.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/stats-generator/src/ssr/handlers/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ export async function buildAstroHandler(): Promise<SSRHandler> {
)
const entryUrl = pathToFileURL(entryPath).href
const { handler } = await import(entryUrl)
return handler as SSRHandler
return { type: 'node', handler }
}
7 changes: 5 additions & 2 deletions packages/stats-generator/src/ssr/handlers/nextjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export async function buildNextJSHandler(): Promise<SSRHandler> {
dir: join(packagesDir, 'app-next-js'),
})
await app.prepare()
// TODO: Make the SSRHandler type more flexible so we don't have to cast here
return app.getRequestHandler() as unknown as SSRHandler
return {
type: 'node',
handler:
app.getRequestHandler() as unknown as import('../types.ts').NodeSSRHandler,
}
}
2 changes: 1 addition & 1 deletion packages/stats-generator/src/ssr/handlers/nuxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ export async function buildNuxtHandler(): Promise<SSRHandler> {
)
const entryUrl = pathToFileURL(entryPath).href
const { handler } = await import(entryUrl)
return handler as SSRHandler
return { type: 'node', handler }
}
11 changes: 5 additions & 6 deletions packages/stats-generator/src/ssr/handlers/react-router.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { createRequestListener } from '@react-router/node'
import { createRequestHandler } from 'react-router'
import { packagesDir } from '../../constants.ts'
import type { SSRHandler } from '../types.ts'

Expand All @@ -14,9 +14,8 @@ export async function buildReactRouterHandler(): Promise<SSRHandler> {
)
const buildUrl = pathToFileURL(buildPath).href
const build = await import(buildUrl)
// TODO: Make the SSRHandler type more flexible so we don't have to cast here
return createRequestListener({
build,
mode: 'production',
}) as unknown as SSRHandler
return {
type: 'web',
handler: createRequestHandler(build, 'production'),
}
}
15 changes: 9 additions & 6 deletions packages/stats-generator/src/ssr/handlers/solid-start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ export async function buildSolidStartHandler(): Promise<SSRHandler> {

// h3's sendStream short-circuits when res.socket is falsy,
// so we wrap the handler to set a truthy socket on the mock response
return ((req, res) => {
if (!res.socket) {
res.socket = {} as any
}
return handler(req, res)
}) as SSRHandler
return {
type: 'node',
handler: (req, res) => {
if (!res.socket) {
res.socket = {} as any
}
return handler(req, res)
},
}
}
2 changes: 1 addition & 1 deletion packages/stats-generator/src/ssr/handlers/sveltekit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export async function buildSvelteKitHandler(): Promise<SSRHandler> {
const entryPath = join(packagesDir, 'app-sveltekit', 'build', 'handler.js')
const entryUrl = pathToFileURL(entryPath).href
const { handler } = await import(entryUrl)
return handler as SSRHandler
return { type: 'node', handler }
}
15 changes: 9 additions & 6 deletions packages/stats-generator/src/ssr/handlers/tanstack-start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ export async function buildTanStackStartHandler(): Promise<SSRHandler> {

// srvx's toNodeHandler checks res.socket and short-circuits when falsy,
// so we wrap the handler to set a truthy socket on the mock response
return ((req, res) => {
if (!res.socket) {
res.socket = {} as any
}
return middleware(req, res)
}) as SSRHandler
return {
type: 'node',
handler: (req, res) => {
if (!res.socket) {
res.socket = {} as any
}
return middleware(req, res)
},
}
}
9 changes: 2 additions & 7 deletions packages/stats-generator/src/ssr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@ import { buildNextJSHandler } from './handlers/nextjs.ts'
import { buildReactRouterHandler } from './handlers/react-router.ts'
import { buildSolidStartHandler } from './handlers/solid-start.ts'
import { buildTanStackStartHandler } from './handlers/tanstack-start.ts'
import type { SSRBenchmarkResult, SSRStats } from './types.ts'
import type { SSRBenchmarkResult, SSRHandler, SSRStats } from './types.ts'

interface SSRFrameworkConfig {
name: string
displayName: string
package: string
buildHandler: () => Promise<
(
req: import('./mock-http.ts').IncomingMessage,
res: import('./mock-http.ts').ServerResponse,
) => void | Promise<void>
>
buildHandler: () => Promise<SSRHandler>
}

const SSR_FRAMEWORKS: SSRFrameworkConfig[] = [
Expand Down
47 changes: 39 additions & 8 deletions packages/stats-generator/src/ssr/run-benchmark.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Bench } from 'tinybench'
import { IncomingMessage, ServerResponse } from './mock-http.ts'
import type { SSRHandler, SSRBenchmarkResult } from './types.ts'
import type {
NodeSSRHandler,
SSRBenchmarkResult,
SSRHandler,
WebSSRHandler,
} from './types.ts'

interface HandlerConfig {
name: string
Expand All @@ -9,17 +14,43 @@ interface HandlerConfig {
handler: SSRHandler
}

async function runHandler(
handler: SSRHandler,
interface HandlerResult {
body: string
length: number
}

async function runWebHandler(
handler: WebSSRHandler,
collect = false,
): Promise<HandlerResult> {
const request = new Request('http://localhost/')
const response = await handler(request)
const buffer = await response.arrayBuffer()
const body = collect ? new TextDecoder().decode(buffer) : ''
return { body, length: buffer.byteLength }
}

async function runNodeHandler(
handler: NodeSSRHandler,
collect = false,
): Promise<ServerResponse> {
): Promise<HandlerResult> {
const request = new IncomingMessage()
const response = new ServerResponse(request, collect)

handler(request, response)

await response.await
return response
return { body: response.body, length: response.length }
}

async function runHandler(
ssrHandler: SSRHandler,
collect = false,
): Promise<HandlerResult> {
if (ssrHandler.type === 'web') {
return runWebHandler(ssrHandler.handler, collect)
}
return runNodeHandler(ssrHandler.handler, collect)
}

function getDuplicationFactor(body: string): number {
Expand Down Expand Up @@ -64,8 +95,8 @@ export async function runBenchmark(
const task = bench.getTask(config.name)
if (!task || !task.result) continue

const response = await runHandler(config.handler, true)
const duplicationFactor = getDuplicationFactor(response.body)
const { body, length } = await runHandler(config.handler, true)
const duplicationFactor = getDuplicationFactor(body)

results.push({
name: config.name,
Expand All @@ -74,7 +105,7 @@ export async function runBenchmark(
opsPerSec: Math.round(task.result.hz),
avgLatencyMs: Number(task.result.mean.toFixed(3)),
samples: task.result.samples.length,
bodySizeKb: Number((response.length / 1024).toFixed(2)),
bodySizeKb: Number((length / 1024).toFixed(2)),
duplicationFactor: Number(duplicationFactor.toFixed(2)),
})
}
Expand Down
8 changes: 7 additions & 1 deletion packages/stats-generator/src/ssr/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type { IncomingMessage, ServerResponse } from './mock-http.ts'

export type SSRHandler = (
export type WebSSRHandler = (request: Request) => Promise<Response>

export type NodeSSRHandler = (
req: IncomingMessage,
res: ServerResponse,
) => void | Promise<void>

export type SSRHandler =
| { type: 'web'; handler: WebSSRHandler }
| { type: 'node'; handler: NodeSSRHandler }

export interface SSRBenchmarkResult {
name: string
displayName: string
Expand Down
28 changes: 3 additions & 25 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading