@tanstack/react-start-client 1.111.10 → 1.111.15
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/ssr-client.cjs +35 -19
- package/dist/cjs/ssr-client.cjs.map +1 -1
- package/dist/esm/ssr-client.js +35 -19
- package/dist/esm/ssr-client.js.map +1 -1
- package/package.json +2 -2
- package/src/ssr-client.tsx +52 -27
package/dist/cjs/ssr-client.cjs
CHANGED
|
@@ -40,24 +40,16 @@ function hydrate(router) {
|
|
|
40
40
|
})
|
|
41
41
|
);
|
|
42
42
|
matches.forEach((match) => {
|
|
43
|
-
var _a2
|
|
44
|
-
const route = router.looseRoutesById[match.routeId];
|
|
43
|
+
var _a2;
|
|
45
44
|
const dehydratedMatch = window.__TSR_SSR__.matches.find(
|
|
46
45
|
(d) => d.id === match.id
|
|
47
46
|
);
|
|
48
47
|
if (dehydratedMatch) {
|
|
49
48
|
Object.assign(match, dehydratedMatch);
|
|
50
|
-
const parentMatch = matches[match.index - 1];
|
|
51
|
-
const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? router.options.context ?? {};
|
|
52
49
|
if (dehydratedMatch.__beforeLoadContext) {
|
|
53
50
|
match.__beforeLoadContext = router.ssr.serializer.parse(
|
|
54
51
|
dehydratedMatch.__beforeLoadContext
|
|
55
52
|
);
|
|
56
|
-
match.context = {
|
|
57
|
-
...parentContext,
|
|
58
|
-
...match.__routeContext,
|
|
59
|
-
...match.__beforeLoadContext
|
|
60
|
-
};
|
|
61
53
|
}
|
|
62
54
|
if (dehydratedMatch.loaderData) {
|
|
63
55
|
match.loaderData = router.ssr.serializer.parse(
|
|
@@ -76,27 +68,51 @@ function hydrate(router) {
|
|
|
76
68
|
updatedAt: Date.now()
|
|
77
69
|
});
|
|
78
70
|
}
|
|
71
|
+
return match;
|
|
72
|
+
});
|
|
73
|
+
router.__store.setState((s) => {
|
|
74
|
+
return {
|
|
75
|
+
...s,
|
|
76
|
+
matches
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
(_c = (_b = router.options).hydrate) == null ? void 0 : _c.call(_b, dehydratedData);
|
|
80
|
+
router.state.matches.forEach((match) => {
|
|
81
|
+
var _a2, _b2, _c2, _d, _e, _f;
|
|
82
|
+
const route = router.looseRoutesById[match.routeId];
|
|
83
|
+
const parentMatch = router.state.matches[match.index - 1];
|
|
84
|
+
const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? router.options.context ?? {};
|
|
85
|
+
const contextFnContext = {
|
|
86
|
+
deps: match.loaderDeps,
|
|
87
|
+
params: match.params,
|
|
88
|
+
context: parentContext,
|
|
89
|
+
location: router.state.location,
|
|
90
|
+
navigate: (opts) => router.navigate({ ...opts, _fromLocation: router.state.location }),
|
|
91
|
+
buildLocation: router.buildLocation,
|
|
92
|
+
cause: match.cause,
|
|
93
|
+
abortController: match.abortController,
|
|
94
|
+
preload: false,
|
|
95
|
+
matches
|
|
96
|
+
};
|
|
97
|
+
match.__routeContext = ((_b2 = (_a2 = route.options).context) == null ? void 0 : _b2.call(_a2, contextFnContext)) ?? {};
|
|
98
|
+
match.context = {
|
|
99
|
+
...parentContext,
|
|
100
|
+
...match.__routeContext,
|
|
101
|
+
...match.__beforeLoadContext
|
|
102
|
+
};
|
|
79
103
|
const assetContext = {
|
|
80
104
|
matches: router.state.matches,
|
|
81
105
|
match,
|
|
82
106
|
params: match.params,
|
|
83
107
|
loaderData: match.loaderData
|
|
84
108
|
};
|
|
85
|
-
const headFnContent = (
|
|
86
|
-
const scripts = (
|
|
109
|
+
const headFnContent = (_d = (_c2 = route.options).head) == null ? void 0 : _d.call(_c2, assetContext);
|
|
110
|
+
const scripts = (_f = (_e = route.options).scripts) == null ? void 0 : _f.call(_e, assetContext);
|
|
87
111
|
match.meta = headFnContent == null ? void 0 : headFnContent.meta;
|
|
88
112
|
match.links = headFnContent == null ? void 0 : headFnContent.links;
|
|
89
113
|
match.headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
|
|
90
114
|
match.scripts = scripts;
|
|
91
|
-
return match;
|
|
92
115
|
});
|
|
93
|
-
router.__store.setState((s) => {
|
|
94
|
-
return {
|
|
95
|
-
...s,
|
|
96
|
-
matches
|
|
97
|
-
};
|
|
98
|
-
});
|
|
99
|
-
(_c = (_b = router.options).hydrate) == null ? void 0 : _c.call(_b, dehydratedData);
|
|
100
116
|
return routeChunkPromise;
|
|
101
117
|
}
|
|
102
118
|
function deepMutableSetByPath(obj, path, value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-client.cjs","sources":["../../src/ssr-client.tsx"],"sourcesContent":["import { isPlainObject } from '@tanstack/router-core'\n\nimport invariant from 'tiny-invariant'\n\nimport { startSerializer } from './serializer'\nimport type {\n AnyRouter,\n ControllablePromise,\n MakeRouteMatch,\n} from '@tanstack/react-router'\n\nimport type {
|
|
1
|
+
{"version":3,"file":"ssr-client.cjs","sources":["../../src/ssr-client.tsx"],"sourcesContent":["import { isPlainObject } from '@tanstack/router-core'\n\nimport invariant from 'tiny-invariant'\n\nimport { startSerializer } from './serializer'\nimport type {\n AnyRouter,\n ControllablePromise,\n MakeRouteMatch,\n} from '@tanstack/react-router'\n\nimport type {\n DeferredPromiseState,\n Manifest,\n RouteContextOptions,\n} from '@tanstack/router-core'\n\ndeclare global {\n interface Window {\n __TSR_SSR__?: StartSsrGlobal\n }\n}\n\nexport interface StartSsrGlobal {\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}\n\nexport function hydrate(router: AnyRouter) {\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 } = startSerializer.parse(\n window.__TSR_SSR__.dehydrated,\n ) as DehydratedRouter\n\n router.ssr = {\n manifest,\n serializer: startSerializer,\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 // 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 // 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 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 } else {\n Object.assign(match, {\n status: 'success',\n updatedAt: Date.now(),\n })\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 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 router.state.matches.forEach((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 = route.options.head?.(assetContext)\n\n const scripts = 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 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":["startSerializer","_a","_b","_c","isPlainObject"],"mappings":"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"}
|
package/dist/esm/ssr-client.js
CHANGED
|
@@ -38,24 +38,16 @@ function hydrate(router) {
|
|
|
38
38
|
})
|
|
39
39
|
);
|
|
40
40
|
matches.forEach((match) => {
|
|
41
|
-
var _a2
|
|
42
|
-
const route = router.looseRoutesById[match.routeId];
|
|
41
|
+
var _a2;
|
|
43
42
|
const dehydratedMatch = window.__TSR_SSR__.matches.find(
|
|
44
43
|
(d) => d.id === match.id
|
|
45
44
|
);
|
|
46
45
|
if (dehydratedMatch) {
|
|
47
46
|
Object.assign(match, dehydratedMatch);
|
|
48
|
-
const parentMatch = matches[match.index - 1];
|
|
49
|
-
const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? router.options.context ?? {};
|
|
50
47
|
if (dehydratedMatch.__beforeLoadContext) {
|
|
51
48
|
match.__beforeLoadContext = router.ssr.serializer.parse(
|
|
52
49
|
dehydratedMatch.__beforeLoadContext
|
|
53
50
|
);
|
|
54
|
-
match.context = {
|
|
55
|
-
...parentContext,
|
|
56
|
-
...match.__routeContext,
|
|
57
|
-
...match.__beforeLoadContext
|
|
58
|
-
};
|
|
59
51
|
}
|
|
60
52
|
if (dehydratedMatch.loaderData) {
|
|
61
53
|
match.loaderData = router.ssr.serializer.parse(
|
|
@@ -74,27 +66,51 @@ function hydrate(router) {
|
|
|
74
66
|
updatedAt: Date.now()
|
|
75
67
|
});
|
|
76
68
|
}
|
|
69
|
+
return match;
|
|
70
|
+
});
|
|
71
|
+
router.__store.setState((s) => {
|
|
72
|
+
return {
|
|
73
|
+
...s,
|
|
74
|
+
matches
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
(_c = (_b = router.options).hydrate) == null ? void 0 : _c.call(_b, dehydratedData);
|
|
78
|
+
router.state.matches.forEach((match) => {
|
|
79
|
+
var _a2, _b2, _c2, _d, _e, _f;
|
|
80
|
+
const route = router.looseRoutesById[match.routeId];
|
|
81
|
+
const parentMatch = router.state.matches[match.index - 1];
|
|
82
|
+
const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? router.options.context ?? {};
|
|
83
|
+
const contextFnContext = {
|
|
84
|
+
deps: match.loaderDeps,
|
|
85
|
+
params: match.params,
|
|
86
|
+
context: parentContext,
|
|
87
|
+
location: router.state.location,
|
|
88
|
+
navigate: (opts) => router.navigate({ ...opts, _fromLocation: router.state.location }),
|
|
89
|
+
buildLocation: router.buildLocation,
|
|
90
|
+
cause: match.cause,
|
|
91
|
+
abortController: match.abortController,
|
|
92
|
+
preload: false,
|
|
93
|
+
matches
|
|
94
|
+
};
|
|
95
|
+
match.__routeContext = ((_b2 = (_a2 = route.options).context) == null ? void 0 : _b2.call(_a2, contextFnContext)) ?? {};
|
|
96
|
+
match.context = {
|
|
97
|
+
...parentContext,
|
|
98
|
+
...match.__routeContext,
|
|
99
|
+
...match.__beforeLoadContext
|
|
100
|
+
};
|
|
77
101
|
const assetContext = {
|
|
78
102
|
matches: router.state.matches,
|
|
79
103
|
match,
|
|
80
104
|
params: match.params,
|
|
81
105
|
loaderData: match.loaderData
|
|
82
106
|
};
|
|
83
|
-
const headFnContent = (
|
|
84
|
-
const scripts = (
|
|
107
|
+
const headFnContent = (_d = (_c2 = route.options).head) == null ? void 0 : _d.call(_c2, assetContext);
|
|
108
|
+
const scripts = (_f = (_e = route.options).scripts) == null ? void 0 : _f.call(_e, assetContext);
|
|
85
109
|
match.meta = headFnContent == null ? void 0 : headFnContent.meta;
|
|
86
110
|
match.links = headFnContent == null ? void 0 : headFnContent.links;
|
|
87
111
|
match.headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
|
|
88
112
|
match.scripts = scripts;
|
|
89
|
-
return match;
|
|
90
113
|
});
|
|
91
|
-
router.__store.setState((s) => {
|
|
92
|
-
return {
|
|
93
|
-
...s,
|
|
94
|
-
matches
|
|
95
|
-
};
|
|
96
|
-
});
|
|
97
|
-
(_c = (_b = router.options).hydrate) == null ? void 0 : _c.call(_b, dehydratedData);
|
|
98
114
|
return routeChunkPromise;
|
|
99
115
|
}
|
|
100
116
|
function deepMutableSetByPath(obj, path, value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-client.js","sources":["../../src/ssr-client.tsx"],"sourcesContent":["import { isPlainObject } from '@tanstack/router-core'\n\nimport invariant from 'tiny-invariant'\n\nimport { startSerializer } from './serializer'\nimport type {\n AnyRouter,\n ControllablePromise,\n MakeRouteMatch,\n} from '@tanstack/react-router'\n\nimport type {
|
|
1
|
+
{"version":3,"file":"ssr-client.js","sources":["../../src/ssr-client.tsx"],"sourcesContent":["import { isPlainObject } from '@tanstack/router-core'\n\nimport invariant from 'tiny-invariant'\n\nimport { startSerializer } from './serializer'\nimport type {\n AnyRouter,\n ControllablePromise,\n MakeRouteMatch,\n} from '@tanstack/react-router'\n\nimport type {\n DeferredPromiseState,\n Manifest,\n RouteContextOptions,\n} from '@tanstack/router-core'\n\ndeclare global {\n interface Window {\n __TSR_SSR__?: StartSsrGlobal\n }\n}\n\nexport interface StartSsrGlobal {\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}\n\nexport function hydrate(router: AnyRouter) {\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 } = startSerializer.parse(\n window.__TSR_SSR__.dehydrated,\n ) as DehydratedRouter\n\n router.ssr = {\n manifest,\n serializer: startSerializer,\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 // 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 // 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 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 } else {\n Object.assign(match, {\n status: 'success',\n updatedAt: Date.now(),\n })\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 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 router.state.matches.forEach((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 = route.options.head?.(assetContext)\n\n const scripts = 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 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-start-client",
|
|
3
|
-
"version": "1.111.
|
|
3
|
+
"version": "1.111.15",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"tiny-invariant": "^1.3.3",
|
|
53
53
|
"tiny-warning": "^1.0.3",
|
|
54
54
|
"vinxi": "^0.5.3",
|
|
55
|
-
"@tanstack/react-router": "^1.111.
|
|
55
|
+
"@tanstack/react-router": "^1.111.11",
|
|
56
56
|
"@tanstack/router-core": "^1.111.7"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
package/src/ssr-client.tsx
CHANGED
|
@@ -9,7 +9,11 @@ import type {
|
|
|
9
9
|
MakeRouteMatch,
|
|
10
10
|
} from '@tanstack/react-router'
|
|
11
11
|
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
DeferredPromiseState,
|
|
14
|
+
Manifest,
|
|
15
|
+
RouteContextOptions,
|
|
16
|
+
} from '@tanstack/router-core'
|
|
13
17
|
|
|
14
18
|
declare global {
|
|
15
19
|
interface Window {
|
|
@@ -122,13 +126,9 @@ export function hydrate(router: AnyRouter) {
|
|
|
122
126
|
return router.loadRouteChunk(route)
|
|
123
127
|
}),
|
|
124
128
|
)
|
|
129
|
+
// Right after hydration and before the first render, we need to rehydrate each match
|
|
130
|
+
// First step is to reyhdrate loaderData and __beforeLoadContext
|
|
125
131
|
matches.forEach((match) => {
|
|
126
|
-
const route = router.looseRoutesById[match.routeId]!
|
|
127
|
-
|
|
128
|
-
// Right after hydration and before the first render, we need to rehydrate each match
|
|
129
|
-
// This includes rehydrating the loaderData and also using the beforeLoadContext
|
|
130
|
-
// to reconstruct any context that was serialized on the server
|
|
131
|
-
|
|
132
132
|
const dehydratedMatch = window.__TSR_SSR__!.matches.find(
|
|
133
133
|
(d) => d.id === match.id,
|
|
134
134
|
)
|
|
@@ -136,20 +136,11 @@ export function hydrate(router: AnyRouter) {
|
|
|
136
136
|
if (dehydratedMatch) {
|
|
137
137
|
Object.assign(match, dehydratedMatch)
|
|
138
138
|
|
|
139
|
-
const parentMatch = matches[match.index - 1]
|
|
140
|
-
const parentContext = parentMatch?.context ?? router.options.context ?? {}
|
|
141
|
-
|
|
142
139
|
// Handle beforeLoadContext
|
|
143
140
|
if (dehydratedMatch.__beforeLoadContext) {
|
|
144
141
|
match.__beforeLoadContext = router.ssr!.serializer.parse(
|
|
145
142
|
dehydratedMatch.__beforeLoadContext,
|
|
146
143
|
) as any
|
|
147
|
-
|
|
148
|
-
match.context = {
|
|
149
|
-
...parentContext,
|
|
150
|
-
...match.__routeContext,
|
|
151
|
-
...match.__beforeLoadContext,
|
|
152
|
-
}
|
|
153
144
|
}
|
|
154
145
|
|
|
155
146
|
// Handle loaderData
|
|
@@ -175,6 +166,51 @@ export function hydrate(router: AnyRouter) {
|
|
|
175
166
|
})
|
|
176
167
|
}
|
|
177
168
|
|
|
169
|
+
return match
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
router.__store.setState((s) => {
|
|
173
|
+
return {
|
|
174
|
+
...s,
|
|
175
|
+
matches,
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// Allow the user to handle custom hydration data
|
|
180
|
+
router.options.hydrate?.(dehydratedData)
|
|
181
|
+
|
|
182
|
+
// now that all necessary data is hydrated:
|
|
183
|
+
// 1) fully reconstruct the route context
|
|
184
|
+
// 2) execute `head()` and `scripts()` for each match
|
|
185
|
+
router.state.matches.forEach((match) => {
|
|
186
|
+
const route = router.looseRoutesById[match.routeId]!
|
|
187
|
+
|
|
188
|
+
const parentMatch = router.state.matches[match.index - 1]
|
|
189
|
+
const parentContext = parentMatch?.context ?? router.options.context ?? {}
|
|
190
|
+
|
|
191
|
+
// `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed
|
|
192
|
+
// so run it again and merge route context
|
|
193
|
+
const contextFnContext: RouteContextOptions<any, any, any, any> = {
|
|
194
|
+
deps: match.loaderDeps,
|
|
195
|
+
params: match.params,
|
|
196
|
+
context: parentContext,
|
|
197
|
+
location: router.state.location,
|
|
198
|
+
navigate: (opts: any) =>
|
|
199
|
+
router.navigate({ ...opts, _fromLocation: router.state.location }),
|
|
200
|
+
buildLocation: router.buildLocation,
|
|
201
|
+
cause: match.cause,
|
|
202
|
+
abortController: match.abortController,
|
|
203
|
+
preload: false,
|
|
204
|
+
matches,
|
|
205
|
+
}
|
|
206
|
+
match.__routeContext = route.options.context?.(contextFnContext) ?? {}
|
|
207
|
+
|
|
208
|
+
match.context = {
|
|
209
|
+
...parentContext,
|
|
210
|
+
...match.__routeContext,
|
|
211
|
+
...match.__beforeLoadContext,
|
|
212
|
+
}
|
|
213
|
+
|
|
178
214
|
const assetContext = {
|
|
179
215
|
matches: router.state.matches,
|
|
180
216
|
match,
|
|
@@ -189,19 +225,8 @@ export function hydrate(router: AnyRouter) {
|
|
|
189
225
|
match.links = headFnContent?.links
|
|
190
226
|
match.headScripts = headFnContent?.scripts
|
|
191
227
|
match.scripts = scripts
|
|
192
|
-
|
|
193
|
-
return match
|
|
194
228
|
})
|
|
195
229
|
|
|
196
|
-
router.__store.setState((s) => {
|
|
197
|
-
return {
|
|
198
|
-
...s,
|
|
199
|
-
matches,
|
|
200
|
-
}
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
// Allow the user to handle custom hydration data
|
|
204
|
-
router.options.hydrate?.(dehydratedData)
|
|
205
230
|
return routeChunkPromise
|
|
206
231
|
}
|
|
207
232
|
|