@rangojs/router 0.0.0-experimental.70 → 0.0.0-experimental.71
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/vite/index.js
CHANGED
|
@@ -1864,7 +1864,7 @@ import { resolve } from "node:path";
|
|
|
1864
1864
|
// package.json
|
|
1865
1865
|
var package_default = {
|
|
1866
1866
|
name: "@rangojs/router",
|
|
1867
|
-
version: "0.0.0-experimental.
|
|
1867
|
+
version: "0.0.0-experimental.71",
|
|
1868
1868
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1869
1869
|
keywords: [
|
|
1870
1870
|
"react",
|
package/package.json
CHANGED
|
@@ -125,6 +125,69 @@ export async function collectSegments(
|
|
|
125
125
|
return segments;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Deduplicate inherited loader segments by loaderId.
|
|
130
|
+
*
|
|
131
|
+
* When a route has loaders and a child layout has parallel slots, the same
|
|
132
|
+
* loader is resolved twice: once for the route and once inherited into the
|
|
133
|
+
* layout (tagged with `_inherited`). The inherited copy is only needed when
|
|
134
|
+
* the route uses `loading()` — in that case, the loader data is inside a
|
|
135
|
+
* LoaderBoundary/Suspense that parallel slots can't reach through. Without
|
|
136
|
+
* loading(), useLoader() traverses parent contexts and finds the data.
|
|
137
|
+
*/
|
|
138
|
+
function deduplicateLoaderSegments(
|
|
139
|
+
segments: ResolvedSegment[],
|
|
140
|
+
logPrefix: string,
|
|
141
|
+
): ResolvedSegment[] {
|
|
142
|
+
// First pass: collect loaderIds of original (non-inherited) segments
|
|
143
|
+
// and whether their parent entry uses loading()
|
|
144
|
+
const originalLoaders = new Set<string>();
|
|
145
|
+
const loadersWithLoading = new Set<string>();
|
|
146
|
+
for (const s of segments) {
|
|
147
|
+
if (s.type === "loader" && s.loaderId && !s._inherited) {
|
|
148
|
+
originalLoaders.add(s.loaderId);
|
|
149
|
+
// If the segment has a sibling with loading, the parent uses loading()
|
|
150
|
+
// We detect this by checking if any non-loader segment in the same
|
|
151
|
+
// namespace has loading defined
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Check if any layout/route segment has loading — if a loader's namespace
|
|
155
|
+
// matches a segment with loading, the inherited copy is needed
|
|
156
|
+
for (const s of segments) {
|
|
157
|
+
if (s.type !== "loader" && s.loading !== undefined && s.loading !== false) {
|
|
158
|
+
// Find loaders in this namespace
|
|
159
|
+
for (const l of segments) {
|
|
160
|
+
if (l.type === "loader" && l.namespace === s.namespace && l.loaderId) {
|
|
161
|
+
loadersWithLoading.add(l.loaderId);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result: ResolvedSegment[] = [];
|
|
168
|
+
let dedupCount = 0;
|
|
169
|
+
|
|
170
|
+
for (const s of segments) {
|
|
171
|
+
if (
|
|
172
|
+
s.type === "loader" &&
|
|
173
|
+
s.loaderId &&
|
|
174
|
+
s._inherited &&
|
|
175
|
+
originalLoaders.has(s.loaderId) &&
|
|
176
|
+
!loadersWithLoading.has(s.loaderId)
|
|
177
|
+
) {
|
|
178
|
+
dedupCount++;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
result.push(s);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (dedupCount > 0) {
|
|
185
|
+
debugLog(logPrefix, `deduped ${dedupCount} inherited loader segment(s)`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
|
|
128
191
|
/**
|
|
129
192
|
* Build the final MatchResult from collected segments and context
|
|
130
193
|
*/
|
|
@@ -181,6 +244,11 @@ export function buildMatchResult<TEnv>(
|
|
|
181
244
|
);
|
|
182
245
|
}
|
|
183
246
|
|
|
247
|
+
const dedupedSegments = deduplicateLoaderSegments(
|
|
248
|
+
segmentsToRender,
|
|
249
|
+
logPrefix,
|
|
250
|
+
);
|
|
251
|
+
|
|
184
252
|
debugLog(logPrefix, "all segments", {
|
|
185
253
|
segments: allSegments.map((s) => ({
|
|
186
254
|
id: s.id,
|
|
@@ -189,13 +257,23 @@ export function buildMatchResult<TEnv>(
|
|
|
189
257
|
})),
|
|
190
258
|
});
|
|
191
259
|
debugLog(logPrefix, "segments to render", {
|
|
192
|
-
segmentIds:
|
|
260
|
+
segmentIds: dedupedSegments.map((s) => s.id),
|
|
193
261
|
});
|
|
194
262
|
|
|
263
|
+
// Remove deduped loader IDs from matched so the client doesn't treat
|
|
264
|
+
// them as missing segments and trigger a fallback refetch.
|
|
265
|
+
const removedIds = new Set(
|
|
266
|
+
segmentsToRender
|
|
267
|
+
.filter((s) => !dedupedSegments.includes(s))
|
|
268
|
+
.map((s) => s.id),
|
|
269
|
+
);
|
|
270
|
+
const matchedIds =
|
|
271
|
+
removedIds.size > 0 ? allIds.filter((id) => !removedIds.has(id)) : allIds;
|
|
272
|
+
|
|
195
273
|
return {
|
|
196
|
-
segments:
|
|
197
|
-
matched:
|
|
198
|
-
diff:
|
|
274
|
+
segments: dedupedSegments,
|
|
275
|
+
matched: matchedIds,
|
|
276
|
+
diff: dedupedSegments.map((s) => s.id),
|
|
199
277
|
params: ctx.matched.params,
|
|
200
278
|
routeName: ctx.routeKey,
|
|
201
279
|
slots: Object.keys(state.slots).length > 0 ? state.slots : undefined,
|
|
@@ -419,6 +419,10 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
419
419
|
deps,
|
|
420
420
|
orphan.shortCode,
|
|
421
421
|
);
|
|
422
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
423
|
+
for (const s of inheritedLoaders) {
|
|
424
|
+
s._inherited = true;
|
|
425
|
+
}
|
|
422
426
|
segments.push(...inheritedLoaders);
|
|
423
427
|
}
|
|
424
428
|
}
|
|
@@ -728,6 +732,7 @@ export async function resolveLoadersOnly<TEnv>(
|
|
|
728
732
|
for (const seg of inherited) {
|
|
729
733
|
if (!seenIds.has(seg.id)) {
|
|
730
734
|
seenIds.add(seg.id);
|
|
735
|
+
seg._inherited = true;
|
|
731
736
|
loaderSegments.push(seg);
|
|
732
737
|
}
|
|
733
738
|
}
|
|
@@ -346,6 +346,7 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
346
346
|
for (const seg of inherited.segments) {
|
|
347
347
|
if (!seenIds.has(seg.id)) {
|
|
348
348
|
seenIds.add(seg.id);
|
|
349
|
+
seg._inherited = true;
|
|
349
350
|
allLoaderSegments.push(seg);
|
|
350
351
|
}
|
|
351
352
|
}
|
|
@@ -1036,6 +1037,10 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1036
1037
|
orphan.shortCode,
|
|
1037
1038
|
stale,
|
|
1038
1039
|
);
|
|
1040
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
1041
|
+
for (const s of inheritedResult.segments) {
|
|
1042
|
+
s._inherited = true;
|
|
1043
|
+
}
|
|
1039
1044
|
segments.push(...inheritedResult.segments);
|
|
1040
1045
|
matchedIds.push(...inheritedResult.matchedIds);
|
|
1041
1046
|
}
|
|
@@ -1126,6 +1131,7 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1126
1131
|
);
|
|
1127
1132
|
|
|
1128
1133
|
if (!resolvedParallelEntries.has(parallelEntry.id)) {
|
|
1134
|
+
// shortCodeOverride must match the parent layout, not the parallel entry.
|
|
1129
1135
|
const loaderResult = await resolveLoadersWithRevalidation(
|
|
1130
1136
|
parallelEntry,
|
|
1131
1137
|
context,
|
|
@@ -1138,7 +1144,7 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1138
1144
|
routeKey,
|
|
1139
1145
|
deps,
|
|
1140
1146
|
actionContext,
|
|
1141
|
-
|
|
1147
|
+
orphan.shortCode,
|
|
1142
1148
|
stale,
|
|
1143
1149
|
);
|
|
1144
1150
|
segments.push(...loaderResult.segments);
|
package/src/types/segments.ts
CHANGED
|
@@ -50,6 +50,7 @@ export interface ResolvedSegment {
|
|
|
50
50
|
parallelName?: string; // For parallels: the parallel group name (used to match with revalidations)
|
|
51
51
|
// Loader-specific fields
|
|
52
52
|
loaderId?: string; // For loaders: the loader $$id identifier
|
|
53
|
+
_inherited?: boolean; // For inherited loaders: dedup marker for buildMatchResult
|
|
53
54
|
loaderData?: any; // For loaders: the resolved data from loader execution
|
|
54
55
|
parallelLoading?: ReactNode; // For parallel-owned loaders: the parallel's loading fallback
|
|
55
56
|
// Intercept loader fields (for streaming loader data in parallel segments)
|