@lovrozagar/flare 0.3.1 → 0.4.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/package.json +1 -1
- package/src/navigation/index.ts +32 -20
package/package.json
CHANGED
package/src/navigation/index.ts
CHANGED
|
@@ -585,12 +585,12 @@ function hydrateCachedDeferred(cached: CachedMatch): boolean {
|
|
|
585
585
|
function commitCachedShell(
|
|
586
586
|
c: FlareProviderContext,
|
|
587
587
|
allModules: LoadedRouteModule[],
|
|
588
|
+
rootLayout: LoadedRouteModule | undefined,
|
|
588
589
|
search: SearchParams,
|
|
589
590
|
params: Record<string, string | string[]>,
|
|
590
591
|
): { hadShell: boolean; keepMatchIds: string[] } {
|
|
591
592
|
if (!ctx) return { hadShell: false, keepMatchIds: [] };
|
|
592
593
|
let found = false;
|
|
593
|
-
const heads: PerRouteHead[] = [];
|
|
594
594
|
const keepMatchIds: string[] = [];
|
|
595
595
|
for (const mod of allModules) {
|
|
596
596
|
const matchId = matchIdForModule(mod, search, params);
|
|
@@ -600,10 +600,23 @@ function commitCachedShell(
|
|
|
600
600
|
if (cached.hasDeferred && hydrateCachedDeferred(cached)) {
|
|
601
601
|
keepMatchIds.push(matchId);
|
|
602
602
|
}
|
|
603
|
-
if (cached.headConfig) heads.push({ head: cached.headConfig, matchId });
|
|
604
603
|
}
|
|
605
604
|
if (!found) return { hadShell: false, keepMatchIds: [] };
|
|
606
605
|
|
|
606
|
+
/* applyPerRouteHeads replaces the whole hierarchy, so a partial set would
|
|
607
|
+
* drop the root layout's CSS. Apply only when every route has a cached head;
|
|
608
|
+
* otherwise the post-fetch update applies the full set. */
|
|
609
|
+
const heads: PerRouteHead[] = [];
|
|
610
|
+
for (const mod of rootLayout ? [rootLayout, ...allModules] : allModules) {
|
|
611
|
+
const matchId = matchIdForModule(mod, search, params);
|
|
612
|
+
const head = ctx.matchCache.get(matchId)?.headConfig;
|
|
613
|
+
if (!head) {
|
|
614
|
+
heads.length = 0;
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
heads.push({ head, matchId });
|
|
618
|
+
}
|
|
619
|
+
|
|
607
620
|
c.setIntercepted(null);
|
|
608
621
|
c.setNotFound(false);
|
|
609
622
|
assignMatches(c, buildClientMatches(allModules, search, params));
|
|
@@ -1141,6 +1154,9 @@ export async function navigate(options: InternalNavigateOptions, redirectCount =
|
|
|
1141
1154
|
*/
|
|
1142
1155
|
const nonRootLayouts = modules.layouts.filter((m) => m._type !== "root-layout");
|
|
1143
1156
|
const allModules: LoadedRouteModule[] = [...nonRootLayouts, modules.page];
|
|
1157
|
+
/* The root layout still owns head state (title, CSS) on every navigation. */
|
|
1158
|
+
const rootLayout = modules.layouts.find((m) => m._type === "root-layout");
|
|
1159
|
+
const headModules = rootLayout ? [rootLayout, ...allModules] : allModules;
|
|
1144
1160
|
|
|
1145
1161
|
/* Instant navigation: reuse in-flight prefetch and paint a cached shell
|
|
1146
1162
|
* before the enter NDJSON hop. Skipped when this nav already fetched in
|
|
@@ -1160,7 +1176,7 @@ export async function navigate(options: InternalNavigateOptions, redirectCount =
|
|
|
1160
1176
|
}
|
|
1161
1177
|
}
|
|
1162
1178
|
if (controller.signal.aborted || myVersion !== navigationVersion) return;
|
|
1163
|
-
const shell = commitCachedShell(c, allModules, search, modules.params);
|
|
1179
|
+
const shell = commitCachedShell(c, allModules, rootLayout, search, modules.params);
|
|
1164
1180
|
hadShell = shell.hadShell;
|
|
1165
1181
|
paintedShell = shell.hadShell;
|
|
1166
1182
|
if (shell.hadShell) {
|
|
@@ -1387,25 +1403,21 @@ export async function navigate(options: InternalNavigateOptions, redirectCount =
|
|
|
1387
1403
|
const clientMatches = buildClientMatches(allModules, search, modules.params);
|
|
1388
1404
|
|
|
1389
1405
|
/* Step 11: Compute head data before update — used inside VT callback */
|
|
1390
|
-
const freshHeads =
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
if (!fetchedHeadIds.has(matchId)) {
|
|
1403
|
-
const cached = ctx.matchCache.get(matchId);
|
|
1404
|
-
if (cached?.headConfig) {
|
|
1405
|
-
perRouteHeads.push({ head: cached.headConfig, matchId });
|
|
1406
|
-
}
|
|
1406
|
+
const freshHeads = new Map<string, HeadConfig>();
|
|
1407
|
+
for (const h of fetchResult?.perRouteHeads ?? []) freshHeads.set(h.matchId, h.head);
|
|
1408
|
+
const fetchedIds = new Set(fetchResult?.matches.map((m) => m.matchId));
|
|
1409
|
+
/* Hierarchy order, root first. A fetched route uses its fresh head (none
|
|
1410
|
+
* under headReplace); an unfetched one falls back to the cache. */
|
|
1411
|
+
const perRouteHeads: PerRouteHead[] = [];
|
|
1412
|
+
for (const mod of headModules) {
|
|
1413
|
+
const matchId = matchIdForModule(mod, search, modules.params);
|
|
1414
|
+
const head = fetchedIds.has(matchId) ? freshHeads.get(matchId) : ctx.matchCache.get(matchId)?.headConfig;
|
|
1415
|
+
if (head) {
|
|
1416
|
+
perRouteHeads.push({ head, matchId });
|
|
1417
|
+
freshHeads.delete(matchId);
|
|
1407
1418
|
}
|
|
1408
1419
|
}
|
|
1420
|
+
for (const [matchId, head] of freshHeads) perRouteHeads.push({ head, matchId });
|
|
1409
1421
|
|
|
1410
1422
|
/* Step 12: Update state + scroll + head + cleanup.
|
|
1411
1423
|
* Everything inside update() so VT captures the complete state transition. */
|