@rangojs/router 0.0.0-experimental.124 → 0.0.0-experimental.126
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 +6 -4
- package/dist/bin/rango.js +3 -4
- package/dist/vite/index.js +315 -68
- package/package.json +19 -18
- package/skills/breadcrumbs/SKILL.md +60 -0
- package/skills/hooks/SKILL.md +2 -2
- package/skills/route/SKILL.md +6 -0
- package/skills/server-actions/SKILL.md +25 -1
- package/skills/testing/SKILL.md +17 -17
- package/skills/testing/cache-prerender.md +29 -3
- package/skills/testing/flight.md +13 -10
- package/skills/testing/render-handler.md +3 -0
- package/skills/testing/server-tree.md +1 -1
- package/skills/testing/setup.md +1 -1
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +10 -0
- package/src/browser/event-controller.ts +1 -83
- package/src/browser/navigation-store-handle.ts +3 -4
- package/src/browser/navigation-store.ts +0 -39
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/partial-update.ts +23 -84
- package/src/browser/prefetch/cache.ts +6 -45
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +2 -23
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- package/src/browser/react/index.ts +0 -45
- package/src/browser/react/location-state-shared.ts +0 -13
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +2 -1
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/rsc-router.tsx +10 -3
- package/src/browser/server-action-bridge.ts +51 -3
- package/src/browser/types.ts +23 -5
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/index.ts +8 -9
- package/src/build/route-trie.ts +46 -11
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/router-processing.ts +0 -8
- package/src/cache/cache-policy.ts +0 -54
- package/src/cache/cache-runtime.ts +48 -24
- package/src/cache/cache-scope.ts +0 -27
- package/src/cache/cache-tag.ts +0 -37
- package/src/cache/cf/cf-cache-store.ts +72 -45
- package/src/cache/cf/index.ts +0 -24
- package/src/cache/document-cache.ts +10 -36
- package/src/cache/handle-snapshot.ts +0 -40
- package/src/cache/index.ts +0 -27
- package/src/cache/memory-segment-store.ts +0 -52
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/types.ts +0 -98
- package/src/client.rsc.tsx +4 -22
- package/src/client.tsx +19 -32
- package/src/context-var.ts +12 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/handle.ts +2 -12
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +6 -0
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +0 -16
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +0 -4
- package/src/index.rsc.ts +27 -2
- package/src/index.ts +7 -0
- package/src/internal-debug.ts +2 -4
- package/src/loader.rsc.ts +4 -15
- package/src/loader.ts +3 -9
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +23 -30
- package/src/prerender.ts +34 -0
- package/src/redirect-origin.ts +100 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +1 -44
- package/src/route-definition/dsl-helpers.ts +7 -19
- package/src/route-definition/helpers-types.ts +3 -3
- package/src/route-definition/redirect.ts +43 -9
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-map-builder.ts +0 -16
- package/src/router/content-negotiation.ts +0 -13
- package/src/router/error-handling.ts +12 -16
- package/src/router/find-match.ts +4 -31
- package/src/router/intercept-resolution.ts +10 -1
- package/src/router/lazy-includes.ts +1 -57
- package/src/router/loader-resolution.ts +25 -23
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +1 -25
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +0 -43
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +96 -179
- package/src/router/match-middleware/cache-store.ts +0 -31
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +1 -52
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +77 -60
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +5 -56
- package/src/router/prerender-match.ts +56 -51
- package/src/router/request-classification.ts +1 -38
- package/src/router/revalidation.ts +14 -62
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +10 -0
- package/src/router/segment-resolution/fresh.ts +25 -57
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +35 -23
- package/src/router/segment-resolution/revalidation.ts +188 -283
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +0 -3
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +0 -22
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +66 -45
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +8 -11
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +20 -4
- package/src/rsc/helpers.ts +71 -3
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/origin-guard.ts +9 -15
- package/src/rsc/progressive-enhancement.ts +10 -1
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-route-handler.ts +23 -18
- package/src/rsc/rsc-rendering.ts +2 -7
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +34 -29
- package/src/rsc/types.ts +6 -3
- package/src/search-params.ts +0 -16
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +79 -88
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +29 -92
- package/src/ssr/index.tsx +14 -14
- package/src/static-handler.ts +2 -27
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +1 -24
- package/src/testing/dispatch.ts +43 -6
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/flight-matchers.ts +0 -13
- package/src/testing/flight-normalize.ts +3 -30
- package/src/testing/flight.ts +46 -48
- package/src/testing/generated-routes.ts +1 -41
- package/src/testing/index.ts +1 -21
- package/src/testing/internal/context.ts +3 -45
- package/src/testing/internal/seed-vars.ts +0 -26
- package/src/testing/render-handler.ts +31 -61
- package/src/testing/render-route.tsx +75 -103
- package/src/testing/run-loader.ts +0 -96
- package/src/testing/run-middleware.ts +0 -26
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/error-types.ts +25 -89
- package/src/types/global-namespace.ts +4 -14
- package/src/types/handler-context.ts +28 -9
- package/src/types/index.ts +0 -10
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +0 -13
- package/src/urls/include-helper.ts +0 -4
- package/src/urls/index.ts +0 -6
- package/src/urls/path-helper-types.ts +2 -2
- package/src/urls/path-helper.ts +0 -54
- package/src/urls/urls-function.ts +0 -13
- package/src/use-loader.tsx +0 -186
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/discover-routers.ts +28 -18
- package/src/vite/discovery/prerender-collection.ts +2 -4
- package/src/vite/discovery/state.ts +5 -0
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/plugin-types.ts +35 -9
- package/src/vite/plugins/cjs-to-esm.ts +0 -11
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +0 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +0 -38
- package/src/vite/plugins/expose-ids/handler-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/loader-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +10 -0
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/version-injector.ts +0 -20
- package/src/vite/plugins/version-plugin.ts +1 -49
- package/src/vite/plugins/virtual-entries.ts +0 -15
- package/src/vite/rango.ts +2 -108
- package/src/vite/router-discovery.ts +9 -1
- package/src/vite/utils/ast-handler-extract.ts +0 -16
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +0 -4
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -35
- package/src/vite/utils/shared-utils.ts +3 -35
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -9,18 +9,6 @@ export function hasCreateLoaderImport(code: string): boolean {
|
|
|
9
9
|
);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Generate lightweight client stubs for loader files.
|
|
14
|
-
*
|
|
15
|
-
* When a loader file is imported from a client component (e.g., for useLoader()),
|
|
16
|
-
* the client only needs { __brand: "loader", $$id: "..." } objects.
|
|
17
|
-
* This function replaces the entire file contents with just those stub exports,
|
|
18
|
-
* preventing server-only data (constants, DB queries, etc.) from leaking into
|
|
19
|
-
* the client bundle.
|
|
20
|
-
*
|
|
21
|
-
* Only applies when ALL named exports are createLoader() calls (plus type exports
|
|
22
|
-
* which are erased at compile time). Files with mixed exports are left untouched.
|
|
23
|
-
*/
|
|
24
12
|
export function generateClientLoaderStubs(
|
|
25
13
|
bindings: CreateExportBinding[],
|
|
26
14
|
code: string,
|
|
@@ -56,9 +44,6 @@ export function transformLoaders(
|
|
|
56
44
|
|
|
57
45
|
const loaderId = makeStubId(filePath, exportName, isBuild);
|
|
58
46
|
|
|
59
|
-
// Inject $$id as hidden third parameter.
|
|
60
|
-
// createLoader(fn) -> createLoader(fn, undefined, "id")
|
|
61
|
-
// createLoader(fn, true) -> createLoader(fn, true, "id")
|
|
62
47
|
const paramInjection =
|
|
63
48
|
binding.argCount === 1 ? `, undefined, "${loaderId}"` : `, "${loaderId}"`;
|
|
64
49
|
s.appendLeft(binding.callCloseParenPos, paramInjection);
|
|
@@ -32,15 +32,11 @@ export function transformRouter(
|
|
|
32
32
|
const callStart = match.index;
|
|
33
33
|
const parenPos = match.index + match[0].length - 1;
|
|
34
34
|
|
|
35
|
-
// Scope the $$id check to within this call's arguments only,
|
|
36
|
-
// not the entire remaining file.
|
|
37
35
|
const closeParen = findMatchingParen(code, parenPos + 1);
|
|
38
36
|
const callArgs = code.slice(parenPos + 1, closeParen);
|
|
39
37
|
|
|
40
|
-
// Skip if $$id is already present in this call
|
|
41
38
|
if (callArgs.includes("$$id")) continue;
|
|
42
39
|
|
|
43
|
-
// Compute line number for this call
|
|
44
40
|
const lineNumber = code.slice(0, callStart).split("\n").length;
|
|
45
41
|
const hash = createHash("sha256")
|
|
46
42
|
.update(`${filePath}:${lineNumber}`)
|
|
@@ -48,9 +44,6 @@ export function transformRouter(
|
|
|
48
44
|
.slice(0, 8);
|
|
49
45
|
|
|
50
46
|
changed = true;
|
|
51
|
-
// $$sourceFile uses the absolute path so that downstream consumers
|
|
52
|
-
// (virtual-module-codegen, runtime-discovery) can resolve gen file
|
|
53
|
-
// imports correctly via path.dirname / path.join.
|
|
54
47
|
const sourceFilePath = absolutePath ?? filePath;
|
|
55
48
|
const injected = ` $$id: "${hash}", $$sourceFile: "${sourceFilePath}", $$routeNames: ${routeNamesVar},`;
|
|
56
49
|
|
|
@@ -65,8 +58,6 @@ export function transformRouter(
|
|
|
65
58
|
|
|
66
59
|
if (!changed) return null;
|
|
67
60
|
|
|
68
|
-
// Prepend the static import as the first line. MagicString tracks the
|
|
69
|
-
// offset so all downstream source maps remain correct.
|
|
70
61
|
s.prepend(
|
|
71
62
|
`import { NamedRoutes as ${routeNamesVar} } from "${routeNamesImport}";\n`,
|
|
72
63
|
);
|
|
@@ -96,10 +87,6 @@ export function exposeRouterId(): Plugin {
|
|
|
96
87
|
},
|
|
97
88
|
transform(code, id) {
|
|
98
89
|
if (!code.includes("createRouter")) return null;
|
|
99
|
-
// Accepts both @rangojs/router and @rangojs/router/server subpath.
|
|
100
|
-
// NOTE: detectImports in expose-id-utils has a stricter check that
|
|
101
|
-
// excludes /server for its router flag -- that's intentional since
|
|
102
|
-
// detectImports is only used in exposeInternalIds, not here.
|
|
103
90
|
if (
|
|
104
91
|
!/import\s*\{[^}]*\bcreateRouter\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
|
|
105
92
|
code,
|
|
@@ -184,6 +184,16 @@ ${lazyImports.join(",\n")}
|
|
|
184
184
|
async buildStart() {
|
|
185
185
|
if (!isBuild) return;
|
|
186
186
|
|
|
187
|
+
// The loader pre-scan walks and reads the entire project, but the
|
|
188
|
+
// loaderRegistry it populates is consumed only by the RSC loader-manifest
|
|
189
|
+
// virtual module (and the transform hook already gates its registry writes
|
|
190
|
+
// with isRscEnv). plugin-rsc runs ~5 build passes (rsc-scan, ssr-scan, rsc,
|
|
191
|
+
// client, ssr) over this single shared plugin instance; without this gate
|
|
192
|
+
// the full-tree I/O ran on every pass with no consumer on the non-RSC
|
|
193
|
+
// ones. Skip only when the environment is known and not RSC, so an
|
|
194
|
+
// unavailable environment still falls through (no empty registry).
|
|
195
|
+
if (this.environment && this.environment.name !== "rsc") return;
|
|
196
|
+
|
|
187
197
|
const fs = await import("node:fs/promises");
|
|
188
198
|
|
|
189
199
|
const SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage"]);
|
|
@@ -54,9 +54,6 @@ export function performanceTracksOptimizeDepsPlugin(): {
|
|
|
54
54
|
/react-server-dom-webpack-client\.browser\.(development|production)\.js$/;
|
|
55
55
|
return {
|
|
56
56
|
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
57
|
-
// Vite 8 optimizes deps with Rolldown (Rollup-style plugin pipeline), so the
|
|
58
|
-
// pre-bundled RSDW client is patched via load() rather than esbuild's onLoad.
|
|
59
|
-
// Returning code overrides Rolldown's default filesystem read for the module.
|
|
60
57
|
async load(id: string): Promise<{ code: string } | null> {
|
|
61
58
|
const cleanId = id.split("?")[0] ?? id;
|
|
62
59
|
if (!RSDW_CLIENT_RE.test(cleanId)) return null;
|
|
@@ -30,11 +30,6 @@ const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
|
30
30
|
// and should not be wrapped (children can't be cache-keyed).
|
|
31
31
|
const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* Grammar for a valid function-level directive: `use cache` optionally followed
|
|
35
|
-
* by `: <profile-name>`. The single source of truth for both the transform and
|
|
36
|
-
* the near-miss validator below.
|
|
37
|
-
*/
|
|
38
33
|
export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
|
|
39
34
|
|
|
40
35
|
export function useCacheTransform(): Plugin {
|
|
@@ -72,7 +67,6 @@ export function useCacheTransform(): Plugin {
|
|
|
72
67
|
|
|
73
68
|
const start = counter ? performance.now() : 0;
|
|
74
69
|
try {
|
|
75
|
-
// Lazy-load transform helpers
|
|
76
70
|
if (!rscTransforms) {
|
|
77
71
|
try {
|
|
78
72
|
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
@@ -87,7 +81,6 @@ export function useCacheTransform(): Plugin {
|
|
|
87
81
|
transformHoistInlineDirective,
|
|
88
82
|
} = rscTransforms;
|
|
89
83
|
|
|
90
|
-
// Parse AST
|
|
91
84
|
let ast: any;
|
|
92
85
|
try {
|
|
93
86
|
const { parseAst } = await import("vite");
|
|
@@ -99,7 +92,6 @@ export function useCacheTransform(): Plugin {
|
|
|
99
92
|
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
100
93
|
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
101
94
|
|
|
102
|
-
// Check for file-level "use cache"
|
|
103
95
|
if (hasDirective(ast.body, "use cache")) {
|
|
104
96
|
return transformFileLevelUseCache(
|
|
105
97
|
code,
|
|
@@ -112,8 +104,6 @@ export function useCacheTransform(): Plugin {
|
|
|
112
104
|
);
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
// Check for function-level "use cache" / "use cache: profileName"
|
|
116
|
-
// (only if there's no file-level directive but code still contains the string)
|
|
117
107
|
const functionResult = transformFunctionLevelUseCache(
|
|
118
108
|
code,
|
|
119
109
|
ast,
|
|
@@ -123,9 +113,6 @@ export function useCacheTransform(): Plugin {
|
|
|
123
113
|
transformHoistInlineDirective,
|
|
124
114
|
);
|
|
125
115
|
|
|
126
|
-
// Check for near-miss directives on the function-level path. The
|
|
127
|
-
// file-level branch above returns earlier (it wraps every export
|
|
128
|
-
// regardless), so this runs only when there is no file-level directive.
|
|
129
116
|
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
130
117
|
|
|
131
118
|
if (functionResult) return functionResult;
|
|
@@ -145,8 +132,7 @@ function transformFileLevelUseCache(
|
|
|
145
132
|
isLayoutOrTemplate: boolean,
|
|
146
133
|
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
147
134
|
) {
|
|
148
|
-
|
|
149
|
-
const nonFunctionExports: string[] = [];
|
|
135
|
+
const unconfirmedExports: string[] = [];
|
|
150
136
|
|
|
151
137
|
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
152
138
|
runtime: (value: string, name: string) => {
|
|
@@ -155,30 +141,38 @@ function transformFileLevelUseCache(
|
|
|
155
141
|
},
|
|
156
142
|
rejectNonAsyncFunction: false,
|
|
157
143
|
filter: (name: string, meta: { isFunction?: boolean }) => {
|
|
158
|
-
// Skip default export of layout/template files (they receive children)
|
|
159
144
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
145
|
+
// isFunction is boolean | undefined: true = confirmed function, false =
|
|
146
|
+
// confirmed non-function, undefined = cannot tell statically (e.g. a
|
|
147
|
+
// factory/HOF initializer `const x = makeCached(fn)`). Deliberate policy:
|
|
148
|
+
// require a confirmed function and reject everything else, including
|
|
149
|
+
// indeterminate initializers that may be functions at runtime -- rewrite
|
|
150
|
+
// those as direct async functions. (Pre-#1246 plugin-rsc reported false,
|
|
151
|
+
// not undefined, here, so === false would wrongly wrap them post-bump.)
|
|
152
|
+
if (meta.isFunction !== true) {
|
|
153
|
+
unconfirmedExports.push(name);
|
|
163
154
|
return false;
|
|
164
155
|
}
|
|
165
156
|
return true;
|
|
166
157
|
},
|
|
167
158
|
});
|
|
168
159
|
|
|
169
|
-
if (
|
|
160
|
+
if (unconfirmedExports.length > 0) {
|
|
161
|
+
const plural = unconfirmedExports.length > 1;
|
|
170
162
|
throw new Error(
|
|
171
|
-
`[rango:use-cache] File-level "use cache" in ${sourceId}
|
|
172
|
-
`
|
|
173
|
-
`${
|
|
174
|
-
|
|
175
|
-
`
|
|
176
|
-
`
|
|
163
|
+
`[rango:use-cache] File-level "use cache" in ${sourceId} only wraps ` +
|
|
164
|
+
`exports that are statically-confirmed functions. ` +
|
|
165
|
+
`${plural ? "These exports are" : "This export is"} not: ` +
|
|
166
|
+
`${unconfirmedExports.map((n) => `"${n}"`).join(", ")}. ` +
|
|
167
|
+
`Declare them directly (export async function foo() {} or ` +
|
|
168
|
+
`export const foo = async () => {}). A factory or otherwise ` +
|
|
169
|
+
`statically-indeterminate initializer (export const foo = makeCached(fn)) ` +
|
|
170
|
+
`is rejected even if it returns a function at runtime -- rewrite it as a ` +
|
|
171
|
+
`direct async function, or move non-function exports to a separate module.`,
|
|
177
172
|
);
|
|
178
173
|
}
|
|
179
174
|
|
|
180
175
|
if (exportNames.length === 0) {
|
|
181
|
-
// Even if no exports were wrapped, strip the directive
|
|
182
176
|
const s = new MagicString(code);
|
|
183
177
|
const directive = findFileLevelDirective(ast);
|
|
184
178
|
if (directive) {
|
|
@@ -195,12 +189,10 @@ function transformFileLevelUseCache(
|
|
|
195
189
|
return;
|
|
196
190
|
}
|
|
197
191
|
|
|
198
|
-
// Prepend the import
|
|
199
192
|
output.prepend(
|
|
200
193
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
201
194
|
);
|
|
202
195
|
|
|
203
|
-
// Replace the directive with a comment
|
|
204
196
|
const directive = findFileLevelDirective(ast);
|
|
205
197
|
if (directive) {
|
|
206
198
|
output.overwrite(
|
|
@@ -244,9 +236,6 @@ function transformFunctionLevelUseCache(
|
|
|
244
236
|
|
|
245
237
|
if (names.length === 0) return;
|
|
246
238
|
|
|
247
|
-
// Use a top-level import instead of await import() — the hoisted wrapper
|
|
248
|
-
// may be placed in a non-async context (e.g., inside a synchronous
|
|
249
|
-
// urls() callback) where await is not allowed.
|
|
250
239
|
output.prepend(
|
|
251
240
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
252
241
|
);
|
|
@@ -261,9 +250,6 @@ function transformFunctionLevelUseCache(
|
|
|
261
250
|
}
|
|
262
251
|
}
|
|
263
252
|
|
|
264
|
-
/**
|
|
265
|
-
* Find the file-level "use cache" directive AST node for removal.
|
|
266
|
-
*/
|
|
267
253
|
function findFileLevelDirective(
|
|
268
254
|
ast: any,
|
|
269
255
|
): { start: number; end: number } | null {
|
|
@@ -280,18 +266,8 @@ function findFileLevelDirective(
|
|
|
280
266
|
return null;
|
|
281
267
|
}
|
|
282
268
|
|
|
283
|
-
/**
|
|
284
|
-
* Regex for near-miss: starts with "use cache:" but has invalid tokens.
|
|
285
|
-
*/
|
|
286
269
|
const NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
287
270
|
|
|
288
|
-
/**
|
|
289
|
-
* Walk the AST looking for string literals that look like malformed
|
|
290
|
-
* "use cache" directives and emit a Vite warning for each.
|
|
291
|
-
*
|
|
292
|
-
* This catches cases like `"use cache: bad.name"` or `"use cache: "`
|
|
293
|
-
* that the transform regex silently ignores.
|
|
294
|
-
*/
|
|
295
271
|
function warnOnNearMissDirectives(
|
|
296
272
|
ast: any,
|
|
297
273
|
fileId: string,
|
|
@@ -319,7 +295,6 @@ function warnOnNearMissDirectives(
|
|
|
319
295
|
}
|
|
320
296
|
}
|
|
321
297
|
|
|
322
|
-
// Walk into function bodies where directives appear
|
|
323
298
|
for (const key of Object.keys(node)) {
|
|
324
299
|
const child = node[key];
|
|
325
300
|
if (Array.isArray(child)) {
|
|
@@ -29,7 +29,6 @@ export function createVersionInjectorPlugin(
|
|
|
29
29
|
|
|
30
30
|
transform(code, id) {
|
|
31
31
|
if (!resolvedEntryPath) return null;
|
|
32
|
-
// Only transform the RSC entry file
|
|
33
32
|
const normalizedId = Vite.normalizePath(id);
|
|
34
33
|
const normalizedEntry = Vite.normalizePath(resolvedEntryPath);
|
|
35
34
|
|
|
@@ -37,25 +36,10 @@ export function createVersionInjectorPlugin(
|
|
|
37
36
|
return null;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
// Always prepend `import "virtual:rsc-router/routes-manifest"` as the
|
|
41
|
-
// first side-effect import. The manifest virtual module's `load()` hook
|
|
42
|
-
// awaits `s.discoveryDone` so that, by the time the rest of the entry
|
|
43
|
-
// including any module-level `router.reverse()` calls under `./router.js`
|
|
44
|
-
// evaluates, runtime discovery has rewritten `router.named-routes.gen.ts`
|
|
45
|
-
// with the full route table.
|
|
46
|
-
//
|
|
47
|
-
// ES module evaluation order matters here: while imports are *parsed*
|
|
48
|
-
// hoisted, side-effect imports are evaluated in source order in the
|
|
49
|
-
// dependency graph. A user-authored `import "virtual:rsc-router/..."`
|
|
50
|
-
// placed after `import "./router.js"` runs too late: the manifest
|
|
51
|
-
// gate fires after router.tsx has already crashed on a stale gen file.
|
|
52
|
-
// We always prepend; ESM dedups any user-written duplicate, so module
|
|
53
|
-
// initialization still runs once.
|
|
54
39
|
const prepend: string[] = [
|
|
55
40
|
`import "virtual:rsc-router/routes-manifest";`,
|
|
56
41
|
];
|
|
57
42
|
|
|
58
|
-
// Auto-inject VERSION if file uses createRSCHandler without version
|
|
59
43
|
let newCode = code;
|
|
60
44
|
const needsVersion =
|
|
61
45
|
code.includes("createRSCHandler") &&
|
|
@@ -70,10 +54,6 @@ export function createVersionInjectorPlugin(
|
|
|
70
54
|
);
|
|
71
55
|
}
|
|
72
56
|
|
|
73
|
-
// Insert after any leading `/// <reference ... />` triple-slash
|
|
74
|
-
// directives (and surrounding blank lines). TypeScript requires those
|
|
75
|
-
// directives to precede all other code; putting our imports above
|
|
76
|
-
// them silently demotes the directives to plain comments.
|
|
77
57
|
const lines = newCode.split("\n");
|
|
78
58
|
let insertAt = 0;
|
|
79
59
|
while (insertAt < lines.length) {
|
|
@@ -138,8 +138,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
138
138
|
|
|
139
139
|
let versionCounter = 0;
|
|
140
140
|
const bumpVersion = (reason: string) => {
|
|
141
|
-
// Use timestamp + counter to guarantee uniqueness even when multiple
|
|
142
|
-
// bumps happen within the same millisecond (e.g. cascading HMR events).
|
|
143
141
|
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
144
142
|
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
145
143
|
|
|
@@ -158,9 +156,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
158
156
|
|
|
159
157
|
configResolved(config) {
|
|
160
158
|
isDev = config.command === "serve";
|
|
161
|
-
// Capture the resolved cacheDir so we can ignore optimizer-output
|
|
162
|
-
// writes inside it. Vite resolves cacheDir against the project root,
|
|
163
|
-
// so this is a stable absolute path for the lifetime of the server.
|
|
164
159
|
resolvedCacheDir = config.cacheDir
|
|
165
160
|
? String(config.cacheDir).replace(/\\/g, "/")
|
|
166
161
|
: undefined;
|
|
@@ -210,27 +205,15 @@ export function createVersionPlugin(): Plugin {
|
|
|
210
205
|
return null;
|
|
211
206
|
},
|
|
212
207
|
|
|
213
|
-
// Track RSC module changes and update version
|
|
214
208
|
async hotUpdate(ctx) {
|
|
215
209
|
if (!isDev) return;
|
|
216
210
|
|
|
217
|
-
// Check if this is an RSC environment update (not client/ssr)
|
|
218
|
-
// RSC modules affect server-rendered content and cached payloads
|
|
219
|
-
// In Vite 6, environment is accessed via `this.environment`
|
|
220
211
|
const isRscModule = this.environment?.name === "rsc";
|
|
221
212
|
|
|
222
213
|
if (!isRscModule) return;
|
|
223
214
|
|
|
224
|
-
// Skip Vite's own pre-bundled dep cache writes. The optimizer rewrites
|
|
225
|
-
// files inside the configured `cacheDir` on every discovery cycle
|
|
226
|
-
// (and when other dev servers under the same cwd populate their own
|
|
227
|
-
// isolated cache dirs). These are not user-source changes, so bumping
|
|
228
|
-
// the app version on them produces spurious version mismatches that
|
|
229
|
-
// surface as forced reloads on in-flight actions.
|
|
230
215
|
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
231
216
|
|
|
232
|
-
// Skip re-bumping when the version virtual module itself is invalidated
|
|
233
|
-
// (our own bumpVersion() invalidates it, which re-triggers hotUpdate).
|
|
234
217
|
if (
|
|
235
218
|
ctx.modules.length === 1 &&
|
|
236
219
|
ctx.modules[0].id === "\0" + VIRTUAL_IDS.version
|
|
@@ -245,9 +228,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
245
228
|
const source = await ctx.read();
|
|
246
229
|
const nextSignature = getClientModuleSignature(source);
|
|
247
230
|
if (nextSignature) {
|
|
248
|
-
// "use client" file — compare export signatures.
|
|
249
|
-
// client-component-hmr may have cleared ctx.modules, so we
|
|
250
|
-
// cannot rely on ctx.modules.length for these files.
|
|
251
231
|
clientModuleSignatures.set(filePath, nextSignature);
|
|
252
232
|
if (
|
|
253
233
|
previousSignature &&
|
|
@@ -258,20 +238,11 @@ export function createVersionPlugin(): Plugin {
|
|
|
258
238
|
} else {
|
|
259
239
|
clientModuleSignatures.delete(filePath);
|
|
260
240
|
if (!previousSignature) {
|
|
261
|
-
// Not and never was "use client" — use module graph check.
|
|
262
|
-
// ctx.modules is reliable for pure server files (only
|
|
263
|
-
// client-component-hmr clears it for "use client" modules).
|
|
264
241
|
if (ctx.modules.length === 0) return;
|
|
265
242
|
}
|
|
266
|
-
// Was "use client" but directive removed — boundary changed,
|
|
267
|
-
// bump below.
|
|
268
243
|
}
|
|
269
|
-
} catch {
|
|
270
|
-
// Fail open: if we can't read or parse the update, invalidate.
|
|
271
|
-
}
|
|
244
|
+
} catch {}
|
|
272
245
|
} else {
|
|
273
|
-
// Non-code file (json, css, etc.) — only bump if it's actually
|
|
274
|
-
// referenced by the RSC module graph.
|
|
275
246
|
if (ctx.modules.length === 0) return;
|
|
276
247
|
}
|
|
277
248
|
|
|
@@ -280,20 +251,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
280
251
|
};
|
|
281
252
|
}
|
|
282
253
|
|
|
283
|
-
/**
|
|
284
|
-
* Match Vite's pre-bundled dep cache directories. These paths are rewritten
|
|
285
|
-
* by the dep optimizer (and by isolated test fixtures sharing the same cwd),
|
|
286
|
-
* not by user source changes, so they should not bump the app version (which
|
|
287
|
-
* would force a client reload mid-request).
|
|
288
|
-
*
|
|
289
|
-
* Two checks:
|
|
290
|
-
* 1. Anything inside the resolved `cacheDir` (precise — covers custom paths
|
|
291
|
-
* like the `RANGO_E2E_VITE_CACHE_DIR` overrides in the test fixtures).
|
|
292
|
-
* 2. Heuristic match for any `node_modules/.vite*` directory or a
|
|
293
|
-
* `.vite-isolated/` segment anywhere in the path. This catches the
|
|
294
|
-
* *other* dev servers in the same cwd whose cacheDir we cannot read
|
|
295
|
-
* (we only see config of the server we're attached to).
|
|
296
|
-
*/
|
|
297
254
|
export function isViteDepCachePath(
|
|
298
255
|
filePath: string | undefined,
|
|
299
256
|
cacheDir?: string,
|
|
@@ -311,11 +268,6 @@ export function isViteDepCachePath(
|
|
|
311
268
|
}
|
|
312
269
|
}
|
|
313
270
|
|
|
314
|
-
// Vite/optimizer convention: cache dirs always sit directly under
|
|
315
|
-
// `node_modules/` and start with `.vite` (e.g. `.vite`, `.vite-temp`,
|
|
316
|
-
// `.vite_rango_generate`, `.vite-e2e-test-app`). The `/.vite-isolated/`
|
|
317
|
-
// segment covers the test-fixture pattern that places the cache outside
|
|
318
|
-
// node_modules.
|
|
319
271
|
return (
|
|
320
272
|
/\/node_modules\/\.vite[^/]*\//.test(normalized) ||
|
|
321
273
|
normalized.includes("/.vite-isolated/")
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default virtual entry file contents for rsc-router.
|
|
3
|
-
* These are used when users don't provide their own entry files.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export const VIRTUAL_ENTRY_BROWSER: string = `
|
|
7
2
|
import {
|
|
8
3
|
createFromReadableStream,
|
|
@@ -51,9 +46,6 @@ export const renderHTML = createSSRHandler({
|
|
|
51
46
|
});
|
|
52
47
|
`.trim();
|
|
53
48
|
|
|
54
|
-
/**
|
|
55
|
-
* Generate the RSC entry content with the specified router path
|
|
56
|
-
*/
|
|
57
49
|
export function getVirtualEntryRSC(routerPath: string): string {
|
|
58
50
|
return `
|
|
59
51
|
import {
|
|
@@ -104,9 +96,6 @@ export default function handler(request, env) {
|
|
|
104
96
|
`.trim();
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
/**
|
|
108
|
-
* Virtual module IDs
|
|
109
|
-
*/
|
|
110
99
|
export const VIRTUAL_IDS = {
|
|
111
100
|
browser: "virtual:rsc-router/entry.browser.js",
|
|
112
101
|
ssr: "virtual:rsc-router/entry.ssr.js",
|
|
@@ -114,10 +103,6 @@ export const VIRTUAL_IDS = {
|
|
|
114
103
|
version: "@rangojs/router:version",
|
|
115
104
|
} as const;
|
|
116
105
|
|
|
117
|
-
/**
|
|
118
|
-
* Virtual module content for version.
|
|
119
|
-
* Exports VERSION - a timestamp that changes on server restart (dev) or at build time (production).
|
|
120
|
-
*/
|
|
121
106
|
export function getVirtualVersionContent(version: string): string {
|
|
122
107
|
return `export const VERSION = ${JSON.stringify(version)};`;
|
|
123
108
|
}
|