@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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/README.md +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
package/dist/bin/rango.js
CHANGED
|
@@ -119,6 +119,21 @@ var init_ast_route_extraction = __esm({
|
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
// src/route-name.ts
|
|
123
|
+
function isAutoGeneratedRouteName(name) {
|
|
124
|
+
return name.split(".").some((segment) => {
|
|
125
|
+
return segment.startsWith(AUTO_GENERATED_ROUTE_PREFIX) || segment.startsWith(INTERNAL_INCLUDE_SCOPE_PREFIX);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
var AUTO_GENERATED_ROUTE_PREFIX, INTERNAL_INCLUDE_SCOPE_PREFIX;
|
|
129
|
+
var init_route_name = __esm({
|
|
130
|
+
"src/route-name.ts"() {
|
|
131
|
+
"use strict";
|
|
132
|
+
AUTO_GENERATED_ROUTE_PREFIX = "$path_";
|
|
133
|
+
INTERNAL_INCLUDE_SCOPE_PREFIX = "$prefix_";
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
122
137
|
// src/build/route-types/codegen.ts
|
|
123
138
|
function generatePerModuleTypesSource(routes) {
|
|
124
139
|
const valid = routes.filter(({ name }) => {
|
|
@@ -153,13 +168,16 @@ export type routes = typeof routes;
|
|
|
153
168
|
`;
|
|
154
169
|
}
|
|
155
170
|
function generateRouteTypesSource(routeManifest, searchSchemas) {
|
|
156
|
-
const entries = Object.entries(routeManifest).sort(
|
|
157
|
-
|
|
158
|
-
|
|
171
|
+
const entries = Object.entries(routeManifest).filter(([name]) => !isAutoGeneratedRouteName(name)).sort(([a], [b]) => a.localeCompare(b));
|
|
172
|
+
const filteredSearchSchemas = searchSchemas ? Object.fromEntries(
|
|
173
|
+
Object.entries(searchSchemas).filter(
|
|
174
|
+
([name]) => !isAutoGeneratedRouteName(name)
|
|
175
|
+
)
|
|
176
|
+
) : void 0;
|
|
159
177
|
const objectBody = entries.map(([name, pattern]) => {
|
|
160
178
|
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
161
179
|
const params = extractParamsFromPattern(pattern);
|
|
162
|
-
const search =
|
|
180
|
+
const search = filteredSearchSchemas?.[name];
|
|
163
181
|
return formatRouteEntry(key, pattern, params, search);
|
|
164
182
|
}).join("\n");
|
|
165
183
|
return `// Auto-generated by @rangojs/router - do not edit
|
|
@@ -178,6 +196,7 @@ var init_codegen = __esm({
|
|
|
178
196
|
"src/build/route-types/codegen.ts"() {
|
|
179
197
|
"use strict";
|
|
180
198
|
init_param_extraction();
|
|
199
|
+
init_route_name();
|
|
181
200
|
}
|
|
182
201
|
});
|
|
183
202
|
|
|
@@ -410,6 +429,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
410
429
|
diagnosticsOut
|
|
411
430
|
);
|
|
412
431
|
}
|
|
432
|
+
if (namePrefix === null) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
413
435
|
for (const [name, pattern] of Object.entries(childResult.routes)) {
|
|
414
436
|
const prefixedName = namePrefix ? `${namePrefix}.${name}` : name;
|
|
415
437
|
let prefixedPattern;
|
|
@@ -460,6 +482,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
460
482
|
searchSchemas,
|
|
461
483
|
diagnosticsOut
|
|
462
484
|
);
|
|
485
|
+
visited.delete(key);
|
|
463
486
|
return { routes, searchSchemas };
|
|
464
487
|
}
|
|
465
488
|
var init_include_resolution = __esm({
|
|
@@ -517,8 +540,16 @@ function writePerModuleRouteTypesForFile(filePath) {
|
|
|
517
540
|
} else {
|
|
518
541
|
routes = extractRoutesFromSource(source);
|
|
519
542
|
}
|
|
520
|
-
if (routes.length === 0) return;
|
|
521
543
|
const genPath = filePath.replace(/\.(tsx?)$/, ".gen.ts");
|
|
544
|
+
if (routes.length === 0) {
|
|
545
|
+
if (varNames.length > 0 && !existsSync2(genPath)) {
|
|
546
|
+
writeFileSync(genPath, generatePerModuleTypesSource([]));
|
|
547
|
+
console.log(
|
|
548
|
+
`[rsc-router] Generated route types (placeholder) -> ${genPath}`
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
522
553
|
const genSource = generatePerModuleTypesSource(routes);
|
|
523
554
|
const existing = existsSync2(genPath) ? readFileSync2(genPath, "utf-8") : null;
|
|
524
555
|
if (existing !== genSource) {
|
|
@@ -543,9 +574,102 @@ var init_per_module_writer = __esm({
|
|
|
543
574
|
});
|
|
544
575
|
|
|
545
576
|
// src/build/route-types/router-processing.ts
|
|
546
|
-
import {
|
|
547
|
-
|
|
577
|
+
import {
|
|
578
|
+
readFileSync as readFileSync3,
|
|
579
|
+
writeFileSync as writeFileSync2,
|
|
580
|
+
existsSync as existsSync3,
|
|
581
|
+
unlinkSync,
|
|
582
|
+
readdirSync as readdirSync2
|
|
583
|
+
} from "node:fs";
|
|
584
|
+
import {
|
|
585
|
+
join as join2,
|
|
586
|
+
dirname as dirname2,
|
|
587
|
+
resolve as resolve2,
|
|
588
|
+
sep,
|
|
589
|
+
basename as pathBasename
|
|
590
|
+
} from "node:path";
|
|
548
591
|
import ts5 from "typescript";
|
|
592
|
+
function countPublicRouteEntries(source) {
|
|
593
|
+
const matches = source.matchAll(/^\s+(?:"([^"]+)"|([a-zA-Z_$][^:]*)):\s*["{]/gm) ?? [];
|
|
594
|
+
let count = 0;
|
|
595
|
+
for (const match of matches) {
|
|
596
|
+
const routeName = match[1] || match[2];
|
|
597
|
+
if (routeName && !isAutoGeneratedRouteName(routeName.trim())) {
|
|
598
|
+
count++;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return count;
|
|
602
|
+
}
|
|
603
|
+
function isRoutableSourceFile(name) {
|
|
604
|
+
return (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js") || name.endsWith(".jsx")) && !name.includes(".gen.");
|
|
605
|
+
}
|
|
606
|
+
function findRouterFilesRecursive(dir, filter, results) {
|
|
607
|
+
let entries;
|
|
608
|
+
try {
|
|
609
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
610
|
+
} catch (err) {
|
|
611
|
+
console.warn(
|
|
612
|
+
`[rsc-router] Failed to scan directory ${dir}: ${err.message}`
|
|
613
|
+
);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const childDirs = [];
|
|
617
|
+
const routerFilesInDir = [];
|
|
618
|
+
for (const entry of entries) {
|
|
619
|
+
const fullPath = join2(dir, entry.name);
|
|
620
|
+
if (entry.isDirectory()) {
|
|
621
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
622
|
+
childDirs.push(fullPath);
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
if (!isRoutableSourceFile(entry.name)) continue;
|
|
626
|
+
if (filter && !filter(fullPath)) continue;
|
|
627
|
+
try {
|
|
628
|
+
const source = readFileSync3(fullPath, "utf-8");
|
|
629
|
+
if (ROUTER_CALL_PATTERN.test(source)) {
|
|
630
|
+
routerFilesInDir.push(fullPath);
|
|
631
|
+
}
|
|
632
|
+
} catch {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (routerFilesInDir.length > 0) {
|
|
637
|
+
results.push(...routerFilesInDir);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
for (const childDir of childDirs) {
|
|
641
|
+
findRouterFilesRecursive(childDir, filter, results);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function findNestedRouterConflict(routerFiles) {
|
|
645
|
+
const routerDirs = [
|
|
646
|
+
...new Set(routerFiles.map((filePath) => dirname2(resolve2(filePath))))
|
|
647
|
+
].sort((a, b) => a.length - b.length);
|
|
648
|
+
for (let i = 0; i < routerDirs.length; i++) {
|
|
649
|
+
const ancestorDir = routerDirs[i];
|
|
650
|
+
const prefix = ancestorDir.endsWith(sep) ? ancestorDir : `${ancestorDir}${sep}`;
|
|
651
|
+
for (let j = i + 1; j < routerDirs.length; j++) {
|
|
652
|
+
const nestedDir = routerDirs[j];
|
|
653
|
+
if (!nestedDir.startsWith(prefix)) continue;
|
|
654
|
+
const ancestorFile = routerFiles.find(
|
|
655
|
+
(filePath) => dirname2(resolve2(filePath)) === ancestorDir
|
|
656
|
+
);
|
|
657
|
+
const nestedFile = routerFiles.find(
|
|
658
|
+
(filePath) => dirname2(resolve2(filePath)) === nestedDir
|
|
659
|
+
);
|
|
660
|
+
if (ancestorFile && nestedFile) {
|
|
661
|
+
return { ancestor: ancestorFile, nested: nestedFile };
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
function formatNestedRouterConflictError(conflict, prefix = "[rsc-router]") {
|
|
668
|
+
return `${prefix} Nested router roots are not supported.
|
|
669
|
+
Router root: ${conflict.ancestor}
|
|
670
|
+
Nested router: ${conflict.nested}
|
|
671
|
+
Move the nested router into a sibling directory or configure it as a separate app root.`;
|
|
672
|
+
}
|
|
549
673
|
function extractUrlsVariableFromRouter(code) {
|
|
550
674
|
const sourceFile = ts5.createSourceFile(
|
|
551
675
|
"router.tsx",
|
|
@@ -652,20 +776,25 @@ function detectUnresolvableIncludes(routerFilePath) {
|
|
|
652
776
|
);
|
|
653
777
|
return diagnostics;
|
|
654
778
|
}
|
|
779
|
+
function detectUnresolvableIncludesForUrlsFile(filePath) {
|
|
780
|
+
const realPath = resolve2(filePath);
|
|
781
|
+
let source;
|
|
782
|
+
try {
|
|
783
|
+
source = readFileSync3(realPath, "utf-8");
|
|
784
|
+
} catch {
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
const varNames = findUrlsVariableNames(source);
|
|
788
|
+
if (varNames.length === 0) return [];
|
|
789
|
+
const diagnostics = [];
|
|
790
|
+
for (const varName of varNames) {
|
|
791
|
+
buildCombinedRouteMapWithSearch(realPath, varName, /* @__PURE__ */ new Set(), diagnostics);
|
|
792
|
+
}
|
|
793
|
+
return diagnostics;
|
|
794
|
+
}
|
|
655
795
|
function findRouterFiles(root, filter) {
|
|
656
|
-
const files = findTsFiles(root, filter);
|
|
657
796
|
const result = [];
|
|
658
|
-
|
|
659
|
-
if (filePath.includes(".gen.")) continue;
|
|
660
|
-
try {
|
|
661
|
-
const source = readFileSync3(filePath, "utf-8");
|
|
662
|
-
if (/\bcreateRouter\s*[<(]/.test(source)) {
|
|
663
|
-
result.push(filePath);
|
|
664
|
-
}
|
|
665
|
-
} catch {
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
797
|
+
findRouterFilesRecursive(root, filter, result);
|
|
669
798
|
return result;
|
|
670
799
|
}
|
|
671
800
|
function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
@@ -681,6 +810,10 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
681
810
|
}
|
|
682
811
|
const routerFilePaths = knownRouterFiles ?? findRouterFiles(root);
|
|
683
812
|
if (routerFilePaths.length === 0) return;
|
|
813
|
+
const nestedRouterConflict = findNestedRouterConflict(routerFilePaths);
|
|
814
|
+
if (nestedRouterConflict) {
|
|
815
|
+
throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
|
|
816
|
+
}
|
|
684
817
|
for (const routerFilePath of routerFilePaths) {
|
|
685
818
|
let routerSource;
|
|
686
819
|
try {
|
|
@@ -725,8 +858,10 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
725
858
|
);
|
|
726
859
|
if (existing !== source) {
|
|
727
860
|
if (opts?.preserveIfLarger && existing) {
|
|
728
|
-
const existingCount = (existing
|
|
729
|
-
const newCount = Object.keys(result.routes).
|
|
861
|
+
const existingCount = countPublicRouteEntries(existing);
|
|
862
|
+
const newCount = Object.keys(result.routes).filter(
|
|
863
|
+
(name) => !isAutoGeneratedRouteName(name)
|
|
864
|
+
).length;
|
|
730
865
|
if (existingCount > newCount) {
|
|
731
866
|
continue;
|
|
732
867
|
}
|
|
@@ -738,12 +873,15 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
738
873
|
}
|
|
739
874
|
}
|
|
740
875
|
}
|
|
876
|
+
var ROUTER_CALL_PATTERN;
|
|
741
877
|
var init_router_processing = __esm({
|
|
742
878
|
"src/build/route-types/router-processing.ts"() {
|
|
743
879
|
"use strict";
|
|
744
880
|
init_codegen();
|
|
745
|
-
init_scan_filter();
|
|
746
881
|
init_include_resolution();
|
|
882
|
+
init_per_module_writer();
|
|
883
|
+
init_route_name();
|
|
884
|
+
ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
|
|
747
885
|
}
|
|
748
886
|
});
|
|
749
887
|
|
|
@@ -758,6 +896,7 @@ var init_generate_route_types = __esm({
|
|
|
758
896
|
init_per_module_writer();
|
|
759
897
|
init_include_resolution();
|
|
760
898
|
init_router_processing();
|
|
899
|
+
init_per_module_writer();
|
|
761
900
|
}
|
|
762
901
|
});
|
|
763
902
|
|
|
@@ -829,11 +968,111 @@ var version_plugin_exports = {};
|
|
|
829
968
|
__export(version_plugin_exports, {
|
|
830
969
|
createVersionPlugin: () => createVersionPlugin
|
|
831
970
|
});
|
|
971
|
+
import { parseAst } from "vite";
|
|
972
|
+
function isCodeModule(id) {
|
|
973
|
+
return /\.(tsx?|jsx?)($|\?)/.test(id);
|
|
974
|
+
}
|
|
975
|
+
function normalizeModuleId(id) {
|
|
976
|
+
return id.split("?", 1)[0];
|
|
977
|
+
}
|
|
978
|
+
function getClientModuleSignature(source) {
|
|
979
|
+
let program;
|
|
980
|
+
try {
|
|
981
|
+
program = parseAst(source, { jsx: true });
|
|
982
|
+
} catch {
|
|
983
|
+
return void 0;
|
|
984
|
+
}
|
|
985
|
+
let isUseClient = false;
|
|
986
|
+
for (const node of program.body ?? []) {
|
|
987
|
+
if (node?.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
|
|
988
|
+
if (node.expression.value === "use client") {
|
|
989
|
+
isUseClient = true;
|
|
990
|
+
}
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
if (!isUseClient) return void 0;
|
|
996
|
+
const exports = /* @__PURE__ */ new Set();
|
|
997
|
+
let hasDefault = false;
|
|
998
|
+
let hasExportAll = false;
|
|
999
|
+
const collectBindingNames = (pattern) => {
|
|
1000
|
+
if (!pattern) return;
|
|
1001
|
+
if (pattern.type === "Identifier") {
|
|
1002
|
+
exports.add(pattern.name);
|
|
1003
|
+
} else if (pattern.type === "ObjectPattern") {
|
|
1004
|
+
for (const prop of pattern.properties ?? []) {
|
|
1005
|
+
if (prop?.type === "RestElement") {
|
|
1006
|
+
collectBindingNames(prop.argument);
|
|
1007
|
+
} else {
|
|
1008
|
+
collectBindingNames(prop?.value);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
} else if (pattern.type === "ArrayPattern") {
|
|
1012
|
+
for (const el of pattern.elements ?? []) {
|
|
1013
|
+
if (el?.type === "RestElement") {
|
|
1014
|
+
collectBindingNames(el.argument);
|
|
1015
|
+
} else {
|
|
1016
|
+
collectBindingNames(el);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
const collectDeclarationNames = (declaration) => {
|
|
1022
|
+
if (!declaration) return;
|
|
1023
|
+
if (declaration.type === "VariableDeclaration") {
|
|
1024
|
+
for (const decl of declaration.declarations ?? []) {
|
|
1025
|
+
collectBindingNames(decl?.id);
|
|
1026
|
+
}
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
collectBindingNames(declaration.id);
|
|
1030
|
+
};
|
|
1031
|
+
for (const node of program.body ?? []) {
|
|
1032
|
+
if (node?.type === "ExportDefaultDeclaration") {
|
|
1033
|
+
hasDefault = true;
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (node?.type === "ExportAllDeclaration") {
|
|
1037
|
+
hasExportAll = true;
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (node?.type !== "ExportNamedDeclaration") continue;
|
|
1041
|
+
collectDeclarationNames(node.declaration);
|
|
1042
|
+
for (const specifier of node.specifiers ?? []) {
|
|
1043
|
+
const exportedName = specifier?.exported?.name ?? specifier?.exported?.value;
|
|
1044
|
+
if (exportedName === "default") {
|
|
1045
|
+
hasDefault = true;
|
|
1046
|
+
} else if (typeof exportedName === "string") {
|
|
1047
|
+
exports.add(exportedName);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return {
|
|
1052
|
+
key: JSON.stringify({
|
|
1053
|
+
default: hasDefault,
|
|
1054
|
+
exportAll: hasExportAll,
|
|
1055
|
+
exports: [...exports].sort()
|
|
1056
|
+
})
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
832
1059
|
function createVersionPlugin() {
|
|
833
1060
|
const buildVersion = Date.now().toString(16);
|
|
834
1061
|
let currentVersion = buildVersion;
|
|
835
1062
|
let isDev = false;
|
|
836
1063
|
let server = null;
|
|
1064
|
+
const clientModuleSignatures = /* @__PURE__ */ new Map();
|
|
1065
|
+
const bumpVersion = (reason) => {
|
|
1066
|
+
currentVersion = Date.now().toString(16);
|
|
1067
|
+
console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
|
|
1068
|
+
const rscEnv = server?.environments?.rsc;
|
|
1069
|
+
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
1070
|
+
"\0" + VIRTUAL_IDS.version
|
|
1071
|
+
);
|
|
1072
|
+
if (versionMod) {
|
|
1073
|
+
rscEnv.moduleGraph.invalidateModule(versionMod);
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
837
1076
|
return {
|
|
838
1077
|
name: "@rangojs/router:version",
|
|
839
1078
|
enforce: "pre",
|
|
@@ -842,6 +1081,12 @@ function createVersionPlugin() {
|
|
|
842
1081
|
},
|
|
843
1082
|
configureServer(devServer) {
|
|
844
1083
|
server = devServer;
|
|
1084
|
+
devServer.watcher.on("unlink", (filePath) => {
|
|
1085
|
+
if (!isDev) return;
|
|
1086
|
+
if (!clientModuleSignatures.has(filePath)) return;
|
|
1087
|
+
clientModuleSignatures.delete(filePath);
|
|
1088
|
+
bumpVersion("Client module removed");
|
|
1089
|
+
});
|
|
845
1090
|
},
|
|
846
1091
|
resolveId(id) {
|
|
847
1092
|
if (id === VIRTUAL_IDS.version) {
|
|
@@ -855,27 +1100,48 @@ function createVersionPlugin() {
|
|
|
855
1100
|
}
|
|
856
1101
|
return null;
|
|
857
1102
|
},
|
|
1103
|
+
transform(code, id) {
|
|
1104
|
+
if (!isDev || !isCodeModule(id)) return null;
|
|
1105
|
+
const normalizedId = normalizeModuleId(id);
|
|
1106
|
+
if (!code.includes("use client") && !clientModuleSignatures.has(normalizedId)) {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
const signature = getClientModuleSignature(code);
|
|
1110
|
+
if (signature) {
|
|
1111
|
+
clientModuleSignatures.set(normalizedId, signature);
|
|
1112
|
+
} else {
|
|
1113
|
+
clientModuleSignatures.delete(normalizedId);
|
|
1114
|
+
}
|
|
1115
|
+
return null;
|
|
1116
|
+
},
|
|
858
1117
|
// Track RSC module changes and update version
|
|
859
|
-
hotUpdate(ctx) {
|
|
1118
|
+
async hotUpdate(ctx) {
|
|
860
1119
|
if (!isDev) return;
|
|
861
1120
|
const isRscModule = this.environment?.name === "rsc";
|
|
862
|
-
if (isRscModule
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
const
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1121
|
+
if (!isRscModule) return;
|
|
1122
|
+
if (isCodeModule(ctx.file)) {
|
|
1123
|
+
const filePath = normalizeModuleId(ctx.file);
|
|
1124
|
+
const previousSignature = clientModuleSignatures.get(filePath);
|
|
1125
|
+
try {
|
|
1126
|
+
const source = await ctx.read();
|
|
1127
|
+
const nextSignature = getClientModuleSignature(source);
|
|
1128
|
+
if (nextSignature) {
|
|
1129
|
+
clientModuleSignatures.set(filePath, nextSignature);
|
|
1130
|
+
if (previousSignature && previousSignature.key === nextSignature.key) {
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
} else {
|
|
1134
|
+
clientModuleSignatures.delete(filePath);
|
|
1135
|
+
if (!previousSignature) {
|
|
1136
|
+
if (ctx.modules.length === 0) return;
|
|
875
1137
|
}
|
|
876
1138
|
}
|
|
1139
|
+
} catch {
|
|
877
1140
|
}
|
|
1141
|
+
} else {
|
|
1142
|
+
if (ctx.modules.length === 0) return;
|
|
878
1143
|
}
|
|
1144
|
+
bumpVersion("RSC module changed");
|
|
879
1145
|
}
|
|
880
1146
|
};
|
|
881
1147
|
}
|
|
@@ -1012,7 +1278,13 @@ async function discoverAndWriteRouteTypes(opts) {
|
|
|
1012
1278
|
if (!router.urlpatterns) continue;
|
|
1013
1279
|
const manifest = generateManifest(router.urlpatterns, routerMountIndex);
|
|
1014
1280
|
routerMountIndex++;
|
|
1015
|
-
const
|
|
1281
|
+
const rawManifest = manifest.routeManifest;
|
|
1282
|
+
const routeManifest = {};
|
|
1283
|
+
for (const [name, pattern] of Object.entries(rawManifest)) {
|
|
1284
|
+
if (!isAutoGeneratedRouteName(name)) {
|
|
1285
|
+
routeManifest[name] = pattern;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1016
1288
|
let routeSearchSchemas = manifest.routeSearchSchemas;
|
|
1017
1289
|
const sourceFile = router.__sourceFile;
|
|
1018
1290
|
if (!sourceFile) {
|
|
@@ -1074,6 +1346,7 @@ var init_runtime_discovery = __esm({
|
|
|
1074
1346
|
"src/build/runtime-discovery.ts"() {
|
|
1075
1347
|
"use strict";
|
|
1076
1348
|
init_generate_route_types();
|
|
1349
|
+
init_route_name();
|
|
1077
1350
|
}
|
|
1078
1351
|
});
|
|
1079
1352
|
|
|
@@ -1178,16 +1451,15 @@ function runStaticGeneration(args, mode) {
|
|
|
1178
1451
|
process.exit(0);
|
|
1179
1452
|
}
|
|
1180
1453
|
const routerFiles = [];
|
|
1454
|
+
const urlsFiles = [];
|
|
1181
1455
|
for (const filePath of files) {
|
|
1182
1456
|
try {
|
|
1183
1457
|
const source = readFileSync5(filePath, "utf-8");
|
|
1184
|
-
|
|
1185
|
-
const isUrls = source.includes("urls(");
|
|
1186
|
-
if (isRouter) {
|
|
1458
|
+
if (/\bcreateRouter\s*[<(]/.test(source)) {
|
|
1187
1459
|
routerFiles.push(filePath);
|
|
1188
1460
|
}
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
1461
|
+
if (source.includes("urls(")) {
|
|
1462
|
+
urlsFiles.push(filePath);
|
|
1191
1463
|
}
|
|
1192
1464
|
} catch (err) {
|
|
1193
1465
|
console.warn(
|
|
@@ -1202,21 +1474,48 @@ function runStaticGeneration(args, mode) {
|
|
|
1202
1474
|
allDiagnostics.push({ ...d, routerFile });
|
|
1203
1475
|
}
|
|
1204
1476
|
}
|
|
1205
|
-
|
|
1477
|
+
const routerFileSet = new Set(routerFiles);
|
|
1478
|
+
for (const urlsFile of urlsFiles) {
|
|
1479
|
+
if (routerFileSet.has(urlsFile)) continue;
|
|
1480
|
+
const diagnostics = detectUnresolvableIncludesForUrlsFile(urlsFile);
|
|
1481
|
+
for (const d of diagnostics) {
|
|
1482
|
+
allDiagnostics.push({ ...d, routerFile: urlsFile });
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1486
|
+
const uniqueDiagnostics = allDiagnostics.filter((d) => {
|
|
1487
|
+
const key = `${d.sourceFile}:${d.pathPrefix}:${d.reason}`;
|
|
1488
|
+
if (seen.has(key)) return false;
|
|
1489
|
+
seen.add(key);
|
|
1490
|
+
return true;
|
|
1491
|
+
});
|
|
1492
|
+
if (uniqueDiagnostics.length > 0 && mode === "default") {
|
|
1206
1493
|
console.error("\n[rango] Unresolvable includes detected:\n");
|
|
1207
|
-
formatDiagnostics(
|
|
1494
|
+
formatDiagnostics(uniqueDiagnostics);
|
|
1208
1495
|
console.error(
|
|
1209
1496
|
"\nThe static parser cannot resolve these includes because they use factory functions or dynamic expressions.\n\nOptions:\n rango generate <path> --runtime Use Vite-based discovery (requires vite)\n rango generate <path> --static Accept partial output (missing routes above)\n"
|
|
1210
1497
|
);
|
|
1211
1498
|
process.exit(1);
|
|
1212
1499
|
}
|
|
1213
|
-
if (
|
|
1500
|
+
if (uniqueDiagnostics.length > 0 && mode === "static") {
|
|
1214
1501
|
console.warn(
|
|
1215
1502
|
"\n[rango] Warning: partial output (unresolvable includes):\n"
|
|
1216
1503
|
);
|
|
1217
|
-
formatDiagnostics(
|
|
1504
|
+
formatDiagnostics(uniqueDiagnostics);
|
|
1218
1505
|
console.warn("");
|
|
1219
1506
|
}
|
|
1507
|
+
const nestedRouterConflict = findNestedRouterConflict(routerFiles);
|
|
1508
|
+
if (nestedRouterConflict) {
|
|
1509
|
+
console.error(
|
|
1510
|
+
`
|
|
1511
|
+
${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
|
|
1512
|
+
`
|
|
1513
|
+
);
|
|
1514
|
+
process.exit(1);
|
|
1515
|
+
}
|
|
1516
|
+
for (const urlsFile of urlsFiles) {
|
|
1517
|
+
writePerModuleRouteTypesForFile(urlsFile);
|
|
1518
|
+
}
|
|
1220
1519
|
for (const routerFile of routerFiles) {
|
|
1221
1520
|
const projectRoot = findProjectRoot(routerFile);
|
|
1222
1521
|
writeCombinedRouteTypes(projectRoot, [routerFile]);
|
|
@@ -1257,6 +1556,15 @@ async function runRuntimeDiscovery(args, configFile) {
|
|
|
1257
1556
|
console.error("[rango] No router files found in the provided paths");
|
|
1258
1557
|
process.exit(1);
|
|
1259
1558
|
}
|
|
1559
|
+
const nestedRouterConflict = findNestedRouterConflict(routerEntries);
|
|
1560
|
+
if (nestedRouterConflict) {
|
|
1561
|
+
console.error(
|
|
1562
|
+
`
|
|
1563
|
+
${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
|
|
1564
|
+
`
|
|
1565
|
+
);
|
|
1566
|
+
process.exit(1);
|
|
1567
|
+
}
|
|
1260
1568
|
let discoverAndWriteRouteTypes2;
|
|
1261
1569
|
try {
|
|
1262
1570
|
const mod = await Promise.resolve().then(() => (init_runtime_discovery(), runtime_discovery_exports));
|
|
@@ -1271,8 +1579,8 @@ async function runRuntimeDiscovery(args, configFile) {
|
|
|
1271
1579
|
}
|
|
1272
1580
|
process.exit(1);
|
|
1273
1581
|
}
|
|
1274
|
-
const projectRoot = findProjectRoot(routerEntries[0]);
|
|
1275
1582
|
for (const entry of routerEntries) {
|
|
1583
|
+
const projectRoot = findProjectRoot(entry);
|
|
1276
1584
|
const result = await discoverAndWriteRouteTypes2({
|
|
1277
1585
|
root: projectRoot,
|
|
1278
1586
|
configFile,
|