@tanstack/router-core 1.171.5 → 1.171.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/index.cjs +5 -1
- package/dist/cjs/index.d.cts +2 -2
- package/dist/cjs/invariant.cjs.map +1 -1
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/lru-cache.cjs.map +1 -1
- package/dist/cjs/manifest.cjs +43 -17
- package/dist/cjs/manifest.cjs.map +1 -1
- package/dist/cjs/manifest.d.cts +76 -24
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/rewrite.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +31 -16
- package/dist/cjs/scroll-restoration-script/client.cjs.map +1 -1
- package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/searchMiddleware.cjs.map +1 -1
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.cjs +10 -8
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.d.cts +2 -2
- package/dist/cjs/ssr/handlerCallback.cjs +46 -0
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
- package/dist/cjs/ssr/handlerCallback.d.cts +15 -1
- package/dist/cjs/ssr/headers.cjs.map +1 -1
- package/dist/cjs/ssr/json.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/server.cjs +6 -1
- package/dist/cjs/ssr/server.d.cts +3 -2
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +263 -132
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +4 -19
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +455 -203
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +14 -5
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/invariant.js.map +1 -1
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/lru-cache.js.map +1 -1
- package/dist/esm/manifest.d.ts +76 -24
- package/dist/esm/manifest.js +39 -17
- package/dist/esm/manifest.js.map +1 -1
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/rewrite.js.map +1 -1
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +31 -16
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration-script/client.js.map +1 -1
- package/dist/esm/scroll-restoration-script/server.js.map +1 -1
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/searchMiddleware.js.map +1 -1
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.d.ts +2 -2
- package/dist/esm/ssr/createRequestHandler.js +10 -8
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/handlerCallback.d.ts +15 -1
- package/dist/esm/ssr/handlerCallback.js +42 -1
- package/dist/esm/ssr/handlerCallback.js.map +1 -1
- package/dist/esm/ssr/headers.js.map +1 -1
- package/dist/esm/ssr/json.js.map +1 -1
- package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/server.d.ts +3 -2
- package/dist/esm/ssr/server.js +2 -2
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-match-id.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +4 -19
- package/dist/esm/ssr/ssr-server.js +264 -133
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +14 -5
- package/dist/esm/ssr/transformStreamWithRouter.js +455 -203
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/stores.js.map +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +21 -1
- package/src/manifest.ts +151 -59
- package/src/router.ts +37 -19
- package/src/ssr/createRequestHandler.ts +14 -13
- package/src/ssr/handlerCallback.ts +84 -1
- package/src/ssr/server.ts +14 -2
- package/src/ssr/ssr-server.ts +418 -222
- package/src/ssr/transformStreamWithRouter.ts +662 -281
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-client.js","names":[],"sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import { invariant } from '../invariant'\nimport { isNotFound } from '../not-found'\nimport { createControlledPromise } from '../utils'\nimport { hydrateSsrMatchId } from './ssr-match-id'\nimport type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'\nimport type { DehydratedMatch, TsrSsrGlobal } from './types'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { RouteContextOptions } from '../route'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare global {\n interface Window {\n [GLOBAL_TSR]?: TsrSsrGlobal\n [GLOBAL_SEROVAL]?: any\n }\n}\n\nfunction hydrateMatch(\n match: AnyRouteMatch,\n deyhydratedMatch: DehydratedMatch,\n): void {\n match.id = deyhydratedMatch.i\n match.__beforeLoadContext = deyhydratedMatch.b\n match.loaderData = deyhydratedMatch.l\n match.status = deyhydratedMatch.s\n match.ssr = deyhydratedMatch.ssr\n match.updatedAt = deyhydratedMatch.u\n match.error = deyhydratedMatch.e\n // Only hydrate global-not-found when a defined value is present in the\n // dehydrated payload. If omitted, preserve the value computed from the\n // current client location (important for SPA fallback HTML served at unknown\n // URLs, where dehydrated matches may come from `/` but client matching marks\n // root as globalNotFound).\n if (deyhydratedMatch.g !== undefined) {\n match.globalNotFound = deyhydratedMatch.g\n }\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n if (!window.$_TSR) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',\n )\n }\n\n invariant()\n }\n\n const serializationAdapters = router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n\n if (serializationAdapters?.length) {\n const fromSerializableMap = new Map()\n serializationAdapters.forEach((adapter) => {\n fromSerializableMap.set(adapter.key, adapter.fromSerializable)\n })\n window.$_TSR.t = fromSerializableMap\n window.$_TSR.buffer.forEach((script) => script())\n }\n window.$_TSR.initialized = true\n\n if (!window.$_TSR.router) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n )\n }\n\n invariant()\n }\n\n const dehydratedRouter = window.$_TSR.router\n dehydratedRouter.matches.forEach((dehydratedMatch) => {\n dehydratedMatch.i = hydrateSsrMatchId(dehydratedMatch.i)\n })\n if (dehydratedRouter.lastMatchId) {\n dehydratedRouter.lastMatchId = hydrateSsrMatchId(\n dehydratedRouter.lastMatchId,\n )\n }\n const { manifest, dehydratedData, lastMatchId } = dehydratedRouter\n\n router.ssr = {\n manifest,\n }\n const meta = document.querySelector('meta[property=\"csp-nonce\"]') as\n | HTMLMetaElement\n | undefined\n const nonce = meta?.content\n router.options.ssr = {\n nonce,\n }\n\n // Allow the user to handle custom hydration data before matching routes.\n // This lets hydration install router config that affects matching, e.g. rewrites.\n await router.options.hydrate?.(dehydratedData)\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.stores.location.get())\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) =>\n router.loadRouteChunk(router.looseRoutesById[match.routeId]!),\n ),\n )\n\n function setMatchForcePending(match: AnyRouteMatch) {\n // usually the minPendingPromise is created in the Match component if a pending match is rendered\n // however, this might be too late if the match synchronously resolves\n const route = router.looseRoutesById[match.routeId]!\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n if (pendingMinMs) {\n const minPendingPromise = createControlledPromise<void>()\n match._nonReactive.minPendingPromise = minPendingPromise\n match._forcePending = true\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => {\n prev._nonReactive.minPendingPromise = undefined\n return {\n ...prev,\n _forcePending: undefined,\n }\n })\n }, pendingMinMs)\n }\n }\n\n function setRouteSsr(match: AnyRouteMatch) {\n const route = router.looseRoutesById[match.routeId]\n if (route) {\n route.options.ssr = match.ssr\n }\n }\n // Right after hydration and before the first render, we need to rehydrate each match\n // First step is to reyhdrate loaderData and __beforeLoadContext\n let firstNonSsrMatchIndex: number | undefined = undefined\n matches.forEach((match) => {\n const dehydratedMatch = dehydratedRouter.matches.find(\n (d) => d.i === match.id,\n )\n if (!dehydratedMatch) {\n match._nonReactive.dehydrated = false\n match.ssr = false\n setRouteSsr(match)\n return\n }\n\n hydrateMatch(match, dehydratedMatch)\n setRouteSsr(match)\n\n match._nonReactive.dehydrated = match.ssr !== false\n\n if (match.ssr === 'data-only' || match.ssr === false) {\n if (firstNonSsrMatchIndex === undefined) {\n firstNonSsrMatchIndex = match.index\n setMatchForcePending(match)\n }\n }\n })\n\n router.stores.setMatches(matches)\n\n // now that all necessary data is hydrated:\n // 1) fully reconstruct the route context\n // 2) execute `head()` and `scripts()` for each match\n const activeMatches = router.stores.matches.get()\n const location = router.stores.location.get()\n await Promise.all(\n activeMatches.map(async (match) => {\n try {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = activeMatches[match.index - 1]\n const parentContext = parentMatch?.context ?? router.options.context\n\n // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n // so run it again and merge route context\n if (route.options.context) {\n const contextFnContext: RouteContextOptions<any, any, any, any, any> =\n {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext ?? {},\n location,\n navigate: (opts: any) =>\n router.navigate({\n ...opts,\n _fromLocation: location,\n }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n routeId: route.id,\n }\n match.__routeContext =\n route.options.context(contextFnContext) ?? undefined\n }\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n ssr: router.options.ssr,\n matches: activeMatches,\n match,\n params: match.params,\n loaderData: match.loaderData,\n }\n const headFnContent = await route.options.head?.(assetContext)\n\n const scripts = await route.options.scripts?.(assetContext)\n\n match.meta = headFnContent?.meta\n match.links = headFnContent?.links\n match.headScripts = headFnContent?.scripts\n match.styles = headFnContent?.styles\n match.scripts = scripts\n } catch (err) {\n if (isNotFound(err)) {\n match.error = { isNotFound: true }\n console.error(\n `NotFound error during hydration for routeId: ${match.routeId}`,\n err,\n )\n } else {\n match.error = err as any\n console.error(\n `Error during hydration for route ${match.routeId}:`,\n err,\n )\n throw err\n }\n }\n }),\n )\n\n const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n if (!hasSsrFalseMatches && !isSpaMode) {\n matches.forEach((match) => {\n // remove the dehydrated flag since we won't run router.load() which would remove it\n match._nonReactive.dehydrated = undefined\n })\n // Mark the current location as resolved so that later load cycles\n // (e.g. preloads, invalidations) don't mistakenly detect a href change\n // (resolvedLocation defaults to undefined and router.load() is skipped\n // in the normal SSR hydration path).\n router.stores.resolvedLocation.set(router.stores.location.get())\n return routeChunkPromise\n }\n\n // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n const loadPromise = Promise.resolve()\n .then(() => router.load())\n .catch((err) => {\n console.error('Error during router hydration:', err)\n })\n\n // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n // this will prevent that other pending components are rendered but hydration is not blocked\n if (isSpaMode) {\n const match = matches[1]\n if (!match) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: Expected to find a match below the root match in SPA mode.',\n )\n }\n\n invariant()\n }\n setMatchForcePending(match)\n\n match._displayPending = true\n match._nonReactive.displayPendingPromise = loadPromise\n\n loadPromise.then(() => {\n router.batch(() => {\n // ensure router is not in status 'pending' anymore\n // this usually happens in Transitioner but if loading synchronously resolves,\n // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n if (router.stores.status.get() === 'pending') {\n router.stores.status.set('idle')\n router.stores.resolvedLocation.set(router.stores.location.get())\n }\n // hide the pending component once the load is finished\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n _displayPending: undefined,\n displayPendingPromise: undefined,\n }))\n })\n })\n }\n return routeChunkPromise\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ssr-client.js","names":[],"sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import { invariant } from '../invariant'\nimport { isNotFound } from '../not-found'\nimport { createControlledPromise } from '../utils'\nimport { hydrateSsrMatchId } from './ssr-match-id'\nimport type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'\nimport type { DehydratedMatch, TsrSsrGlobal } from './types'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { RouteContextOptions } from '../route'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare global {\n interface Window {\n [GLOBAL_TSR]?: TsrSsrGlobal\n [GLOBAL_SEROVAL]?: any\n }\n}\n\nfunction hydrateMatch(\n match: AnyRouteMatch,\n deyhydratedMatch: DehydratedMatch,\n): void {\n match.id = deyhydratedMatch.i\n match.__beforeLoadContext = deyhydratedMatch.b\n match.loaderData = deyhydratedMatch.l\n match.status = deyhydratedMatch.s\n match.ssr = deyhydratedMatch.ssr\n match.updatedAt = deyhydratedMatch.u\n match.error = deyhydratedMatch.e\n // Only hydrate global-not-found when a defined value is present in the\n // dehydrated payload. If omitted, preserve the value computed from the\n // current client location (important for SPA fallback HTML served at unknown\n // URLs, where dehydrated matches may come from `/` but client matching marks\n // root as globalNotFound).\n if (deyhydratedMatch.g !== undefined) {\n match.globalNotFound = deyhydratedMatch.g\n }\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n if (!window.$_TSR) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',\n )\n }\n\n invariant()\n }\n\n const serializationAdapters = router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n\n if (serializationAdapters?.length) {\n const fromSerializableMap = new Map()\n serializationAdapters.forEach((adapter) => {\n fromSerializableMap.set(adapter.key, adapter.fromSerializable)\n })\n window.$_TSR.t = fromSerializableMap\n window.$_TSR.buffer.forEach((script) => script())\n }\n window.$_TSR.initialized = true\n\n if (!window.$_TSR.router) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n )\n }\n\n invariant()\n }\n\n const dehydratedRouter = window.$_TSR.router\n dehydratedRouter.matches.forEach((dehydratedMatch) => {\n dehydratedMatch.i = hydrateSsrMatchId(dehydratedMatch.i)\n })\n if (dehydratedRouter.lastMatchId) {\n dehydratedRouter.lastMatchId = hydrateSsrMatchId(\n dehydratedRouter.lastMatchId,\n )\n }\n const { manifest, dehydratedData, lastMatchId } = dehydratedRouter\n\n router.ssr = {\n manifest,\n }\n const meta = document.querySelector('meta[property=\"csp-nonce\"]') as\n | HTMLMetaElement\n | undefined\n const nonce = meta?.content\n router.options.ssr = {\n nonce,\n }\n\n // Allow the user to handle custom hydration data before matching routes.\n // This lets hydration install router config that affects matching, e.g. rewrites.\n await router.options.hydrate?.(dehydratedData)\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.stores.location.get())\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) =>\n router.loadRouteChunk(router.looseRoutesById[match.routeId]!),\n ),\n )\n\n function setMatchForcePending(match: AnyRouteMatch) {\n // usually the minPendingPromise is created in the Match component if a pending match is rendered\n // however, this might be too late if the match synchronously resolves\n const route = router.looseRoutesById[match.routeId]!\n const pendingMinMs =\n route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n if (pendingMinMs) {\n const minPendingPromise = createControlledPromise<void>()\n match._nonReactive.minPendingPromise = minPendingPromise\n match._forcePending = true\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n router.updateMatch(match.id, (prev) => {\n prev._nonReactive.minPendingPromise = undefined\n return {\n ...prev,\n _forcePending: undefined,\n }\n })\n }, pendingMinMs)\n }\n }\n\n function setRouteSsr(match: AnyRouteMatch) {\n const route = router.looseRoutesById[match.routeId]\n if (route) {\n route.options.ssr = match.ssr\n }\n }\n // Right after hydration and before the first render, we need to rehydrate each match\n // First step is to reyhdrate loaderData and __beforeLoadContext\n let firstNonSsrMatchIndex: number | undefined = undefined\n matches.forEach((match) => {\n const dehydratedMatch = dehydratedRouter.matches.find(\n (d) => d.i === match.id,\n )\n if (!dehydratedMatch) {\n match._nonReactive.dehydrated = false\n match.ssr = false\n setRouteSsr(match)\n return\n }\n\n hydrateMatch(match, dehydratedMatch)\n setRouteSsr(match)\n\n match._nonReactive.dehydrated = match.ssr !== false\n\n if (match.ssr === 'data-only' || match.ssr === false) {\n if (firstNonSsrMatchIndex === undefined) {\n firstNonSsrMatchIndex = match.index\n setMatchForcePending(match)\n }\n }\n })\n\n router.stores.setMatches(matches)\n\n // now that all necessary data is hydrated:\n // 1) fully reconstruct the route context\n // 2) execute `head()` and `scripts()` for each match\n const activeMatches = router.stores.matches.get()\n const location = router.stores.location.get()\n await Promise.all(\n activeMatches.map(async (match) => {\n try {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = activeMatches[match.index - 1]\n const parentContext = parentMatch?.context ?? router.options.context\n\n // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n // so run it again and merge route context\n if (route.options.context) {\n const contextFnContext: RouteContextOptions<any, any, any, any, any> =\n {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext ?? {},\n location,\n navigate: (opts: any) =>\n router.navigate({\n ...opts,\n _fromLocation: location,\n }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n routeId: route.id,\n }\n match.__routeContext =\n route.options.context(contextFnContext) ?? undefined\n }\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n ssr: router.options.ssr,\n matches: activeMatches,\n match,\n params: match.params,\n loaderData: match.loaderData,\n }\n const headFnContent = await route.options.head?.(assetContext)\n\n const scripts = await route.options.scripts?.(assetContext)\n\n match.meta = headFnContent?.meta\n match.links = headFnContent?.links\n match.headScripts = headFnContent?.scripts\n match.styles = headFnContent?.styles\n match.scripts = scripts\n } catch (err) {\n if (isNotFound(err)) {\n match.error = { isNotFound: true }\n console.error(\n `NotFound error during hydration for routeId: ${match.routeId}`,\n err,\n )\n } else {\n match.error = err as any\n console.error(\n `Error during hydration for route ${match.routeId}:`,\n err,\n )\n throw err\n }\n }\n }),\n )\n\n const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n if (!hasSsrFalseMatches && !isSpaMode) {\n matches.forEach((match) => {\n // remove the dehydrated flag since we won't run router.load() which would remove it\n match._nonReactive.dehydrated = undefined\n })\n // Mark the current location as resolved so that later load cycles\n // (e.g. preloads, invalidations) don't mistakenly detect a href change\n // (resolvedLocation defaults to undefined and router.load() is skipped\n // in the normal SSR hydration path).\n router.stores.resolvedLocation.set(router.stores.location.get())\n return routeChunkPromise\n }\n\n // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n const loadPromise = Promise.resolve()\n .then(() => router.load())\n .catch((err) => {\n console.error('Error during router hydration:', err)\n })\n\n // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n // this will prevent that other pending components are rendered but hydration is not blocked\n if (isSpaMode) {\n const match = matches[1]\n if (!match) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: Expected to find a match below the root match in SPA mode.',\n )\n }\n\n invariant()\n }\n setMatchForcePending(match)\n\n match._displayPending = true\n match._nonReactive.displayPendingPromise = loadPromise\n\n loadPromise.then(() => {\n router.batch(() => {\n // ensure router is not in status 'pending' anymore\n // this usually happens in Transitioner but if loading synchronously resolves,\n // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n if (router.stores.status.get() === 'pending') {\n router.stores.status.set('idle')\n router.stores.resolvedLocation.set(router.stores.location.get())\n }\n // hide the pending component once the load is finished\n router.updateMatch(match.id, (prev) => ({\n ...prev,\n _displayPending: undefined,\n displayPendingPromise: undefined,\n }))\n })\n })\n }\n return routeChunkPromise\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-match-id.js","names":[],"sources":["../../../src/ssr/ssr-match-id.ts"],"sourcesContent":["export function dehydrateSsrMatchId(id: string): string {\n return id.replaceAll('/', '\\0')\n}\n\nexport function hydrateSsrMatchId(id: string): string {\n return id.replaceAll('\\0', '/').replaceAll('\\uFFFD', '/')\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,IAAoB;
|
|
1
|
+
{"version":3,"file":"ssr-match-id.js","names":[],"sources":["../../../src/ssr/ssr-match-id.ts"],"sourcesContent":["export function dehydrateSsrMatchId(id: string): string {\n return id.replaceAll('/', '\\0')\n}\n\nexport function hydrateSsrMatchId(id: string): string {\n return id.replaceAll('\\0', '/').replaceAll('\\uFFFD', '/')\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,IAAoB;CACtD,OAAO,GAAG,WAAW,KAAK,IAAI;AAChC;AAEA,SAAgB,kBAAkB,IAAoB;CACpD,OAAO,GAAG,WAAW,MAAM,GAAG,EAAE,WAAW,KAAU,GAAG;AAC1D"}
|
|
@@ -1,27 +1,12 @@
|
|
|
1
1
|
import { DehydratedMatch } from './types.js';
|
|
2
2
|
import { AnyRouter } from '../router.js';
|
|
3
3
|
import { AnyRouteMatch } from '../Matches.js';
|
|
4
|
-
import {
|
|
5
|
-
declare module '../router' {
|
|
6
|
-
interface ServerSsr {
|
|
7
|
-
setRenderFinished: () => void;
|
|
8
|
-
cleanup: () => void;
|
|
9
|
-
}
|
|
10
|
-
interface RouterEvents {
|
|
11
|
-
onInjectedHtml: {
|
|
12
|
-
type: 'onInjectedHtml';
|
|
13
|
-
};
|
|
14
|
-
onSerializationFinished: {
|
|
15
|
-
type: 'onSerializationFinished';
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
}
|
|
4
|
+
import { ManifestRouteAssets, ServerManifest } from '../manifest.js';
|
|
19
5
|
export declare function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch;
|
|
20
|
-
export declare function attachRouterServerSsrUtils({ router, manifest, getRequestAssets,
|
|
6
|
+
export declare function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, }: {
|
|
21
7
|
router: AnyRouter;
|
|
22
|
-
manifest:
|
|
23
|
-
getRequestAssets?: () =>
|
|
24
|
-
includeUnmatchedRouteAssets?: boolean;
|
|
8
|
+
manifest: ServerManifest | undefined;
|
|
9
|
+
getRequestAssets?: () => ManifestRouteAssets | undefined;
|
|
25
10
|
}): void;
|
|
26
11
|
/**
|
|
27
12
|
* Get the origin for the request.
|
|
@@ -2,7 +2,7 @@ import { decodePath } from "../utils.js";
|
|
|
2
2
|
import { invariant } from "../invariant.js";
|
|
3
3
|
import { createLRUCache } from "../lru-cache.js";
|
|
4
4
|
import { rootRouteId } from "../root.js";
|
|
5
|
-
import { createInlineCssPlaceholderAsset, createInlineCssStyleAsset, getStylesheetHref
|
|
5
|
+
import { createInlineCssPlaceholderAsset, createInlineCssStyleAsset, getStylesheetHref } from "../manifest.js";
|
|
6
6
|
import { GLOBAL_TSR, TSR_SCRIPT_BARRIER_ID } from "./constants.js";
|
|
7
7
|
import { makeSsrSerovalPlugin } from "./serializer/transformer.js";
|
|
8
8
|
import { defaultSerovalPlugins } from "./serializer/seroval-plugins.js";
|
|
@@ -31,38 +31,42 @@ function dehydrateMatch(match) {
|
|
|
31
31
|
}
|
|
32
32
|
const INITIAL_SCRIPTS = [getCrossReferenceHeader(SCOPE_ID), tsrScript_default];
|
|
33
33
|
var ScriptBuffer = class {
|
|
34
|
-
constructor(
|
|
34
|
+
constructor(injectScript) {
|
|
35
35
|
this._scriptBarrierLifted = false;
|
|
36
36
|
this._cleanedUp = false;
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
37
|
+
this._microtaskVersion = 0;
|
|
38
|
+
this._pendingMicrotaskVersion = 0;
|
|
39
|
+
this.injectScript = injectScript;
|
|
39
40
|
this._queue = INITIAL_SCRIPTS.slice();
|
|
40
41
|
}
|
|
41
42
|
enqueue(script) {
|
|
42
43
|
if (this._cleanedUp) return;
|
|
43
44
|
this._queue.push(script);
|
|
44
|
-
if (this._scriptBarrierLifted
|
|
45
|
-
this._pendingMicrotask = true;
|
|
46
|
-
queueMicrotask(() => {
|
|
47
|
-
this._pendingMicrotask = false;
|
|
48
|
-
this.injectBufferedScripts();
|
|
49
|
-
});
|
|
50
|
-
}
|
|
45
|
+
if (this._scriptBarrierLifted) this.scheduleInjectBufferedScripts();
|
|
51
46
|
}
|
|
52
47
|
liftBarrier() {
|
|
53
48
|
if (this._scriptBarrierLifted || this._cleanedUp) return;
|
|
54
49
|
this._scriptBarrierLifted = true;
|
|
55
|
-
if (this._queue.length > 0
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
if (this._queue.length > 0) this.scheduleInjectBufferedScripts();
|
|
51
|
+
}
|
|
52
|
+
scheduleInjectBufferedScripts() {
|
|
53
|
+
if (this._pendingMicrotaskVersion !== 0) return;
|
|
54
|
+
const pendingVersion = ++this._microtaskVersion;
|
|
55
|
+
this._pendingMicrotaskVersion = pendingVersion;
|
|
56
|
+
queueMicrotask(() => {
|
|
57
|
+
if (this._pendingMicrotaskVersion !== pendingVersion) return;
|
|
58
|
+
this._pendingMicrotaskVersion = 0;
|
|
59
|
+
this.injectBufferedScripts();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
clearPendingMicrotask() {
|
|
63
|
+
if (this._pendingMicrotaskVersion === 0) return;
|
|
64
|
+
this._pendingMicrotaskVersion = 0;
|
|
65
|
+
this._microtaskVersion++;
|
|
62
66
|
}
|
|
63
67
|
/**
|
|
64
68
|
* Flushes any pending scripts synchronously.
|
|
65
|
-
* Call this before
|
|
69
|
+
* Call this before signaling serialization finished to ensure all scripts are injected.
|
|
66
70
|
*
|
|
67
71
|
* IMPORTANT: Only injects if the barrier has been lifted. Before the barrier is lifted,
|
|
68
72
|
* scripts should remain in the queue so takeBufferedScripts() can retrieve them
|
|
@@ -70,33 +74,38 @@ var ScriptBuffer = class {
|
|
|
70
74
|
flush() {
|
|
71
75
|
if (!this._scriptBarrierLifted) return;
|
|
72
76
|
if (this._cleanedUp) return;
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
if (scriptsToInject && this.router?.serverSsr) this.router.serverSsr.injectScript(scriptsToInject);
|
|
77
|
+
this.clearPendingMicrotask();
|
|
78
|
+
this.injectBufferedScripts();
|
|
76
79
|
}
|
|
77
80
|
takeAll() {
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
return this.takeScripts(this._queue.length);
|
|
82
|
+
}
|
|
83
|
+
takeScripts(count) {
|
|
84
|
+
if (count <= 0) return void 0;
|
|
85
|
+
const bufferedScripts = this._queue.splice(0, count);
|
|
80
86
|
if (bufferedScripts.length === 0) return;
|
|
81
87
|
if (bufferedScripts.length === 1) return bufferedScripts[0] + ";document.currentScript.remove()";
|
|
82
88
|
return bufferedScripts.join(";") + ";document.currentScript.remove()";
|
|
83
89
|
}
|
|
90
|
+
hasPending() {
|
|
91
|
+
return this._queue.length > 0;
|
|
92
|
+
}
|
|
84
93
|
injectBufferedScripts() {
|
|
85
94
|
if (this._cleanedUp) return;
|
|
86
95
|
if (this._queue.length === 0) return;
|
|
87
96
|
const scriptsToInject = this.takeAll();
|
|
88
|
-
if (scriptsToInject
|
|
97
|
+
if (scriptsToInject) this.injectScript?.(scriptsToInject);
|
|
89
98
|
}
|
|
90
99
|
cleanup() {
|
|
91
100
|
this._cleanedUp = true;
|
|
101
|
+
this.clearPendingMicrotask();
|
|
92
102
|
this._queue = [];
|
|
93
|
-
this.
|
|
103
|
+
this.injectScript = void 0;
|
|
94
104
|
}
|
|
95
105
|
};
|
|
96
106
|
const isProd = process.env.NODE_ENV === "production";
|
|
97
107
|
const MANIFEST_CACHE_SIZE = 100;
|
|
98
108
|
const manifestCaches = /* @__PURE__ */ new WeakMap();
|
|
99
|
-
const inlineCssCaches = /* @__PURE__ */ new WeakMap();
|
|
100
109
|
function getManifestCache(manifest) {
|
|
101
110
|
const cache = manifestCaches.get(manifest);
|
|
102
111
|
if (cache) return cache;
|
|
@@ -104,102 +113,178 @@ function getManifestCache(manifest) {
|
|
|
104
113
|
manifestCaches.set(manifest, newCache);
|
|
105
114
|
return newCache;
|
|
106
115
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
function getInlineCssForPreparedRoutes(manifest, preparedRoutes) {
|
|
117
|
+
if (preparedRoutes.inlineCss !== void 0) return preparedRoutes.inlineCss;
|
|
118
|
+
const styles = manifest.inlineCss?.styles;
|
|
119
|
+
const hrefs = preparedRoutes.inlineCssHrefs;
|
|
120
|
+
if (!styles || !hrefs?.length) return void 0;
|
|
121
|
+
let css = "";
|
|
122
|
+
for (const href of hrefs) css += styles[href];
|
|
123
|
+
preparedRoutes.inlineCss = css;
|
|
124
|
+
return css;
|
|
113
125
|
}
|
|
114
|
-
function
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const seen = /* @__PURE__ */ new Set();
|
|
118
|
-
const hrefs = [];
|
|
119
|
-
for (const match of matches) {
|
|
120
|
-
const assets = manifest?.routes[match.routeId]?.assets ?? [];
|
|
121
|
-
for (const asset of assets) {
|
|
122
|
-
const href = getStylesheetHref(asset);
|
|
123
|
-
if (!href || seen.has(href) || styles[href] === void 0) continue;
|
|
124
|
-
seen.add(href);
|
|
125
|
-
hrefs.push(href);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return hrefs;
|
|
126
|
+
function getInlineCssAssetForPreparedRoutes(manifest, preparedRoutes) {
|
|
127
|
+
const css = getInlineCssForPreparedRoutes(manifest, preparedRoutes);
|
|
128
|
+
return css === void 0 ? void 0 : createInlineCssStyleAsset(css);
|
|
129
129
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
function getMatchedRoutesCacheKey(matches) {
|
|
131
|
+
let cacheKey = "";
|
|
132
|
+
for (let i = 0; i < matches.length; i++) cacheKey += (i === 0 ? "" : "\0") + matches[i].routeId;
|
|
133
|
+
return cacheKey;
|
|
134
|
+
}
|
|
135
|
+
function getPreparedMatchedManifestRoutes(manifest, matches, cacheKey) {
|
|
134
136
|
if (isProd) {
|
|
135
|
-
const cached =
|
|
136
|
-
if (cached
|
|
137
|
+
const cached = getManifestCache(manifest).get(cacheKey);
|
|
138
|
+
if (cached) return cached;
|
|
137
139
|
}
|
|
138
|
-
const
|
|
139
|
-
if (isProd)
|
|
140
|
-
return
|
|
140
|
+
const preparedRoutes = prepareMatchedManifestRoutes(manifest, matches);
|
|
141
|
+
if (isProd) getManifestCache(manifest).set(cacheKey, preparedRoutes);
|
|
142
|
+
return preparedRoutes;
|
|
141
143
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
144
|
+
function prepareMatchedManifestRoutes(manifest, matches) {
|
|
145
|
+
const inlineStyles = manifest.inlineCss?.styles;
|
|
146
|
+
const routes = {};
|
|
147
|
+
if (!inlineStyles) {
|
|
148
|
+
for (const match of matches) {
|
|
149
|
+
const route = manifest.routes[match.routeId];
|
|
150
|
+
if (route) routes[match.routeId] = route;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
routes,
|
|
154
|
+
hasStrippedRoutes: false
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const inlineCssHrefs = [];
|
|
158
|
+
const seenInlineCssHrefs = /* @__PURE__ */ new Set();
|
|
159
|
+
let hasStrippedRoutes = false;
|
|
160
|
+
for (const match of matches) {
|
|
161
|
+
const routeId = match.routeId;
|
|
162
|
+
const route = manifest.routes[routeId];
|
|
163
|
+
if (!route) continue;
|
|
164
|
+
const nextRoute = stripInlinedStylesheetAssetsFromRoute(inlineStyles, route, inlineCssHrefs, seenInlineCssHrefs);
|
|
165
|
+
if (nextRoute !== route) hasStrippedRoutes = true;
|
|
166
|
+
routes[routeId] = nextRoute;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
routes,
|
|
170
|
+
hasStrippedRoutes,
|
|
171
|
+
...inlineCssHrefs.length ? { inlineCssHrefs } : {}
|
|
172
|
+
};
|
|
146
173
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const assets = route.assets?.filter((asset) => !isInlinableStylesheet(manifest, asset));
|
|
174
|
+
function stripInlinedStylesheetAssetsFromRoute(inlineStyles, route, inlineCssHrefs, seenInlineCssHrefs) {
|
|
175
|
+
const css = route.css;
|
|
176
|
+
if (!css) return route;
|
|
177
|
+
if (css.length === 0) {
|
|
152
178
|
const nextRoute = { ...route };
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
nextRoutes[routeId] = nextRoute;
|
|
179
|
+
delete nextRoute.css;
|
|
180
|
+
return nextRoute;
|
|
156
181
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
182
|
+
let cssLinks;
|
|
183
|
+
for (let i = 0; i < css.length; i++) {
|
|
184
|
+
const link = css[i];
|
|
185
|
+
const href = getStylesheetHref(link);
|
|
186
|
+
if (inlineStyles[href] === void 0) {
|
|
187
|
+
if (cssLinks) cssLinks.push(link);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (!seenInlineCssHrefs.has(href)) {
|
|
191
|
+
seenInlineCssHrefs.add(href);
|
|
192
|
+
inlineCssHrefs.push(href);
|
|
193
|
+
}
|
|
194
|
+
if (!cssLinks) cssLinks = css.slice(0, i);
|
|
163
195
|
}
|
|
164
|
-
return
|
|
196
|
+
if (!cssLinks) return route;
|
|
197
|
+
if (cssLinks.length > 0) return {
|
|
198
|
+
...route,
|
|
199
|
+
css: cssLinks
|
|
200
|
+
};
|
|
201
|
+
const nextRoute = { ...route };
|
|
202
|
+
delete nextRoute.css;
|
|
203
|
+
return nextRoute;
|
|
165
204
|
}
|
|
166
|
-
function
|
|
205
|
+
function hasRouteAssets(route) {
|
|
206
|
+
return !!route.scripts?.length || !!route.css?.length;
|
|
207
|
+
}
|
|
208
|
+
function hasRequestAssets(assets) {
|
|
209
|
+
return !!assets && (!!assets.preloads?.length || hasRouteAssets(assets));
|
|
210
|
+
}
|
|
211
|
+
function mergeRequestAssetsIntoRootRoute(rootRoute, requestAssets) {
|
|
212
|
+
const preloads = requestAssets?.preloads?.length ? [...requestAssets.preloads, ...rootRoute?.preloads ?? []] : rootRoute?.preloads;
|
|
213
|
+
const scripts = requestAssets?.scripts?.length ? [...requestAssets.scripts, ...rootRoute?.scripts ?? []] : rootRoute?.scripts;
|
|
214
|
+
const cssLinks = requestAssets?.css?.length ? [...requestAssets.css, ...rootRoute?.css ?? []] : rootRoute?.css;
|
|
215
|
+
return {
|
|
216
|
+
...rootRoute ?? {},
|
|
217
|
+
...preloads?.length ? { preloads } : {},
|
|
218
|
+
...scripts?.length ? { scripts } : {},
|
|
219
|
+
...cssLinks?.length ? { css: cssLinks } : {}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function attachRouterServerSsrUtils({ router, manifest, getRequestAssets }) {
|
|
167
223
|
router.ssr = { get manifest() {
|
|
168
224
|
if (!manifest) return manifest;
|
|
169
225
|
const requestAssets = getRequestAssets?.();
|
|
170
|
-
const
|
|
171
|
-
|
|
226
|
+
const matches = router.stores.matches.get();
|
|
227
|
+
const hasAssets = hasRequestAssets(requestAssets);
|
|
228
|
+
if (!hasAssets && !manifest.inlineCss) return manifest;
|
|
229
|
+
let inlineCssAsset;
|
|
230
|
+
let routes = manifest.routes;
|
|
231
|
+
if (manifest.inlineCss) {
|
|
232
|
+
const preparedManifest = getPreparedMatchedManifestRoutes(manifest, matches, getMatchedRoutesCacheKey(matches));
|
|
233
|
+
inlineCssAsset = getInlineCssAssetForPreparedRoutes(manifest, preparedManifest);
|
|
234
|
+
if (preparedManifest.hasStrippedRoutes) routes = {
|
|
235
|
+
...manifest.routes,
|
|
236
|
+
...preparedManifest.routes
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (!hasAssets) return {
|
|
240
|
+
...manifest.scriptFormat ? { scriptFormat: manifest.scriptFormat } : {},
|
|
241
|
+
...inlineCssAsset ? { inlineStyle: inlineCssAsset } : {},
|
|
242
|
+
routes
|
|
243
|
+
};
|
|
244
|
+
const rootRoute = routes[rootRouteId];
|
|
172
245
|
return {
|
|
173
|
-
...manifest,
|
|
246
|
+
...manifest.scriptFormat ? { scriptFormat: manifest.scriptFormat } : {},
|
|
247
|
+
...inlineCssAsset ? { inlineStyle: inlineCssAsset } : {},
|
|
174
248
|
routes: {
|
|
175
|
-
...
|
|
176
|
-
[rootRouteId]:
|
|
177
|
-
...manifest.routes[rootRouteId],
|
|
178
|
-
assets: [
|
|
179
|
-
...requestAssets ?? [],
|
|
180
|
-
...inlineCssAsset ? [inlineCssAsset] : [],
|
|
181
|
-
...manifest.routes["__root__"]?.assets ?? []
|
|
182
|
-
]
|
|
183
|
-
}
|
|
249
|
+
...routes,
|
|
250
|
+
[rootRouteId]: mergeRequestAssetsIntoRootRoute(rootRoute, requestAssets)
|
|
184
251
|
}
|
|
185
252
|
};
|
|
186
253
|
} };
|
|
187
254
|
let _dehydrated = false;
|
|
188
255
|
let _serializationFinished = false;
|
|
256
|
+
let streamFastPathReserved = false;
|
|
189
257
|
const renderFinishedListeners = [];
|
|
258
|
+
const injectedHtmlListeners = [];
|
|
190
259
|
const serializationFinishedListeners = [];
|
|
191
|
-
const
|
|
260
|
+
const cleanupListeners = [];
|
|
261
|
+
let cleanupStarted = false;
|
|
192
262
|
let injectedHtmlBuffer = "";
|
|
193
|
-
|
|
263
|
+
const callListeners = (listeners, errorPrefix) => {
|
|
264
|
+
const snapshot = listeners.slice();
|
|
265
|
+
for (const l of snapshot) try {
|
|
266
|
+
l();
|
|
267
|
+
} catch (err) {
|
|
268
|
+
console.error(`${errorPrefix}:`, err);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const removeListener = (listeners, listener) => {
|
|
272
|
+
const index = listeners.indexOf(listener);
|
|
273
|
+
if (index >= 0) listeners.splice(index, 1);
|
|
274
|
+
};
|
|
275
|
+
const scriptBuffer = new ScriptBuffer((script) => {
|
|
276
|
+
serverSsr.injectScript(script);
|
|
277
|
+
});
|
|
278
|
+
const serverSsr = {
|
|
194
279
|
injectHtml: (html) => {
|
|
195
|
-
if (!html) return;
|
|
280
|
+
if (!html || cleanupStarted) return;
|
|
196
281
|
injectedHtmlBuffer += html;
|
|
197
|
-
|
|
282
|
+
callListeners(injectedHtmlListeners, "SSR injected HTML listener error");
|
|
198
283
|
},
|
|
199
284
|
injectScript: (script) => {
|
|
200
|
-
if (!script) return;
|
|
285
|
+
if (!script || cleanupStarted) return;
|
|
201
286
|
const html = `<script${router.options.ssr?.nonce ? ` nonce='${router.options.ssr.nonce}'` : ""}>${script}<\/script>`;
|
|
202
|
-
|
|
287
|
+
serverSsr.injectHtml(html);
|
|
203
288
|
},
|
|
204
289
|
dehydrate: async (opts) => {
|
|
205
290
|
if (_dehydrated) {
|
|
@@ -211,27 +296,19 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includ
|
|
|
211
296
|
const matches = matchesToDehydrate.map(dehydrateMatch);
|
|
212
297
|
let manifestToDehydrate = void 0;
|
|
213
298
|
if (manifest) {
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (currentRouteIds.has(routeId)) nextFilteredRoutes[routeId] = routeManifest;
|
|
224
|
-
else if (includeUnmatchedRouteAssets && routeManifest.assets && routeManifest.assets.length > 0) nextFilteredRoutes[routeId] = { assets: routeManifest.assets };
|
|
225
|
-
}
|
|
226
|
-
filteredRoutes = stripInlinedStylesheetAssets(manifest, nextFilteredRoutes, matchesToDehydrate);
|
|
227
|
-
if (isProd) getManifestCache(manifest).set(manifestCacheKey, filteredRoutes);
|
|
228
|
-
}
|
|
229
|
-
manifestToDehydrate = { routes: { ...filteredRoutes } };
|
|
230
|
-
if (opts?.requestAssets?.length) {
|
|
299
|
+
const cacheKey = getMatchedRoutesCacheKey(matchesToDehydrate);
|
|
300
|
+
const preparedManifest = getPreparedMatchedManifestRoutes(manifest, matchesToDehydrate, cacheKey);
|
|
301
|
+
manifestToDehydrate = {
|
|
302
|
+
...manifest.scriptFormat ? { scriptFormat: manifest.scriptFormat } : {},
|
|
303
|
+
...preparedManifest.inlineCssHrefs ? { inlineStyle: createInlineCssPlaceholderAsset() } : {},
|
|
304
|
+
routes: preparedManifest.routes
|
|
305
|
+
};
|
|
306
|
+
const requestAssets = opts?.requestAssets;
|
|
307
|
+
if (hasRequestAssets(requestAssets)) {
|
|
231
308
|
const existingRoot = manifestToDehydrate.routes[rootRouteId];
|
|
232
|
-
manifestToDehydrate.routes
|
|
233
|
-
...
|
|
234
|
-
|
|
309
|
+
manifestToDehydrate.routes = {
|
|
310
|
+
...manifestToDehydrate.routes,
|
|
311
|
+
[rootRouteId]: mergeRequestAssetsIntoRootRoute(existingRoot, requestAssets)
|
|
235
312
|
};
|
|
236
313
|
}
|
|
237
314
|
}
|
|
@@ -247,18 +324,25 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includ
|
|
|
247
324
|
const trackPlugins = { didRun: false };
|
|
248
325
|
const serializationAdapters = router.options.serializationAdapters;
|
|
249
326
|
const plugins = serializationAdapters ? serializationAdapters.map((t) => /* @__PURE__ */ makeSsrSerovalPlugin(t, trackPlugins)).concat(defaultSerovalPlugins) : defaultSerovalPlugins;
|
|
327
|
+
let serializationCompleteSignaled = false;
|
|
250
328
|
const signalSerializationComplete = () => {
|
|
329
|
+
if (serializationCompleteSignaled || cleanupStarted) return;
|
|
330
|
+
serializationCompleteSignaled = true;
|
|
251
331
|
_serializationFinished = true;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
332
|
+
const listeners = serializationFinishedListeners.slice();
|
|
333
|
+
serializationFinishedListeners.length = 0;
|
|
334
|
+
for (const l of listeners) try {
|
|
335
|
+
l();
|
|
255
336
|
} catch (err) {
|
|
256
337
|
console.error("Serialization listener error:", err);
|
|
257
|
-
} finally {
|
|
258
|
-
serializationFinishedListeners.length = 0;
|
|
259
|
-
renderFinishedListeners.length = 0;
|
|
260
338
|
}
|
|
261
339
|
};
|
|
340
|
+
const finishScriptSerialization = () => {
|
|
341
|
+
if (serializationCompleteSignaled || cleanupStarted) return;
|
|
342
|
+
scriptBuffer.enqueue(GLOBAL_TSR + ".e()");
|
|
343
|
+
scriptBuffer.flush();
|
|
344
|
+
signalSerializationComplete();
|
|
345
|
+
};
|
|
262
346
|
crossSerializeStream(dehydratedRouter, {
|
|
263
347
|
refs: /* @__PURE__ */ new Map(),
|
|
264
348
|
plugins,
|
|
@@ -270,13 +354,11 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includ
|
|
|
270
354
|
onError: (err) => {
|
|
271
355
|
console.error("Serialization error:", err);
|
|
272
356
|
if (err && err.stack) console.error(err.stack);
|
|
273
|
-
|
|
357
|
+
finishScriptSerialization();
|
|
274
358
|
},
|
|
275
359
|
scopeId: SCOPE_ID,
|
|
276
360
|
onDone: () => {
|
|
277
|
-
|
|
278
|
-
scriptBuffer.flush();
|
|
279
|
-
signalSerializationComplete();
|
|
361
|
+
finishScriptSerialization();
|
|
280
362
|
}
|
|
281
363
|
});
|
|
282
364
|
},
|
|
@@ -286,20 +368,54 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includ
|
|
|
286
368
|
isSerializationFinished() {
|
|
287
369
|
return _serializationFinished;
|
|
288
370
|
},
|
|
289
|
-
|
|
290
|
-
|
|
371
|
+
reserveStreamFastPath() {
|
|
372
|
+
if (!cleanupStarted && _serializationFinished && !streamFastPathReserved && renderFinishedListeners.length === 0 && !injectedHtmlBuffer && !scriptBuffer.hasPending()) {
|
|
373
|
+
streamFastPathReserved = true;
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
return false;
|
|
377
|
+
},
|
|
378
|
+
onInjectedHtml: (listener) => {
|
|
379
|
+
if (cleanupStarted) return () => {};
|
|
380
|
+
injectedHtmlListeners.push(listener);
|
|
381
|
+
return () => removeListener(injectedHtmlListeners, listener);
|
|
382
|
+
},
|
|
383
|
+
onRenderFinished: (listener) => {
|
|
384
|
+
if (cleanupStarted || streamFastPathReserved) return;
|
|
385
|
+
renderFinishedListeners.push(listener);
|
|
386
|
+
},
|
|
387
|
+
onSerializationFinished: (listener) => {
|
|
388
|
+
if (cleanupStarted) return () => {};
|
|
389
|
+
if (_serializationFinished && !cleanupStarted) {
|
|
390
|
+
try {
|
|
391
|
+
listener();
|
|
392
|
+
} catch (err) {
|
|
393
|
+
console.error("Serialization listener error:", err);
|
|
394
|
+
}
|
|
395
|
+
return () => {};
|
|
396
|
+
}
|
|
397
|
+
serializationFinishedListeners.push(listener);
|
|
398
|
+
return () => removeListener(serializationFinishedListeners, listener);
|
|
399
|
+
},
|
|
400
|
+
onCleanup: (listener) => {
|
|
401
|
+
if (cleanupStarted) return;
|
|
402
|
+
cleanupListeners.push(listener);
|
|
403
|
+
},
|
|
291
404
|
setRenderFinished: () => {
|
|
292
|
-
|
|
293
|
-
|
|
405
|
+
if (cleanupStarted) return;
|
|
406
|
+
scriptBuffer.liftBarrier();
|
|
407
|
+
const listeners = renderFinishedListeners.slice();
|
|
408
|
+
renderFinishedListeners.length = 0;
|
|
409
|
+
for (const l of listeners) try {
|
|
410
|
+
l();
|
|
294
411
|
} catch (err) {
|
|
295
412
|
console.error("Error in render finished listener:", err);
|
|
296
|
-
} finally {
|
|
297
|
-
renderFinishedListeners.length = 0;
|
|
298
413
|
}
|
|
299
|
-
scriptBuffer.
|
|
414
|
+
if (_serializationFinished) scriptBuffer.flush();
|
|
300
415
|
},
|
|
301
416
|
takeBufferedScripts() {
|
|
302
417
|
const scripts = scriptBuffer.takeAll();
|
|
418
|
+
if (!scripts) return void 0;
|
|
303
419
|
return {
|
|
304
420
|
tag: "script",
|
|
305
421
|
attrs: {
|
|
@@ -320,14 +436,29 @@ function attachRouterServerSsrUtils({ router, manifest, getRequestAssets, includ
|
|
|
320
436
|
return buffered;
|
|
321
437
|
},
|
|
322
438
|
cleanup() {
|
|
323
|
-
if (
|
|
439
|
+
if (cleanupStarted) return;
|
|
440
|
+
cleanupStarted = true;
|
|
441
|
+
const listeners = cleanupListeners.slice();
|
|
442
|
+
cleanupListeners.length = 0;
|
|
443
|
+
for (const l of listeners) try {
|
|
444
|
+
l();
|
|
445
|
+
} catch (err) {
|
|
446
|
+
console.error("Error in SSR cleanup listener:", err);
|
|
447
|
+
}
|
|
324
448
|
renderFinishedListeners.length = 0;
|
|
449
|
+
injectedHtmlListeners.length = 0;
|
|
325
450
|
serializationFinishedListeners.length = 0;
|
|
326
451
|
injectedHtmlBuffer = "";
|
|
327
452
|
scriptBuffer.cleanup();
|
|
328
453
|
router.serverSsr = void 0;
|
|
329
454
|
}
|
|
330
455
|
};
|
|
456
|
+
router.serverSsr = serverSsr;
|
|
457
|
+
for (const listener of router.serverSsrLifecycle?.onServerSsrAttach ?? []) try {
|
|
458
|
+
listener(serverSsr);
|
|
459
|
+
} catch (err) {
|
|
460
|
+
console.error("SSR attach listener error:", err);
|
|
461
|
+
}
|
|
331
462
|
}
|
|
332
463
|
/**
|
|
333
464
|
* Get the origin for the request.
|