@rangojs/router 0.0.0-experimental.fb4fdc18 → 0.0.0-experimental.fce7fbd1
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 +9 -9
- package/dist/bin/rango.js +147 -57
- package/dist/testing/vitest.js +48 -0
- package/dist/vite/index.js +914 -485
- package/package.json +55 -11
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +220 -30
- package/skills/caching/SKILL.md +116 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +214 -18
- package/skills/host-router/SKILL.md +45 -20
- package/skills/intercept/SKILL.md +26 -4
- package/skills/layout/SKILL.md +6 -7
- package/skills/links/SKILL.md +173 -17
- package/skills/loader/SKILL.md +149 -6
- package/skills/middleware/SKILL.md +13 -9
- package/skills/migrate-nextjs/SKILL.md +1 -1
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +5 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +242 -26
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +58 -9
- package/skills/route/SKILL.md +13 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +53 -41
- package/skills/testing/SKILL.md +599 -0
- package/skills/typesafety/SKILL.md +310 -26
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +117 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/event-controller.ts +42 -66
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +6 -6
- package/src/browser/navigation-client.ts +12 -15
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +9 -19
- package/src/browser/react/NavigationProvider.tsx +29 -40
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +17 -9
- package/src/browser/react/use-params.ts +3 -4
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +14 -1
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +30 -16
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +23 -30
- package/src/browser/types.ts +2 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/index.ts +2 -0
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +55 -14
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-scope.ts +28 -42
- package/src/cache/cf/cf-cache-store.ts +49 -6
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +10 -8
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -1
- package/src/handle.ts +26 -13
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -20
- package/src/index.rsc.ts +6 -4
- package/src/index.ts +13 -6
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/prerender.ts +4 -4
- package/src/response-utils.ts +9 -0
- package/src/reverse.ts +65 -41
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +238 -263
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +37 -14
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +19 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/handler-context.ts +4 -42
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +2 -2
- package/src/router/loader-resolution.ts +16 -2
- package/src/router/match-handlers.ts +62 -20
- package/src/router/match-middleware/cache-lookup.ts +44 -91
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +32 -30
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +1 -1
- package/src/router/middleware.ts +46 -78
- package/src/router/prerender-match.ts +1 -1
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +4 -28
- package/src/router/revalidation.ts +43 -1
- package/src/router/router-interfaces.ts +45 -28
- package/src/router/router-options.ts +40 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +19 -6
- package/src/router/segment-resolution/revalidation.ts +19 -6
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry.ts +99 -0
- package/src/router/types.ts +8 -0
- package/src/router.ts +37 -21
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +20 -65
- package/src/rsc/helpers.ts +22 -2
- package/src/rsc/index.ts +1 -1
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/response-route-handler.ts +32 -52
- package/src/rsc/rsc-rendering.ts +27 -53
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +13 -37
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +2 -2
- package/src/search-params.ts +4 -4
- package/src/segment-system.tsx +121 -65
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +118 -51
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +10 -0
- package/src/static-handler.ts +1 -1
- package/src/testing/cache-status.ts +166 -0
- package/src/testing/collect-handle.ts +63 -0
- package/src/testing/dispatch.ts +440 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +154 -0
- package/src/testing/e2e/index.ts +149 -0
- package/src/testing/e2e/matchers.ts +51 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +306 -0
- package/src/testing/e2e/server.ts +183 -0
- package/src/testing/flight-matchers.ts +104 -0
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +22 -0
- package/src/testing/flight.ts +182 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +193 -0
- package/src/testing/render-route.tsx +536 -0
- package/src/testing/run-loader.ts +296 -0
- package/src/testing/run-middleware.ts +170 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +183 -0
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +56 -11
- package/src/types/index.ts +1 -0
- package/src/types/segments.ts +18 -1
- package/src/urls/include-helper.ts +10 -53
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper-types.ts +11 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +20 -19
- package/src/urls/type-extraction.ts +26 -116
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +1 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +70 -48
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/prerender-collection.ts +19 -25
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/state.ts +33 -0
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin-types.ts +67 -0
- package/src/vite/plugins/cjs-to-esm.ts +3 -7
- package/src/vite/plugins/client-ref-hashing.ts +12 -1
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -1
- package/src/vite/plugins/expose-action-id.ts +2 -2
- package/src/vite/plugins/expose-id-utils.ts +12 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-internal-ids.ts +47 -67
- package/src/vite/plugins/performance-tracks.ts +12 -16
- package/src/vite/plugins/use-cache-transform.ts +13 -11
- package/src/vite/plugins/version-injector.ts +2 -12
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +67 -15
- package/src/vite/router-discovery.ts +208 -63
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +21 -5
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
package/README.md
CHANGED
|
@@ -602,7 +602,7 @@ export function Nav({ home, post }: { home: string; post: string }) {
|
|
|
602
602
|
}
|
|
603
603
|
```
|
|
604
604
|
|
|
605
|
-
For client-side navigation to static paths (no named-route lookup), use `href()` — see below. For URLs tied to named routes,
|
|
605
|
+
For client-side navigation to static paths (no named-route lookup), use `href()` — see below. For URLs tied to named routes, you have two options: import the per-module generated `routes` map and use `useReverse(routes)` for in-module names (see [`/links` skill](./skills/links/SKILL.md)), or generate the URL on the server and pass the string in for cross-module URLs.
|
|
606
606
|
|
|
607
607
|
### `href()` for Path Validation (Client Components)
|
|
608
608
|
|
|
@@ -943,9 +943,9 @@ import { createHostRouter } from "@rangojs/router/host";
|
|
|
943
943
|
|
|
944
944
|
const hostRouter = createHostRouter();
|
|
945
945
|
|
|
946
|
-
hostRouter.host(["*.localhost"]).
|
|
947
|
-
hostRouter.host(["localhost"]).
|
|
948
|
-
hostRouter.fallback().
|
|
946
|
+
hostRouter.host(["*.localhost"]).lazy(() => import("./apps/admin/handler.js"));
|
|
947
|
+
hostRouter.host(["localhost"]).lazy(() => import("./apps/site/handler.js"));
|
|
948
|
+
hostRouter.fallback().lazy(() => import("./apps/site/handler.js"));
|
|
949
949
|
|
|
950
950
|
export default {
|
|
951
951
|
async fetch(request, env, ctx) {
|
|
@@ -954,7 +954,7 @@ export default {
|
|
|
954
954
|
};
|
|
955
955
|
```
|
|
956
956
|
|
|
957
|
-
|
|
957
|
+
Use `.lazy(() => import("./sub-app"))` to mount a lazily-imported sub-app (a module whose `default` export is a handler or nested host router), and `.map((request) => Response)` for an inline request handler. Only `.lazy()` mounts are imported during build-time discovery; `.map(() => import(...))` is a type error. Each sub-app has its own `createRouter()` and `urls()`. Patterns are matched in registration order — register more specific patterns (subdomains) before catch-alls.
|
|
958
958
|
|
|
959
959
|
## Meta Tags
|
|
960
960
|
|
|
@@ -993,16 +993,16 @@ Auto-detects file type:
|
|
|
993
993
|
|
|
994
994
|
## Type Safety
|
|
995
995
|
|
|
996
|
-
The Vite plugin automatically generates a `router.named-routes.gen.ts` file that globally registers route names, patterns, and search schemas via `
|
|
996
|
+
The Vite plugin automatically generates a `router.named-routes.gen.ts` file that globally registers route names, patterns, and search schemas via `Rango.GeneratedRouteMap`. This powers server-side named-route typing such as `Handler<"name">`, `ctx.reverse()`, `getRequestContext().reverse()`, and `RouteParams<"name">` without any manual route registration. The gen file is updated on dev server startup, HMR, and production builds.
|
|
997
997
|
|
|
998
|
-
Use the generated map by default. Augment `
|
|
998
|
+
Use the generated map by default. Augment `Rango.RegisteredRoutes` only when you need the richer `typeof router.routeMap` shape globally, especially for response-aware and path-based utilities.
|
|
999
999
|
|
|
1000
1000
|
```typescript
|
|
1001
1001
|
// router.tsx
|
|
1002
1002
|
const router = createRouter<AppBindings>({}).routes(urlpatterns);
|
|
1003
1003
|
|
|
1004
1004
|
declare global {
|
|
1005
|
-
namespace
|
|
1005
|
+
namespace Rango {
|
|
1006
1006
|
interface Env extends AppEnv {}
|
|
1007
1007
|
interface Vars extends AppVars {}
|
|
1008
1008
|
interface RegisteredRoutes extends typeof router.routeMap {}
|
|
@@ -1014,7 +1014,7 @@ Quick rule of thumb:
|
|
|
1014
1014
|
|
|
1015
1015
|
- `GeneratedRouteMap` (auto-generated) — use for server-side named-route typing: `Handler<"name">`, `ctx.reverse()`, `Prerender<"name">`
|
|
1016
1016
|
- `typeof router.routeMap` — use when you need route entries with response metadata
|
|
1017
|
-
- `RegisteredRoutes` (manual augmentation) — use to expose `typeof router.routeMap` globally for `href()`, `
|
|
1017
|
+
- `RegisteredRoutes` (manual augmentation) — use to expose `typeof router.routeMap` globally for `href()`, `Rango.Path`, `Rango.PathResponse`, and other path/response-aware utilities
|
|
1018
1018
|
|
|
1019
1019
|
For extracted reusable loaders or middleware, prefer global dotted names on
|
|
1020
1020
|
`ctx.reverse()` by default. If you want type-safe local names for a specific
|
package/dist/bin/rango.js
CHANGED
|
@@ -139,7 +139,7 @@ function generatePerModuleTypesSource(routes) {
|
|
|
139
139
|
const valid = routes.filter(({ name }) => {
|
|
140
140
|
if (!name || /["'\\`\n\r]/.test(name)) {
|
|
141
141
|
console.warn(
|
|
142
|
-
`[
|
|
142
|
+
`[rango] Skipping route with invalid name: ${JSON.stringify(name)}`
|
|
143
143
|
);
|
|
144
144
|
return false;
|
|
145
145
|
}
|
|
@@ -149,7 +149,7 @@ function generatePerModuleTypesSource(routes) {
|
|
|
149
149
|
for (const { name, pattern, params, search } of valid) {
|
|
150
150
|
if (deduped.has(name)) {
|
|
151
151
|
console.warn(
|
|
152
|
-
`[
|
|
152
|
+
`[rango] Duplicate route name "${name}" \u2014 keeping first definition`
|
|
153
153
|
);
|
|
154
154
|
continue;
|
|
155
155
|
}
|
|
@@ -186,7 +186,7 @@ ${objectBody}
|
|
|
186
186
|
} as const;
|
|
187
187
|
|
|
188
188
|
declare global {
|
|
189
|
-
namespace
|
|
189
|
+
namespace Rango {
|
|
190
190
|
interface GeneratedRouteMap extends Readonly<typeof NamedRoutes> {}
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -211,7 +211,7 @@ function findTsFiles(dir, filter) {
|
|
|
211
211
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
212
212
|
} catch (err) {
|
|
213
213
|
console.warn(
|
|
214
|
-
`[
|
|
214
|
+
`[rango] Failed to scan directory ${dir}: ${err.message}`
|
|
215
215
|
);
|
|
216
216
|
return results;
|
|
217
217
|
}
|
|
@@ -456,7 +456,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
456
456
|
const realPath = resolve(filePath);
|
|
457
457
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
458
458
|
if (visited.has(key)) {
|
|
459
|
-
console.warn(`[
|
|
459
|
+
console.warn(`[rango] Circular include detected, skipping: ${key}`);
|
|
460
460
|
return { routes: {}, searchSchemas: {} };
|
|
461
461
|
}
|
|
462
462
|
visited.add(key);
|
|
@@ -543,12 +543,12 @@ function writePerModuleRouteTypesForFile(filePath) {
|
|
|
543
543
|
} else {
|
|
544
544
|
routes = extractRoutesFromSource(source);
|
|
545
545
|
}
|
|
546
|
-
const genPath = filePath.replace(/\.(tsx?)$/, ".gen.ts");
|
|
546
|
+
const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
|
|
547
547
|
if (routes.length === 0) {
|
|
548
548
|
if (varNames.length > 0 && !existsSync2(genPath)) {
|
|
549
549
|
writeFileSync(genPath, generatePerModuleTypesSource([]));
|
|
550
550
|
console.log(
|
|
551
|
-
`[
|
|
551
|
+
`[rango] Generated route types (placeholder) -> ${genPath}`
|
|
552
552
|
);
|
|
553
553
|
}
|
|
554
554
|
return;
|
|
@@ -557,11 +557,11 @@ function writePerModuleRouteTypesForFile(filePath) {
|
|
|
557
557
|
const existing = existsSync2(genPath) ? readFileSync2(genPath, "utf-8") : null;
|
|
558
558
|
if (existing !== genSource) {
|
|
559
559
|
writeFileSync(genPath, genSource);
|
|
560
|
-
console.log(`[
|
|
560
|
+
console.log(`[rango] Generated route types -> ${genPath}`);
|
|
561
561
|
}
|
|
562
562
|
} catch (err) {
|
|
563
563
|
console.warn(
|
|
564
|
-
`[
|
|
564
|
+
`[rango] Failed to generate route types for ${filePath}: ${err.message}`
|
|
565
565
|
);
|
|
566
566
|
}
|
|
567
567
|
}
|
|
@@ -576,6 +576,75 @@ var init_per_module_writer = __esm({
|
|
|
576
576
|
}
|
|
577
577
|
});
|
|
578
578
|
|
|
579
|
+
// src/build/route-types/source-scan.ts
|
|
580
|
+
function isLineTerminator(ch) {
|
|
581
|
+
const c = ch.charCodeAt(0);
|
|
582
|
+
return c === 10 || c === 13 || c === 8232 || c === 8233;
|
|
583
|
+
}
|
|
584
|
+
function makeCodeClassifier(code) {
|
|
585
|
+
const n = code.length;
|
|
586
|
+
let i = 0;
|
|
587
|
+
let skipStart = -1;
|
|
588
|
+
let skipEnd = -1;
|
|
589
|
+
return (q) => {
|
|
590
|
+
if (q >= skipStart && q < skipEnd) return false;
|
|
591
|
+
while (i < n && i <= q) {
|
|
592
|
+
const c = code[i];
|
|
593
|
+
const d = i + 1 < n ? code[i + 1] : "";
|
|
594
|
+
let end = -1;
|
|
595
|
+
if (c === "/" && d === "/") {
|
|
596
|
+
let j = i + 2;
|
|
597
|
+
while (j < n && !isLineTerminator(code[j])) j++;
|
|
598
|
+
end = j;
|
|
599
|
+
} else if (c === "/" && d === "*") {
|
|
600
|
+
let j = i + 2;
|
|
601
|
+
while (j < n && !(code[j] === "*" && code[j + 1] === "/")) j++;
|
|
602
|
+
end = Math.min(n, j + 2);
|
|
603
|
+
} else if (c === '"' || c === "'" || c === "`") {
|
|
604
|
+
let j = i + 1;
|
|
605
|
+
while (j < n) {
|
|
606
|
+
if (code[j] === "\\") {
|
|
607
|
+
j += 2;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (code[j] === c) {
|
|
611
|
+
j++;
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
j++;
|
|
615
|
+
}
|
|
616
|
+
end = j;
|
|
617
|
+
}
|
|
618
|
+
if (end >= 0) {
|
|
619
|
+
if (q < end) {
|
|
620
|
+
skipStart = i;
|
|
621
|
+
skipEnd = end;
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
i = end;
|
|
625
|
+
} else {
|
|
626
|
+
i++;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return true;
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function firstCodeMatchIndex(code, pattern) {
|
|
633
|
+
const inCode = makeCodeClassifier(code);
|
|
634
|
+
pattern.lastIndex = 0;
|
|
635
|
+
let m;
|
|
636
|
+
while ((m = pattern.exec(code)) !== null) {
|
|
637
|
+
if (inCode(m.index)) return m.index;
|
|
638
|
+
if (pattern.lastIndex <= m.index) pattern.lastIndex = m.index + 1;
|
|
639
|
+
}
|
|
640
|
+
return -1;
|
|
641
|
+
}
|
|
642
|
+
var init_source_scan = __esm({
|
|
643
|
+
"src/build/route-types/source-scan.ts"() {
|
|
644
|
+
"use strict";
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
|
|
579
648
|
// src/build/route-types/router-processing.ts
|
|
580
649
|
import {
|
|
581
650
|
readFileSync as readFileSync3,
|
|
@@ -612,7 +681,7 @@ function findRouterFilesRecursive(dir, filter, results) {
|
|
|
612
681
|
entries = readdirSync2(dir, { withFileTypes: true });
|
|
613
682
|
} catch (err) {
|
|
614
683
|
console.warn(
|
|
615
|
-
`[
|
|
684
|
+
`[rango] Failed to scan directory ${dir}: ${err.message}`
|
|
616
685
|
);
|
|
617
686
|
return;
|
|
618
687
|
}
|
|
@@ -630,7 +699,7 @@ function findRouterFilesRecursive(dir, filter, results) {
|
|
|
630
699
|
if (filter && !filter(fullPath)) continue;
|
|
631
700
|
try {
|
|
632
701
|
const source = readFileSync3(fullPath, "utf-8");
|
|
633
|
-
if (ROUTER_CALL_PATTERN.test(source)) {
|
|
702
|
+
if (ROUTER_CALL_PATTERN.test(source) && firstCodeMatchIndex(source, ROUTER_CALL_PATTERN_G) >= 0) {
|
|
634
703
|
routerFilesInDir.push(fullPath);
|
|
635
704
|
}
|
|
636
705
|
} catch {
|
|
@@ -668,7 +737,7 @@ function findNestedRouterConflict(routerFiles) {
|
|
|
668
737
|
}
|
|
669
738
|
return null;
|
|
670
739
|
}
|
|
671
|
-
function formatNestedRouterConflictError(conflict, prefix = "[
|
|
740
|
+
function formatNestedRouterConflictError(conflict, prefix = "[rango]") {
|
|
672
741
|
return `${prefix} Nested router roots are not supported.
|
|
673
742
|
Router root: ${conflict.ancestor}
|
|
674
743
|
Nested router: ${conflict.nested}
|
|
@@ -764,19 +833,38 @@ function extractBasenameFromRouter(code) {
|
|
|
764
833
|
visit(sourceFile);
|
|
765
834
|
return result;
|
|
766
835
|
}
|
|
767
|
-
function applyBasenameToRoutes(result,
|
|
836
|
+
function applyBasenameToRoutes(result, basename) {
|
|
768
837
|
const prefixed = {};
|
|
769
838
|
for (const [name, pattern] of Object.entries(result.routes)) {
|
|
770
839
|
if (pattern === "/") {
|
|
771
|
-
prefixed[name] =
|
|
772
|
-
} else if (
|
|
773
|
-
prefixed[name] =
|
|
840
|
+
prefixed[name] = basename;
|
|
841
|
+
} else if (basename.endsWith("/") && pattern.startsWith("/")) {
|
|
842
|
+
prefixed[name] = basename + pattern.slice(1);
|
|
774
843
|
} else {
|
|
775
|
-
prefixed[name] =
|
|
844
|
+
prefixed[name] = basename + pattern;
|
|
776
845
|
}
|
|
777
846
|
}
|
|
778
847
|
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
779
848
|
}
|
|
849
|
+
function genFileTsPath(sourceFile) {
|
|
850
|
+
const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
851
|
+
return join2(dirname2(sourceFile), `${base}.named-routes.gen.ts`);
|
|
852
|
+
}
|
|
853
|
+
function resolveSearchSchemas(publicRouteNames, runtimeSchemas, sourceFile) {
|
|
854
|
+
if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
|
|
855
|
+
return runtimeSchemas;
|
|
856
|
+
}
|
|
857
|
+
const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
|
|
858
|
+
if (Object.keys(staticParsed.searchSchemas).length === 0) {
|
|
859
|
+
return runtimeSchemas;
|
|
860
|
+
}
|
|
861
|
+
const filtered = {};
|
|
862
|
+
for (const name of publicRouteNames) {
|
|
863
|
+
const schema = staticParsed.searchSchemas[name];
|
|
864
|
+
if (schema) filtered[name] = schema;
|
|
865
|
+
}
|
|
866
|
+
return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
|
|
867
|
+
}
|
|
780
868
|
function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
781
869
|
let routerSource;
|
|
782
870
|
try {
|
|
@@ -789,7 +877,7 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
|
789
877
|
return { routes: {}, searchSchemas: {} };
|
|
790
878
|
}
|
|
791
879
|
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
792
|
-
const
|
|
880
|
+
const basename = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
|
|
793
881
|
let result;
|
|
794
882
|
if (extraction.kind === "inline") {
|
|
795
883
|
result = buildCombinedRouteMapWithSearch(
|
|
@@ -814,8 +902,8 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
|
814
902
|
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
815
903
|
}
|
|
816
904
|
}
|
|
817
|
-
if (
|
|
818
|
-
result = applyBasenameToRoutes(result,
|
|
905
|
+
if (basename) {
|
|
906
|
+
result = applyBasenameToRoutes(result, basename);
|
|
819
907
|
}
|
|
820
908
|
return result;
|
|
821
909
|
}
|
|
@@ -897,7 +985,7 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
897
985
|
if (existsSync3(oldCombinedPath)) {
|
|
898
986
|
unlinkSync(oldCombinedPath);
|
|
899
987
|
console.log(
|
|
900
|
-
`[
|
|
988
|
+
`[rango] Removed stale combined route types: ${oldCombinedPath}`
|
|
901
989
|
);
|
|
902
990
|
}
|
|
903
991
|
} catch {
|
|
@@ -919,18 +1007,12 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
919
1007
|
}
|
|
920
1008
|
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
921
1009
|
}
|
|
922
|
-
const
|
|
923
|
-
/\.(tsx?|jsx?)$/,
|
|
924
|
-
""
|
|
925
|
-
);
|
|
926
|
-
const outPath = join2(
|
|
927
|
-
dirname2(routerFilePath),
|
|
928
|
-
`${routerBasename}.named-routes.gen.ts`
|
|
929
|
-
);
|
|
1010
|
+
const outPath = genFileTsPath(routerFilePath);
|
|
930
1011
|
const existing = existsSync3(outPath) ? readFileSync3(outPath, "utf-8") : null;
|
|
931
1012
|
if (Object.keys(result.routes).length === 0) {
|
|
932
1013
|
if (!existing) {
|
|
933
1014
|
const emptySource = generateRouteTypesSource({});
|
|
1015
|
+
opts?.onWrite?.(outPath, emptySource);
|
|
934
1016
|
writeFileSync2(outPath, emptySource);
|
|
935
1017
|
}
|
|
936
1018
|
continue;
|
|
@@ -950,22 +1032,25 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
950
1032
|
continue;
|
|
951
1033
|
}
|
|
952
1034
|
}
|
|
1035
|
+
opts?.onWrite?.(outPath, source);
|
|
953
1036
|
writeFileSync2(outPath, source);
|
|
954
1037
|
console.log(
|
|
955
|
-
`[
|
|
1038
|
+
`[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`
|
|
956
1039
|
);
|
|
957
1040
|
}
|
|
958
1041
|
}
|
|
959
1042
|
}
|
|
960
|
-
var ROUTER_CALL_PATTERN;
|
|
1043
|
+
var ROUTER_CALL_PATTERN, ROUTER_CALL_PATTERN_G;
|
|
961
1044
|
var init_router_processing = __esm({
|
|
962
1045
|
"src/build/route-types/router-processing.ts"() {
|
|
963
1046
|
"use strict";
|
|
964
1047
|
init_codegen();
|
|
1048
|
+
init_source_scan();
|
|
965
1049
|
init_include_resolution();
|
|
966
1050
|
init_per_module_writer();
|
|
967
1051
|
init_route_name();
|
|
968
1052
|
ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
|
|
1053
|
+
ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
|
|
969
1054
|
}
|
|
970
1055
|
});
|
|
971
1056
|
|
|
@@ -1003,7 +1088,7 @@ import {
|
|
|
1003
1088
|
import { createElement, StrictMode } from "react";
|
|
1004
1089
|
import { hydrateRoot } from "react-dom/client";
|
|
1005
1090
|
import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
|
|
1006
|
-
import { initBrowserApp,
|
|
1091
|
+
import { initBrowserApp, Rango } from "@rangojs/router/browser";
|
|
1007
1092
|
|
|
1008
1093
|
async function initializeApp() {
|
|
1009
1094
|
const deps = {
|
|
@@ -1018,7 +1103,7 @@ async function initializeApp() {
|
|
|
1018
1103
|
|
|
1019
1104
|
hydrateRoot(
|
|
1020
1105
|
document,
|
|
1021
|
-
createElement(StrictMode, null, createElement(
|
|
1106
|
+
createElement(StrictMode, null, createElement(Rango))
|
|
1022
1107
|
);
|
|
1023
1108
|
}
|
|
1024
1109
|
|
|
@@ -1050,7 +1135,8 @@ export const renderHTML = createSSRHandler({
|
|
|
1050
1135
|
// src/vite/plugins/version-plugin.ts
|
|
1051
1136
|
var version_plugin_exports = {};
|
|
1052
1137
|
__export(version_plugin_exports, {
|
|
1053
|
-
createVersionPlugin: () => createVersionPlugin
|
|
1138
|
+
createVersionPlugin: () => createVersionPlugin,
|
|
1139
|
+
isViteDepCachePath: () => isViteDepCachePath
|
|
1054
1140
|
});
|
|
1055
1141
|
import { parseAst } from "vite";
|
|
1056
1142
|
function isCodeModule(id) {
|
|
@@ -1062,7 +1148,7 @@ function normalizeModuleId(id) {
|
|
|
1062
1148
|
function getClientModuleSignature(source) {
|
|
1063
1149
|
let program;
|
|
1064
1150
|
try {
|
|
1065
|
-
program = parseAst(source, {
|
|
1151
|
+
program = parseAst(source, { lang: "tsx" });
|
|
1066
1152
|
} catch {
|
|
1067
1153
|
return void 0;
|
|
1068
1154
|
}
|
|
@@ -1145,11 +1231,12 @@ function createVersionPlugin() {
|
|
|
1145
1231
|
let currentVersion = buildVersion;
|
|
1146
1232
|
let isDev = false;
|
|
1147
1233
|
let server = null;
|
|
1234
|
+
let resolvedCacheDir;
|
|
1148
1235
|
const clientModuleSignatures = /* @__PURE__ */ new Map();
|
|
1149
1236
|
let versionCounter = 0;
|
|
1150
1237
|
const bumpVersion = (reason) => {
|
|
1151
1238
|
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
1152
|
-
console.log(`[
|
|
1239
|
+
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
1153
1240
|
const rscEnv = server?.environments?.rsc;
|
|
1154
1241
|
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
1155
1242
|
"\0" + VIRTUAL_IDS.version
|
|
@@ -1163,6 +1250,7 @@ function createVersionPlugin() {
|
|
|
1163
1250
|
enforce: "pre",
|
|
1164
1251
|
configResolved(config) {
|
|
1165
1252
|
isDev = config.command === "serve";
|
|
1253
|
+
resolvedCacheDir = config.cacheDir ? String(config.cacheDir).replace(/\\/g, "/") : void 0;
|
|
1166
1254
|
},
|
|
1167
1255
|
configureServer(devServer) {
|
|
1168
1256
|
server = devServer;
|
|
@@ -1204,6 +1292,7 @@ function createVersionPlugin() {
|
|
|
1204
1292
|
if (!isDev) return;
|
|
1205
1293
|
const isRscModule = this.environment?.name === "rsc";
|
|
1206
1294
|
if (!isRscModule) return;
|
|
1295
|
+
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
1207
1296
|
if (ctx.modules.length === 1 && ctx.modules[0].id === "\0" + VIRTUAL_IDS.version) {
|
|
1208
1297
|
return;
|
|
1209
1298
|
}
|
|
@@ -1233,6 +1322,17 @@ function createVersionPlugin() {
|
|
|
1233
1322
|
}
|
|
1234
1323
|
};
|
|
1235
1324
|
}
|
|
1325
|
+
function isViteDepCachePath(filePath, cacheDir) {
|
|
1326
|
+
if (!filePath) return false;
|
|
1327
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
1328
|
+
if (cacheDir) {
|
|
1329
|
+
const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1330
|
+
if (normalized === normalizedCacheDir || normalized.startsWith(normalizedCacheDir + "/")) {
|
|
1331
|
+
return true;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
return /\/node_modules\/\.vite[^/]*\//.test(normalized) || normalized.includes("/.vite-isolated/");
|
|
1335
|
+
}
|
|
1236
1336
|
var init_version_plugin = __esm({
|
|
1237
1337
|
"src/vite/plugins/version-plugin.ts"() {
|
|
1238
1338
|
"use strict";
|
|
@@ -1278,7 +1378,7 @@ var runtime_discovery_exports = {};
|
|
|
1278
1378
|
__export(runtime_discovery_exports, {
|
|
1279
1379
|
discoverAndWriteRouteTypes: () => discoverAndWriteRouteTypes
|
|
1280
1380
|
});
|
|
1281
|
-
import {
|
|
1381
|
+
import { resolve as resolve3 } from "node:path";
|
|
1282
1382
|
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1283
1383
|
async function discoverAndWriteRouteTypes(opts) {
|
|
1284
1384
|
let createViteServer;
|
|
@@ -1388,22 +1488,12 @@ This means createRouter() stack trace parsing matched an internal frame.
|
|
|
1388
1488
|
Set an explicit \`id\` on createRouter() or check the call site.`
|
|
1389
1489
|
);
|
|
1390
1490
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
if (schema) filtered[name] = schema;
|
|
1398
|
-
}
|
|
1399
|
-
if (Object.keys(filtered).length > 0) {
|
|
1400
|
-
routeSearchSchemas = filtered;
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
const routerDir = dirname3(sourceFile);
|
|
1405
|
-
const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
1406
|
-
const outPath = join3(routerDir, `${routerBasename}.named-routes.gen.ts`);
|
|
1491
|
+
routeSearchSchemas = resolveSearchSchemas(
|
|
1492
|
+
Object.keys(routeManifest),
|
|
1493
|
+
routeSearchSchemas,
|
|
1494
|
+
sourceFile
|
|
1495
|
+
);
|
|
1496
|
+
const outPath = genFileTsPath(sourceFile);
|
|
1407
1497
|
const source = generateRouteTypesSource(
|
|
1408
1498
|
routeManifest,
|
|
1409
1499
|
routeSearchSchemas && Object.keys(routeSearchSchemas).length > 0 ? routeSearchSchemas : void 0
|
|
@@ -1440,7 +1530,7 @@ var init_runtime_discovery = __esm({
|
|
|
1440
1530
|
|
|
1441
1531
|
// src/bin/rango.ts
|
|
1442
1532
|
init_generate_route_types();
|
|
1443
|
-
import { resolve as resolve4, dirname as
|
|
1533
|
+
import { resolve as resolve4, dirname as dirname3 } from "node:path";
|
|
1444
1534
|
import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "node:fs";
|
|
1445
1535
|
var [command, ...rawArgs] = process.argv.slice(2);
|
|
1446
1536
|
if (command === "generate") {
|
|
@@ -1511,12 +1601,12 @@ Examples:
|
|
|
1511
1601
|
);
|
|
1512
1602
|
}
|
|
1513
1603
|
function findProjectRoot(fromPath) {
|
|
1514
|
-
let dir =
|
|
1515
|
-
while (dir !==
|
|
1604
|
+
let dir = dirname3(resolve4(fromPath));
|
|
1605
|
+
while (dir !== dirname3(dir)) {
|
|
1516
1606
|
if (existsSync5(resolve4(dir, "package.json")) || existsSync5(resolve4(dir, "vite.config.ts")) || existsSync5(resolve4(dir, "vite.config.js"))) {
|
|
1517
1607
|
return dir;
|
|
1518
1608
|
}
|
|
1519
|
-
dir =
|
|
1609
|
+
dir = dirname3(dir);
|
|
1520
1610
|
}
|
|
1521
1611
|
return process.cwd();
|
|
1522
1612
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/testing/vitest.ts
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
function here(relativeFromRoot) {
|
|
4
|
+
return fileURLToPath(new URL(`../../${relativeFromRoot}`, import.meta.url));
|
|
5
|
+
}
|
|
6
|
+
function rangoTestAliases(opts = {}) {
|
|
7
|
+
const aliases = [
|
|
8
|
+
// Real impls (index.rsc.ts) for the bare specifier ONLY — exact regex so
|
|
9
|
+
// subpaths (/testing, /client, /cache, ...) are untouched. React stays the
|
|
10
|
+
// client build, so createContext and "use client" modules work.
|
|
11
|
+
{ find: /^@rangojs\/router$/, replacement: here("src/index.rsc.ts") },
|
|
12
|
+
{
|
|
13
|
+
find: "@rangojs/router:version",
|
|
14
|
+
replacement: here("src/testing/vitest-stubs/version.ts")
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
find: /^@vitejs\/plugin-rsc\/rsc$/,
|
|
18
|
+
replacement: here("src/testing/vitest-stubs/plugin-rsc.ts")
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
if (opts.cloudflare) {
|
|
22
|
+
aliases.push(
|
|
23
|
+
{
|
|
24
|
+
find: "cloudflare:workers",
|
|
25
|
+
replacement: here("src/testing/vitest-stubs/cloudflare-workers.ts")
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
find: "cloudflare:email",
|
|
29
|
+
replacement: here("src/testing/vitest-stubs/cloudflare-email.ts")
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return aliases;
|
|
34
|
+
}
|
|
35
|
+
var rangoInlineDeps = [/@rangojs[/\\]router/];
|
|
36
|
+
function rangoTestConfig(opts = {}) {
|
|
37
|
+
return {
|
|
38
|
+
alias: rangoTestAliases(opts),
|
|
39
|
+
// fresh copy so the shared rangoInlineDeps const is never aliased into (or
|
|
40
|
+
// mutated through) a consumer's resolved config
|
|
41
|
+
server: { deps: { inline: [...rangoInlineDeps] } }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
rangoInlineDeps,
|
|
46
|
+
rangoTestAliases,
|
|
47
|
+
rangoTestConfig
|
|
48
|
+
};
|