UNPKG

@tanstack/router-core

Version:

Modern and scalable routing for React applications

1 lines 29.7 kB
{"version":3,"file":"ssr-server.cjs","names":[],"sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream } from 'seroval'\nimport { invariant } from '../invariant'\nimport {\n createInlineCssPlaceholderAsset,\n createInlineCssStyleAsset,\n getStylesheetHref,\n} from '../manifest'\nimport { decodePath } from '../utils'\nimport { createSieveCache } from '../sieve-cache'\nimport { rootRouteId } from '../root'\nimport { _getRenderedMatches } from '../load-client'\nimport { waitForReason } from '../await-signal'\nimport {\n SSR_SERIALIZATION_SCOPE_ID,\n createHydrationScripts,\n} from './hydrationScripts'\nimport { dehydrateSsrMatchId } from './ssr-match-id'\nimport { ssrSerovalPlugins } from './serializer/seroval-plugins.ssr'\nimport { makeSsrSerovalPlugin } from './serializer/makeSsrSerovalPlugin'\nimport type { SieveCache } from '../sieve-cache'\nimport type { DehydratedMatch, DehydratedRouter } from './types'\nimport type { AnyRouter, ServerSsr } from '../router'\nimport type { AnyRouteMatch } from '../Matches'\nimport type {\n Manifest,\n ManifestRoute,\n ManifestRouteAssets,\n ServerManifest,\n} from '../manifest'\n\ntype DehydrationPhase = 'idle' | 'started' | 'disabled'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: dehydrateSsrMatchId(match.id),\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n if (match._notFound) {\n dehydratedMatch.g = true\n }\n return dehydratedMatch\n}\n\nfunction disposeSerializationSafely(dispose?: () => void) {\n try {\n dispose?.()\n } catch (err) {\n console.error('Error disposing SSR serialization:', err)\n }\n}\n\nfunction notifyAndClearListeners<T>(\n listeners: Array<(arg: T) => void>,\n errorMessage: string,\n arg: T,\n) {\n const pending = listeners.slice()\n listeners.length = 0\n for (const listener of pending) {\n try {\n listener(arg)\n } catch (error) {\n console.error(errorMessage, error)\n }\n }\n}\n\nconst isProd = process.env.NODE_ENV === 'production'\n\ntype FilteredRoutes = Manifest['routes']\n\ntype PreparedMatchedManifestRoutes = {\n routes: FilteredRoutes\n hasStrippedRoutes: boolean\n inlineCssHrefs?: Array<string>\n}\n\ntype ManifestCache = SieveCache<string, PreparedMatchedManifestRoutes>\n\nconst MANIFEST_CACHE_SIZE = 100\nconst manifestCaches = new WeakMap<ServerManifest, ManifestCache>()\n\nfunction getManifestCache(manifest: ServerManifest): ManifestCache {\n const cache = manifestCaches.get(manifest)\n if (cache) {\n return cache\n }\n const newCache = createSieveCache<string, PreparedMatchedManifestRoutes>(\n MANIFEST_CACHE_SIZE,\n )\n manifestCaches.set(manifest, newCache)\n return newCache\n}\n\nfunction getInlineCssForPreparedRoutes(\n manifest: ServerManifest,\n preparedRoutes: PreparedMatchedManifestRoutes,\n) {\n const styles = manifest.inlineCss?.styles\n const hrefs = preparedRoutes.inlineCssHrefs\n if (!styles || !hrefs?.length) {\n return undefined\n }\n\n // Joined once per matched route set and request. Retaining the joined copy\n // on the module cache entry would permanently duplicate shared CSS across\n // up to MANIFEST_CACHE_SIZE route combinations.\n let css = ''\n for (const href of hrefs) {\n css += styles[href]!\n }\n\n return css\n}\n\nfunction getInlineCssAssetForPreparedRoutes(\n manifest: ServerManifest,\n preparedRoutes: PreparedMatchedManifestRoutes,\n) {\n const css = getInlineCssForPreparedRoutes(manifest, preparedRoutes)\n\n return css === undefined ? undefined : createInlineCssStyleAsset(css)\n}\n\nfunction getMatchedRoutesCacheKey(matches: Array<AnyRouteMatch>) {\n let cacheKey = ''\n for (let i = 0; i < matches.length; i++) {\n cacheKey += (i === 0 ? '' : '\\0') + matches[i]!.routeId\n }\n return cacheKey\n}\n\nfunction getPreparedMatchedManifestRoutes(\n manifest: ServerManifest,\n matches: Array<AnyRouteMatch>,\n cacheKey: string,\n) {\n if (isProd) {\n const cached = getManifestCache(manifest).get(cacheKey)\n if (cached) {\n return cached\n }\n }\n\n const preparedRoutes = prepareMatchedManifestRoutes(manifest, matches)\n\n if (isProd) {\n getManifestCache(manifest).set(cacheKey, preparedRoutes)\n }\n\n return preparedRoutes\n}\n\nfunction prepareMatchedManifestRoutes(\n manifest: ServerManifest,\n matches: Array<AnyRouteMatch>,\n): PreparedMatchedManifestRoutes {\n const inlineStyles = manifest.inlineCss?.styles\n const routes: FilteredRoutes = {}\n\n if (!inlineStyles) {\n for (const match of matches) {\n const route = manifest.routes[match.routeId]\n if (route) {\n routes[match.routeId] = route\n }\n }\n return { routes, hasStrippedRoutes: false }\n }\n\n const inlineCssHrefs: Array<string> = []\n const seenInlineCssHrefs = new Set<string>()\n let hasStrippedRoutes = false\n\n for (const match of matches) {\n const routeId = match.routeId\n const route = manifest.routes[routeId]\n if (!route) {\n continue\n }\n\n const nextRoute = stripInlinedStylesheetAssetsFromRoute(\n inlineStyles,\n route,\n inlineCssHrefs,\n seenInlineCssHrefs,\n )\n\n if (nextRoute !== route) {\n hasStrippedRoutes = true\n }\n routes[routeId] = nextRoute\n }\n\n return {\n routes,\n hasStrippedRoutes,\n ...(inlineCssHrefs.length ? { inlineCssHrefs } : {}),\n }\n}\n\nfunction stripInlinedStylesheetAssetsFromRoute(\n inlineStyles: Record<string, string>,\n route: ManifestRoute,\n inlineCssHrefs: Array<string>,\n seenInlineCssHrefs: Set<string>,\n): ManifestRoute {\n const css = route.css\n if (!css) {\n return route\n }\n\n if (css.length === 0) {\n const nextRoute = { ...route }\n delete nextRoute.css\n return nextRoute\n }\n\n let cssLinks: typeof css | undefined\n for (let i = 0; i < css.length; i++) {\n const link = css[i]!\n const href = getStylesheetHref(link)\n if (inlineStyles[href] === undefined) {\n if (cssLinks) {\n cssLinks.push(link)\n }\n continue\n }\n\n if (!seenInlineCssHrefs.has(href)) {\n seenInlineCssHrefs.add(href)\n inlineCssHrefs.push(href)\n }\n\n if (!cssLinks) {\n cssLinks = css.slice(0, i)\n }\n }\n\n if (!cssLinks) {\n return route\n }\n\n if (cssLinks.length > 0) {\n return { ...route, css: cssLinks }\n }\n\n const nextRoute = { ...route }\n delete nextRoute.css\n return nextRoute\n}\n\nfunction hasRouteAssets(route: ManifestRoute) {\n return !!route.scripts?.length || !!route.css?.length\n}\n\nfunction hasRequestAssets(assets: ManifestRouteAssets | undefined) {\n return !!assets && (!!assets.preloads?.length || hasRouteAssets(assets))\n}\n\nfunction mergeRequestAssetsIntoRootRoute(\n rootRoute: ManifestRoute | undefined,\n requestAssets: ManifestRouteAssets | undefined,\n): ManifestRoute {\n const preloads = requestAssets?.preloads?.length\n ? [...requestAssets.preloads, ...(rootRoute?.preloads ?? [])]\n : rootRoute?.preloads\n const scripts = requestAssets?.scripts?.length\n ? [...requestAssets.scripts, ...(rootRoute?.scripts ?? [])]\n : rootRoute?.scripts\n const cssLinks = requestAssets?.css?.length\n ? [...requestAssets.css, ...(rootRoute?.css ?? [])]\n : rootRoute?.css\n\n return {\n ...(rootRoute ?? {}),\n ...(preloads?.length ? { preloads } : {}),\n ...(scripts?.length ? { scripts } : {}),\n ...(cssLinks?.length ? { css: cssLinks } : {}),\n }\n}\n\n/**\n * Compose a client-facing manifest from prepared routes, an optional inline\n * style, and optional request-scoped assets merged into the root route.\n * Shared by the `router.ssr.manifest` getter and `dehydrate()` so the two\n * compositions cannot drift.\n */\nfunction composeManifest(\n scriptFormat: ServerManifest['scriptFormat'],\n inlineStyle: Manifest['inlineStyle'],\n routes: FilteredRoutes,\n requestAssets: ManifestRouteAssets | undefined,\n): Manifest {\n const base: Manifest = {\n ...(scriptFormat ? { scriptFormat } : {}),\n ...(inlineStyle ? { inlineStyle } : {}),\n routes,\n }\n if (!hasRequestAssets(requestAssets)) {\n return base\n }\n // Merge request-scoped assets into the root route without mutating any\n // cached route map.\n return {\n ...base,\n routes: {\n ...routes,\n [rootRouteId]: mergeRequestAssetsIntoRootRoute(\n routes[rootRouteId],\n requestAssets,\n ),\n },\n }\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n getRequestAssets,\n}: {\n router: AnyRouter\n manifest: ServerManifest | undefined\n getRequestAssets?: () => ManifestRouteAssets | undefined\n}) {\n // Inline CSS joining and route filtering depend only on matched route ids,\n // so keep that immutable preparation request-local. Request assets can be\n // discovered between head and Scripts reads and may mutate in place; always\n // compose their current contents instead of caching by object identity.\n let memoizedPreparedManifest:\n | {\n cacheKey: string\n inlineCssAsset: Manifest['inlineStyle'] | undefined\n routes: FilteredRoutes\n }\n | undefined\n router.ssr = {\n get manifest() {\n if (!manifest) {\n return manifest\n }\n\n const requestAssets = getRequestAssets?.()\n const hasAssets = hasRequestAssets(requestAssets)\n\n if (!hasAssets && !manifest.inlineCss) {\n return manifest\n }\n\n let inlineCssAsset: Manifest['inlineStyle'] | undefined\n let routes = manifest.routes\n if (manifest.inlineCss) {\n const matches = _getRenderedMatches(router.stores.matches.get())\n const cacheKey = getMatchedRoutesCacheKey(matches)\n if (memoizedPreparedManifest?.cacheKey === cacheKey) {\n inlineCssAsset = memoizedPreparedManifest.inlineCssAsset\n routes = memoizedPreparedManifest.routes\n } else {\n const preparedManifest = getPreparedMatchedManifestRoutes(\n manifest,\n matches,\n cacheKey,\n )\n inlineCssAsset = getInlineCssAssetForPreparedRoutes(\n manifest,\n preparedManifest,\n )\n if (preparedManifest.hasStrippedRoutes) {\n routes = { ...manifest.routes, ...preparedManifest.routes }\n }\n memoizedPreparedManifest = { cacheKey, inlineCssAsset, routes }\n }\n }\n\n return composeManifest(\n manifest.scriptFormat,\n inlineCssAsset,\n routes,\n hasAssets ? requestAssets : undefined,\n )\n },\n }\n let dehydrationPhase: DehydrationPhase = 'idle'\n let renderFinished = false\n const renderFinishedListeners: Array<() => void> = []\n const cleanupListeners: Array<(settled: boolean) => void> = []\n let cleanupStarted = false\n // Every value the router dehydrated has settled: nothing it started can\n // still be pending when the response ends.\n let settled = false\n let disposeSerialization: (() => void) | undefined\n const hydrationScripts = createHydrationScripts(router.options.ssr?.nonce)\n\n const serverSsr: ServerSsr = {\n hydrationScripts,\n dehydrate: async (opts?: {\n requestAssets?: ManifestRouteAssets\n signal?: AbortSignal\n }) => {\n // Guard synchronously before the first await: a concurrent second call\n // would double-serialize and corrupt the hydration payload.\n if (dehydrationPhase !== 'idle') {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n dehydrationPhase === 'disabled'\n ? 'Invariant failed: hydration is disabled for this request!'\n : 'Invariant failed: router is already dehydrated!',\n )\n }\n\n invariant()\n }\n opts?.signal?.throwIfAborted()\n dehydrationPhase = 'started'\n let matchesToDehydrate = _getRenderedMatches(router.stores.matches.get())\n const isShell = router.isShell()\n if (isShell) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n let manifestToDehydrate: Manifest | undefined = undefined\n // Only currently matched routes are dehydrated. Other route assets are\n // loaded through dynamic imports when those routes become active.\n if (manifest) {\n const cacheKey = getMatchedRoutesCacheKey(matchesToDehydrate)\n const preparedManifest = getPreparedMatchedManifestRoutes(\n manifest,\n matchesToDehydrate,\n cacheKey,\n )\n\n manifestToDehydrate = composeManifest(\n manifest.scriptFormat,\n preparedManifest.inlineCssHrefs\n ? createInlineCssPlaceholderAsset()\n : undefined,\n preparedManifest.routes,\n opts?.requestAssets,\n )\n }\n const dehydratedRouter: DehydratedRouter = {\n manifest: manifestToDehydrate,\n matches,\n }\n const dehydrate = router.options.dehydrate\n const dehydratedData = dehydrate\n ? opts?.signal\n ? await waitForReason(dehydrate.call(router.options), opts.signal)\n : await dehydrate.call(router.options)\n : undefined\n opts?.signal?.throwIfAborted()\n if (cleanupStarted) {\n return\n }\n if (dehydratedData !== undefined) {\n dehydratedRouter.dehydratedData = dehydratedData\n }\n const trackPlugins = { didRun: false }\n const serializationAdapters = router.options.serializationAdapters\n const plugins = serializationAdapters\n ? [\n ...serializationAdapters.map((adapter) =>\n makeSsrSerovalPlugin(adapter, trackPlugins),\n ),\n ...ssrSerovalPlugins,\n ]\n : ssrSerovalPlugins\n\n let serializationCompleteSignaled = false\n let initialSerialized = false\n const completeScriptSerialization = (\n result: boolean | { error: unknown },\n ) => {\n if (serializationCompleteSignaled || cleanupStarted) {\n return\n }\n serializationCompleteSignaled = true\n const dispose = disposeSerialization\n disposeSerialization = undefined\n if (result === true) {\n settled = true\n hydrationScripts.finish()\n } else if (result) {\n hydrationScripts.fail(result.error)\n }\n if (dispose) {\n // Seroval invokes completion callbacks before it marks its stream as\n // inactive. Clear ownership before notifying the hydration consumer,\n // which can synchronously clean up this request, then dispose later.\n queueMicrotask(() => disposeSerializationSafely(dispose))\n }\n }\n\n let synchronousFailure: { error: unknown } | undefined\n const dispose = crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins,\n onSerialize: (data, initial) => {\n if (serializationCompleteSignaled || cleanupStarted) {\n return\n }\n initialSerialized ||= initial\n if (\n !hydrationScripts.pushSerializedSource(\n data,\n initial,\n trackPlugins.didRun,\n )\n ) {\n // Rejected output stops the producer while deferred work may remain.\n completeScriptSerialization(false)\n }\n },\n onError: (err: unknown) => {\n if (serializationCompleteSignaled || cleanupStarted) {\n return\n }\n console.error('Serialization error:', err)\n synchronousFailure = { error: err }\n completeScriptSerialization({ error: err })\n },\n scopeId: SSR_SERIALIZATION_SCOPE_ID,\n onDone: () => {\n if (initialSerialized) {\n completeScriptSerialization(true)\n }\n },\n })\n // Seroval can call onDone synchronously before it returns dispose().\n if (cleanupStarted || serializationCompleteSignaled) {\n disposeSerializationSafely(dispose)\n } else {\n disposeSerialization = dispose\n }\n if (synchronousFailure) {\n throw synchronousFailure.error\n }\n },\n onRenderFinished: (listener) => {\n if (cleanupStarted) {\n return\n }\n if (renderFinished) {\n try {\n listener()\n } catch (error) {\n console.error('Error in render finished listener:', error)\n }\n return\n }\n renderFinishedListeners.push(listener)\n },\n onCleanup: (listener) => {\n if (cleanupStarted) {\n // Cleanup already happened (or is running). Invoke immediately so\n // late registrants can still release their resources instead of\n // silently retaining them (standard disposer convention).\n try {\n listener(settled)\n } catch (error) {\n console.error('Error in SSR cleanup listener:', error)\n }\n return\n }\n cleanupListeners.push(listener)\n },\n setRenderFinished: () => {\n if (cleanupStarted || renderFinished) {\n return\n }\n renderFinished = true\n hydrationScripts.liftBarrier()\n notifyAndClearListeners(\n renderFinishedListeners,\n 'Error in render finished listener:',\n undefined,\n )\n },\n disableHydration: () => {\n if (cleanupStarted || dehydrationPhase === 'disabled') {\n return\n }\n if (dehydrationPhase !== 'idle') {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: cannot disable hydration after dehydrate()!',\n )\n }\n\n invariant()\n }\n // The owner rejects later takes/claims; guard order matters so a\n // throwing owner does not leave the phase half-set.\n hydrationScripts.disableHydration()\n dehydrationPhase = 'disabled'\n },\n takeInitialHydrationScriptTags:\n hydrationScripts.takeInitialHydrationScriptTags,\n cleanup() {\n // Guard against multiple/reentrant cleanup calls. A listener could call\n // cleanup() again indirectly; snapshot + clear before invoking so each\n // listener runs exactly once and reentry is a no-op.\n if (cleanupStarted) {\n return\n }\n cleanupStarted = true\n hydrationScripts.cleanup()\n const dispose = disposeSerialization\n disposeSerialization = undefined\n disposeSerializationSafely(dispose)\n notifyAndClearListeners(\n cleanupListeners,\n 'Error in SSR cleanup listener:',\n settled,\n )\n renderFinishedListeners.length = 0\n router.ssr = undefined\n router.serverSsr = undefined\n },\n }\n\n router.serverSsr = serverSsr\n for (const listener of router.serverSsrLifecycle?.onServerSsrAttach ?? []) {\n try {\n listener(serverSsr)\n } catch (err) {\n console.error('SSR attach listener error:', err)\n }\n }\n}\n\n/**\n * Get the origin for the request.\n *\n * SECURITY: We intentionally do NOT trust the Origin header for determining\n * the router's origin. The Origin header can be spoofed by attackers, which\n * could lead to SSRF-like vulnerabilities where redirects are constructed\n * using a malicious origin (CVE-2024-34351).\n *\n * Instead, we derive the origin from request.url, which is typically set by\n * the server infrastructure (not client-controlled headers).\n *\n * For applications behind proxies that need to trust forwarded headers,\n * use the router's `origin` option to explicitly configure a trusted origin.\n */\nexport function getOrigin(request: Request) {\n try {\n return new URL(request.url).origin\n } catch {}\n return 'http://localhost'\n}\n\n// server and browser can decode/encode characters differently in paths and search params.\n// Server generally strictly follows the WHATWG URL Standard, while browsers may differ for legacy reasons.\n// for example, in paths \"|\" is not encoded on the server but is encoded on chromium (and not on firefox) while \"\" is encoded on both sides.\n// Another anomaly is that in Node new URLSearchParams and new URL also decode/encode characters differently.\n// new URLSearchParams() encodes \"|\" while new URL() does not, and in this instance\n// chromium treats search params differently than paths, i.e. \"|\" is not encoded in search params.\nexport function getNormalizedURL(url: string | URL, base?: string | URL) {\n // ensure backslashes are encoded correctly in the URL\n if (typeof url === 'string') {\n url = url.replace('\\\\', '%5C')\n }\n\n const rawUrl = new URL(url, base)\n // URL parsing has already handled backslashes and ignored controls. A pathname\n // like \"//evil.example\" would become a protocol-relative URL when rebuilt below.\n const handledProtocolRelativeURL = rawUrl.pathname.startsWith('//')\n const decodedPathname = decodePath(\n handledProtocolRelativeURL\n ? rawUrl.pathname.replace(/^\\/+/, '/')\n : rawUrl.pathname,\n )\n const searchParams = new URLSearchParams(rawUrl.search)\n const normalizedHref =\n decodedPathname +\n (searchParams.size > 0 ? '?' : '') +\n searchParams.toString() +\n rawUrl.hash\n\n return {\n url: new URL(normalizedHref, rawUrl.origin),\n handledProtocolRelativeURL,\n }\n}\n"],"mappings":"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"}