@tanstack/router-core 1.123.0 → 1.124.0
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/Matches.d.cts +1 -0
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +1 -0
- package/dist/cjs/router.cjs +24 -10
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-client.cjs +1 -0
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/esm/Matches.d.ts +1 -0
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/route.d.ts +1 -0
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.js +24 -10
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/ssr-client.js +1 -0
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/package.json +1 -1
- package/src/Matches.ts +1 -0
- package/src/route.ts +1 -0
- package/src/router.ts +35 -17
- package/src/ssr/ssr-client.ts +1 -0
|
@@ -106,6 +106,7 @@ async function hydrate(router) {
|
|
|
106
106
|
match.meta = headFnContent == null ? void 0 : headFnContent.meta;
|
|
107
107
|
match.links = headFnContent == null ? void 0 : headFnContent.links;
|
|
108
108
|
match.headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
|
|
109
|
+
match.styles = headFnContent == null ? void 0 : headFnContent.styles;
|
|
109
110
|
match.scripts = scripts;
|
|
110
111
|
})
|
|
111
112
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-client.js","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { isPlainObject } from '../utils'\nimport { tsrSerializer } from '../serializer'\nimport type { DeferredPromiseState } from '../defer'\nimport type { MakeRouteMatch } from '../Matches'\nimport type { AnyRouter, ControllablePromise } from '../router'\nimport type { Manifest } from '../manifest'\nimport type { RouteContextOptions } from '../route'\n\ndeclare global {\n interface Window {\n __TSR_SSR__?: TsrSsrGlobal\n }\n}\n\nexport interface TsrSsrGlobal {\n matches: Array<SsrMatch>\n streamedValues: Record<\n string,\n {\n value: any\n parsed: any\n }\n >\n cleanScripts: () => void\n dehydrated?: any\n initMatch: (match: SsrMatch) => void\n resolvePromise: (opts: {\n matchId: string\n id: number\n promiseState: DeferredPromiseState<any>\n }) => void\n injectChunk: (opts: { matchId: string; id: number; chunk: string }) => void\n closeStream: (opts: { matchId: string; id: number }) => void\n}\n\nexport interface SsrMatch {\n id: string\n __beforeLoadContext: string\n loaderData?: string\n error?: string\n extracted?: Array<ClientExtractedEntry>\n updatedAt: MakeRouteMatch['updatedAt']\n status: MakeRouteMatch['status']\n}\n\nexport type ClientExtractedEntry =\n | ClientExtractedStream\n | ClientExtractedPromise\n\nexport interface ClientExtractedPromise extends ClientExtractedBaseEntry {\n type: 'promise'\n value?: ControllablePromise<any>\n}\n\nexport interface ClientExtractedStream extends ClientExtractedBaseEntry {\n type: 'stream'\n value?: ReadableStream & { controller?: ReadableStreamDefaultController }\n}\n\nexport interface ClientExtractedBaseEntry {\n type: string\n path: Array<string>\n}\n\nexport interface ResolvePromiseState {\n matchId: string\n id: number\n promiseState: DeferredPromiseState<any>\n}\n\nexport interface DehydratedRouter {\n manifest: Manifest | undefined\n dehydratedData: any\n lastMatchId: string\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n invariant(\n window.__TSR_SSR__?.dehydrated,\n 'Expected to find a dehydrated data on window.__TSR_SSR__.dehydrated... but we did not. Please file an issue!',\n )\n\n const { manifest, dehydratedData, lastMatchId } = tsrSerializer.parse(\n window.__TSR_SSR__.dehydrated,\n ) as DehydratedRouter\n\n router.ssr = {\n manifest,\n serializer: tsrSerializer,\n }\n\n router.clientSsr = {\n getStreamedValue: <T>(key: string): T | undefined => {\n if (router.isServer) {\n return undefined\n }\n\n const streamedValue = window.__TSR_SSR__?.streamedValues[key]\n\n if (!streamedValue) {\n return\n }\n\n if (!streamedValue.parsed) {\n streamedValue.parsed = router.ssr!.serializer.parse(streamedValue.value)\n }\n\n return streamedValue.parsed\n },\n }\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.state.location)\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) => {\n const route = router.looseRoutesById[match.routeId]!\n return router.loadRouteChunk(route)\n }),\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 matches.forEach((match) => {\n const dehydratedMatch = window.__TSR_SSR__!.matches.find(\n (d) => d.id === match.id,\n )\n\n if (!dehydratedMatch) {\n return\n }\n\n Object.assign(match, dehydratedMatch)\n\n // Handle beforeLoadContext\n if (dehydratedMatch.__beforeLoadContext) {\n match.__beforeLoadContext = router.ssr!.serializer.parse(\n dehydratedMatch.__beforeLoadContext,\n ) as any\n }\n\n // Handle loaderData\n if (dehydratedMatch.loaderData) {\n match.loaderData = router.ssr!.serializer.parse(\n dehydratedMatch.loaderData,\n )\n }\n\n // Handle error\n if (dehydratedMatch.error) {\n match.error = router.ssr!.serializer.parse(dehydratedMatch.error)\n }\n\n // Handle extracted\n ;(match as unknown as SsrMatch).extracted?.forEach((ex) => {\n deepMutableSetByPath(match, ['loaderData', ...ex.path], ex.value)\n })\n\n return match\n })\n\n router.__store.setState((s) => {\n return {\n ...s,\n matches,\n }\n })\n\n // Allow the user to handle custom hydration data\n await router.options.hydrate?.(dehydratedData)\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 await Promise.all(\n router.state.matches.map(async (match) => {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = router.state.matches[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 const contextFnContext: RouteContextOptions<any, any, any, any> = {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext,\n location: router.state.location,\n navigate: (opts: any) =>\n router.navigate({ ...opts, _fromLocation: router.state.location }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n }\n match.__routeContext = route.options.context?.(contextFnContext) ?? {}\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n matches: router.state.matches,\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.scripts = scripts\n }),\n )\n\n if (matches[matches.length - 1]!.id !== lastMatchId) {\n return await Promise.all([routeChunkPromise, router.load()])\n }\n\n return routeChunkPromise\n}\n\nfunction deepMutableSetByPath<T>(obj: T, path: Array<string>, value: any) {\n // mutable set by path retaining array and object references\n if (path.length === 1) {\n ;(obj as any)[path[0]!] = value\n }\n\n const [key, ...rest] = path\n\n if (Array.isArray(obj)) {\n deepMutableSetByPath(obj[Number(key)], rest, value)\n } else if (isPlainObject(obj)) {\n deepMutableSetByPath((obj as any)[key!], rest, value)\n }\n}\n"],"names":["_a","_b","_c"],"mappings":";;;AA6EA,eAAsB,QAAQ,QAAiC;;AAC7D;AAAA,KACE,YAAO,gBAAP,mBAAoB;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,EAAE,UAAU,gBAAgB,gBAAgB,cAAc;AAAA,IAC9D,OAAO,YAAY;AAAA,EACrB;AAEA,SAAO,MAAM;AAAA,IACX;AAAA,IACA,YAAY;AAAA,EACd;AAEA,SAAO,YAAY;AAAA,IACjB,kBAAkB,CAAI,QAA+B;;AACnD,UAAI,OAAO,UAAU;AACZ,eAAA;AAAA,MAAA;AAGT,YAAM,iBAAgBA,MAAA,OAAO,gBAAP,gBAAAA,IAAoB,eAAe;AAEzD,UAAI,CAAC,eAAe;AAClB;AAAA,MAAA;AAGE,UAAA,CAAC,cAAc,QAAQ;AACzB,sBAAc,SAAS,OAAO,IAAK,WAAW,MAAM,cAAc,KAAK;AAAA,MAAA;AAGzE,aAAO,cAAc;AAAA,IAAA;AAAA,EAEzB;AAGA,QAAM,UAAU,OAAO,YAAY,OAAO,MAAM,QAAQ;AAGxD,QAAM,oBAAoB,QAAQ;AAAA,IAChC,QAAQ,IAAI,CAAC,UAAU;AACrB,YAAM,QAAQ,OAAO,gBAAgB,MAAM,OAAO;AAC3C,aAAA,OAAO,eAAe,KAAK;AAAA,IACnC,CAAA;AAAA,EACH;AAIQ,UAAA,QAAQ,CAAC,UAAU;;AACnB,UAAA,kBAAkB,OAAO,YAAa,QAAQ;AAAA,MAClD,CAAC,MAAM,EAAE,OAAO,MAAM;AAAA,IACxB;AAEA,QAAI,CAAC,iBAAiB;AACpB;AAAA,IAAA;AAGK,WAAA,OAAO,OAAO,eAAe;AAGpC,QAAI,gBAAgB,qBAAqB;AACjC,YAAA,sBAAsB,OAAO,IAAK,WAAW;AAAA,QACjD,gBAAgB;AAAA,MAClB;AAAA,IAAA;AAIF,QAAI,gBAAgB,YAAY;AACxB,YAAA,aAAa,OAAO,IAAK,WAAW;AAAA,QACxC,gBAAgB;AAAA,MAClB;AAAA,IAAA;AAIF,QAAI,gBAAgB,OAAO;AACzB,YAAM,QAAQ,OAAO,IAAK,WAAW,MAAM,gBAAgB,KAAK;AAAA,IAAA;AAIhE,KAAAA,MAAA,MAA8B,cAA9B,gBAAAA,IAAyC,QAAQ,CAAC,OAAO;AACpC,2BAAA,OAAO,CAAC,cAAc,GAAG,GAAG,IAAI,GAAG,GAAG,KAAK;AAAA,IAAA;AAG3D,WAAA;AAAA,EAAA,CACR;AAEM,SAAA,QAAQ,SAAS,CAAC,MAAM;AACtB,WAAA;AAAA,MACL,GAAG;AAAA,MACH;AAAA,IACF;AAAA,EAAA,CACD;AAGK,UAAA,kBAAO,SAAQ,YAAf,4BAAyB;AAK/B,QAAM,QAAQ;AAAA,IACZ,OAAO,MAAM,QAAQ,IAAI,OAAO,UAAU;;AACxC,YAAM,QAAQ,OAAO,gBAAgB,MAAM,OAAO;AAElD,YAAM,cAAc,OAAO,MAAM,QAAQ,MAAM,QAAQ,CAAC;AACxD,YAAM,iBAAgB,2CAAa,YAAW,OAAO,QAAQ,WAAW,CAAC;AAIzE,YAAM,mBAA4D;AAAA,QAChE,MAAM,MAAM;AAAA,QACZ,QAAQ,MAAM;AAAA,QACd,SAAS;AAAA,QACT,UAAU,OAAO,MAAM;AAAA,QACvB,UAAU,CAAC,SACT,OAAO,SAAS,EAAE,GAAG,MAAM,eAAe,OAAO,MAAM,SAAA,CAAU;AAAA,QACnE,eAAe,OAAO;AAAA,QACtB,OAAO,MAAM;AAAA,QACb,iBAAiB,MAAM;AAAA,QACvB,SAAS;AAAA,QACT;AAAA,MACF;AACA,YAAM,mBAAiBC,OAAAD,MAAA,MAAM,SAAQ,YAAd,gBAAAC,IAAA,KAAAD,KAAwB,sBAAqB,CAAC;AAErE,YAAM,UAAU;AAAA,QACd,GAAG;AAAA,QACH,GAAG,MAAM;AAAA,QACT,GAAG,MAAM;AAAA,MACX;AAEA,YAAM,eAAe;AAAA,QACnB,SAAS,OAAO,MAAM;AAAA,QACtB;AAAA,QACA,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM;AAAA,MACpB;AACA,YAAM,gBAAgB,QAAM,MAAAE,MAAA,MAAM,SAAQ,SAAd,wBAAAA,KAAqB;AAEjD,YAAM,UAAU,QAAM,iBAAM,SAAQ,YAAd,4BAAwB;AAE9C,YAAM,OAAO,+CAAe;AAC5B,YAAM,QAAQ,+CAAe;AAC7B,YAAM,cAAc,+CAAe;AACnC,YAAM,UAAU;AAAA,IACjB,CAAA;AAAA,EACH;AAEA,MAAI,QAAQ,QAAQ,SAAS,CAAC,EAAG,OAAO,aAAa;AAC5C,WAAA,MAAM,QAAQ,IAAI,CAAC,mBAAmB,OAAO,KAAA,CAAM,CAAC;AAAA,EAAA;AAGtD,SAAA;AACT;AAEA,SAAS,qBAAwB,KAAQ,MAAqB,OAAY;AAEpE,MAAA,KAAK,WAAW,GAAG;AACnB,QAAY,KAAK,CAAC,CAAE,IAAI;AAAA,EAAA;AAG5B,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI;AAEnB,MAAA,MAAM,QAAQ,GAAG,GAAG;AACtB,yBAAqB,IAAI,OAAO,GAAG,CAAC,GAAG,MAAM,KAAK;AAAA,EAAA,WACzC,cAAc,GAAG,GAAG;AAC7B,yBAAsB,IAAY,GAAI,GAAG,MAAM,KAAK;AAAA,EAAA;AAExD;"}
|
|
1
|
+
{"version":3,"file":"ssr-client.js","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { isPlainObject } from '../utils'\nimport { tsrSerializer } from '../serializer'\nimport type { DeferredPromiseState } from '../defer'\nimport type { MakeRouteMatch } from '../Matches'\nimport type { AnyRouter, ControllablePromise } from '../router'\nimport type { Manifest } from '../manifest'\nimport type { RouteContextOptions } from '../route'\n\ndeclare global {\n interface Window {\n __TSR_SSR__?: TsrSsrGlobal\n }\n}\n\nexport interface TsrSsrGlobal {\n matches: Array<SsrMatch>\n streamedValues: Record<\n string,\n {\n value: any\n parsed: any\n }\n >\n cleanScripts: () => void\n dehydrated?: any\n initMatch: (match: SsrMatch) => void\n resolvePromise: (opts: {\n matchId: string\n id: number\n promiseState: DeferredPromiseState<any>\n }) => void\n injectChunk: (opts: { matchId: string; id: number; chunk: string }) => void\n closeStream: (opts: { matchId: string; id: number }) => void\n}\n\nexport interface SsrMatch {\n id: string\n __beforeLoadContext: string\n loaderData?: string\n error?: string\n extracted?: Array<ClientExtractedEntry>\n updatedAt: MakeRouteMatch['updatedAt']\n status: MakeRouteMatch['status']\n}\n\nexport type ClientExtractedEntry =\n | ClientExtractedStream\n | ClientExtractedPromise\n\nexport interface ClientExtractedPromise extends ClientExtractedBaseEntry {\n type: 'promise'\n value?: ControllablePromise<any>\n}\n\nexport interface ClientExtractedStream extends ClientExtractedBaseEntry {\n type: 'stream'\n value?: ReadableStream & { controller?: ReadableStreamDefaultController }\n}\n\nexport interface ClientExtractedBaseEntry {\n type: string\n path: Array<string>\n}\n\nexport interface ResolvePromiseState {\n matchId: string\n id: number\n promiseState: DeferredPromiseState<any>\n}\n\nexport interface DehydratedRouter {\n manifest: Manifest | undefined\n dehydratedData: any\n lastMatchId: string\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n invariant(\n window.__TSR_SSR__?.dehydrated,\n 'Expected to find a dehydrated data on window.__TSR_SSR__.dehydrated... but we did not. Please file an issue!',\n )\n\n const { manifest, dehydratedData, lastMatchId } = tsrSerializer.parse(\n window.__TSR_SSR__.dehydrated,\n ) as DehydratedRouter\n\n router.ssr = {\n manifest,\n serializer: tsrSerializer,\n }\n\n router.clientSsr = {\n getStreamedValue: <T>(key: string): T | undefined => {\n if (router.isServer) {\n return undefined\n }\n\n const streamedValue = window.__TSR_SSR__?.streamedValues[key]\n\n if (!streamedValue) {\n return\n }\n\n if (!streamedValue.parsed) {\n streamedValue.parsed = router.ssr!.serializer.parse(streamedValue.value)\n }\n\n return streamedValue.parsed\n },\n }\n\n // Hydrate the router state\n const matches = router.matchRoutes(router.state.location)\n\n // kick off loading the route chunks\n const routeChunkPromise = Promise.all(\n matches.map((match) => {\n const route = router.looseRoutesById[match.routeId]!\n return router.loadRouteChunk(route)\n }),\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 matches.forEach((match) => {\n const dehydratedMatch = window.__TSR_SSR__!.matches.find(\n (d) => d.id === match.id,\n )\n\n if (!dehydratedMatch) {\n return\n }\n\n Object.assign(match, dehydratedMatch)\n\n // Handle beforeLoadContext\n if (dehydratedMatch.__beforeLoadContext) {\n match.__beforeLoadContext = router.ssr!.serializer.parse(\n dehydratedMatch.__beforeLoadContext,\n ) as any\n }\n\n // Handle loaderData\n if (dehydratedMatch.loaderData) {\n match.loaderData = router.ssr!.serializer.parse(\n dehydratedMatch.loaderData,\n )\n }\n\n // Handle error\n if (dehydratedMatch.error) {\n match.error = router.ssr!.serializer.parse(dehydratedMatch.error)\n }\n\n // Handle extracted\n ;(match as unknown as SsrMatch).extracted?.forEach((ex) => {\n deepMutableSetByPath(match, ['loaderData', ...ex.path], ex.value)\n })\n\n return match\n })\n\n router.__store.setState((s) => {\n return {\n ...s,\n matches,\n }\n })\n\n // Allow the user to handle custom hydration data\n await router.options.hydrate?.(dehydratedData)\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 await Promise.all(\n router.state.matches.map(async (match) => {\n const route = router.looseRoutesById[match.routeId]!\n\n const parentMatch = router.state.matches[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 const contextFnContext: RouteContextOptions<any, any, any, any> = {\n deps: match.loaderDeps,\n params: match.params,\n context: parentContext,\n location: router.state.location,\n navigate: (opts: any) =>\n router.navigate({ ...opts, _fromLocation: router.state.location }),\n buildLocation: router.buildLocation,\n cause: match.cause,\n abortController: match.abortController,\n preload: false,\n matches,\n }\n match.__routeContext = route.options.context?.(contextFnContext) ?? {}\n\n match.context = {\n ...parentContext,\n ...match.__routeContext,\n ...match.__beforeLoadContext,\n }\n\n const assetContext = {\n matches: router.state.matches,\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 }),\n )\n\n if (matches[matches.length - 1]!.id !== lastMatchId) {\n return await Promise.all([routeChunkPromise, router.load()])\n }\n\n return routeChunkPromise\n}\n\nfunction deepMutableSetByPath<T>(obj: T, path: Array<string>, value: any) {\n // mutable set by path retaining array and object references\n if (path.length === 1) {\n ;(obj as any)[path[0]!] = value\n }\n\n const [key, ...rest] = path\n\n if (Array.isArray(obj)) {\n deepMutableSetByPath(obj[Number(key)], rest, value)\n } else if (isPlainObject(obj)) {\n deepMutableSetByPath((obj as any)[key!], rest, value)\n }\n}\n"],"names":["_a","_b","_c"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/Matches.ts
CHANGED
package/src/route.ts
CHANGED
|
@@ -1124,6 +1124,7 @@ export interface UpdatableRouteOptions<
|
|
|
1124
1124
|
links?: AnyRouteMatch['links']
|
|
1125
1125
|
scripts?: AnyRouteMatch['headScripts']
|
|
1126
1126
|
meta?: AnyRouteMatch['meta']
|
|
1127
|
+
styles?: AnyRouteMatch['styles']
|
|
1127
1128
|
}>
|
|
1128
1129
|
scripts?: (
|
|
1129
1130
|
ctx: AssetFnContextOptions<
|
package/src/router.ts
CHANGED
|
@@ -1791,16 +1791,9 @@ export class RouterCore<
|
|
|
1791
1791
|
location: this.latestLocation,
|
|
1792
1792
|
pendingMatches,
|
|
1793
1793
|
// If a cached moved to pendingMatches, remove it from cachedMatches
|
|
1794
|
-
cachedMatches: s.cachedMatches.filter(
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
if (!pendingMatch) return true
|
|
1798
|
-
|
|
1799
|
-
return (
|
|
1800
|
-
cachedMatch.status === 'success' &&
|
|
1801
|
-
(cachedMatch.isFetching || cachedMatch.loaderData !== undefined)
|
|
1802
|
-
)
|
|
1803
|
-
}),
|
|
1794
|
+
cachedMatches: s.cachedMatches.filter(
|
|
1795
|
+
(d) => !pendingMatches.find((e) => e.id === d.id),
|
|
1796
|
+
),
|
|
1804
1797
|
}))
|
|
1805
1798
|
}
|
|
1806
1799
|
|
|
@@ -2208,7 +2201,15 @@ export class RouterCore<
|
|
|
2208
2201
|
|
|
2209
2202
|
// Wait for the beforeLoad to resolve before we continue
|
|
2210
2203
|
await existingMatch.beforeLoadPromise
|
|
2211
|
-
|
|
2204
|
+
const match = this.getMatch(matchId)!
|
|
2205
|
+
if (match.status === 'error') {
|
|
2206
|
+
executeBeforeLoad = true
|
|
2207
|
+
} else if (
|
|
2208
|
+
match.preload &&
|
|
2209
|
+
(match.status === 'redirected' || match.status === 'notFound')
|
|
2210
|
+
) {
|
|
2211
|
+
handleRedirectAndNotFound(match, match.error)
|
|
2212
|
+
}
|
|
2212
2213
|
}
|
|
2213
2214
|
if (executeBeforeLoad) {
|
|
2214
2215
|
// If we are not in the middle of a load OR the previous load failed, start it
|
|
@@ -2337,14 +2338,23 @@ export class RouterCore<
|
|
|
2337
2338
|
validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
|
|
2338
2339
|
matchPromises.push(
|
|
2339
2340
|
(async () => {
|
|
2340
|
-
const { loaderPromise: prevLoaderPromise } =
|
|
2341
|
-
this.getMatch(matchId)!
|
|
2342
|
-
|
|
2343
2341
|
let loaderShouldRunAsync = false
|
|
2344
2342
|
let loaderIsRunningAsync = false
|
|
2345
2343
|
|
|
2346
|
-
|
|
2347
|
-
|
|
2344
|
+
const prevMatch = this.getMatch(matchId)!
|
|
2345
|
+
// there is a loaderPromise, so we are in the middle of a load
|
|
2346
|
+
if (prevMatch.loaderPromise) {
|
|
2347
|
+
// do not block if we already have stale data we can show
|
|
2348
|
+
// but only if the ongoing load is not a preload since error handling is different for preloads
|
|
2349
|
+
// and we don't want to swallow errors
|
|
2350
|
+
if (
|
|
2351
|
+
prevMatch.status === 'success' &&
|
|
2352
|
+
!sync &&
|
|
2353
|
+
!prevMatch.preload
|
|
2354
|
+
) {
|
|
2355
|
+
return this.getMatch(matchId)!
|
|
2356
|
+
}
|
|
2357
|
+
await prevMatch.loaderPromise
|
|
2348
2358
|
const match = this.getMatch(matchId)!
|
|
2349
2359
|
if (match.error) {
|
|
2350
2360
|
handleRedirectAndNotFound(match, match.error)
|
|
@@ -2427,12 +2437,20 @@ export class RouterCore<
|
|
|
2427
2437
|
const meta = headFnContent?.meta
|
|
2428
2438
|
const links = headFnContent?.links
|
|
2429
2439
|
const headScripts = headFnContent?.scripts
|
|
2440
|
+
const styles = headFnContent?.styles
|
|
2430
2441
|
|
|
2431
2442
|
const scripts =
|
|
2432
2443
|
await route.options.scripts?.(assetContext)
|
|
2433
2444
|
const headers =
|
|
2434
2445
|
await route.options.headers?.(assetContext)
|
|
2435
|
-
return {
|
|
2446
|
+
return {
|
|
2447
|
+
meta,
|
|
2448
|
+
links,
|
|
2449
|
+
headScripts,
|
|
2450
|
+
headers,
|
|
2451
|
+
scripts,
|
|
2452
|
+
styles,
|
|
2453
|
+
}
|
|
2436
2454
|
}
|
|
2437
2455
|
|
|
2438
2456
|
const runLoader = async () => {
|
package/src/ssr/ssr-client.ts
CHANGED
|
@@ -217,6 +217,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
|
|
|
217
217
|
match.meta = headFnContent?.meta
|
|
218
218
|
match.links = headFnContent?.links
|
|
219
219
|
match.headScripts = headFnContent?.scripts
|
|
220
|
+
match.styles = headFnContent?.styles
|
|
220
221
|
match.scripts = scripts
|
|
221
222
|
}),
|
|
222
223
|
)
|