@rangojs/router 0.0.0-experimental.0f44aca1
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/AGENTS.md +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -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 +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- 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 +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- 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 +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -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 +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +365 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "use cache" Vite Transform Plugin
|
|
3
|
+
*
|
|
4
|
+
* Detects "use cache" directives at file-level and function-level,
|
|
5
|
+
* then wraps exports with registerCachedFunction() from the cache runtime.
|
|
6
|
+
*
|
|
7
|
+
* File-level: "use cache" at top of file wraps all exports (except
|
|
8
|
+
* layout/template default exports which receive children).
|
|
9
|
+
*
|
|
10
|
+
* Function-level: "use cache: profileName" inside a function body
|
|
11
|
+
* hoists the function and wraps it.
|
|
12
|
+
*
|
|
13
|
+
* Uses transform helpers from @vitejs/plugin-rsc/transforms:
|
|
14
|
+
* - hasDirective() for file-level detection
|
|
15
|
+
* - transformWrapExport() for file-level wrapping
|
|
16
|
+
* - transformHoistInlineDirective() for function-level hoisting
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { Plugin } from "vite";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import MagicString from "magic-string";
|
|
22
|
+
import { normalizePath, hashId } from "./expose-id-utils.js";
|
|
23
|
+
|
|
24
|
+
const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
25
|
+
|
|
26
|
+
// Files whose default export receives {children} from the framework
|
|
27
|
+
// and should not be wrapped (children can't be cache-keyed).
|
|
28
|
+
const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
29
|
+
|
|
30
|
+
export function useCacheTransform(): Plugin {
|
|
31
|
+
let projectRoot = "";
|
|
32
|
+
let isBuild = false;
|
|
33
|
+
let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null =
|
|
34
|
+
null;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
name: "@rangojs/router:use-cache",
|
|
38
|
+
enforce: "post",
|
|
39
|
+
|
|
40
|
+
configResolved(config) {
|
|
41
|
+
projectRoot = config.root;
|
|
42
|
+
isBuild = config.command === "build";
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async transform(code, id) {
|
|
46
|
+
// Only process in RSC environment
|
|
47
|
+
if (this.environment?.name !== "rsc") return;
|
|
48
|
+
|
|
49
|
+
// Quick bail: no "use cache" in source
|
|
50
|
+
if (!code.includes("use cache")) return;
|
|
51
|
+
|
|
52
|
+
// Skip node_modules and virtual modules
|
|
53
|
+
if (id.includes("/node_modules/") || id.startsWith("\0")) return;
|
|
54
|
+
|
|
55
|
+
// Only JS/TS files
|
|
56
|
+
if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
|
|
57
|
+
|
|
58
|
+
// Lazy-load transform helpers
|
|
59
|
+
if (!rscTransforms) {
|
|
60
|
+
try {
|
|
61
|
+
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
62
|
+
} catch {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const {
|
|
68
|
+
hasDirective,
|
|
69
|
+
transformWrapExport,
|
|
70
|
+
transformHoistInlineDirective,
|
|
71
|
+
} = rscTransforms;
|
|
72
|
+
|
|
73
|
+
// Parse AST
|
|
74
|
+
let ast: any;
|
|
75
|
+
try {
|
|
76
|
+
const { parseAst } = await import("vite");
|
|
77
|
+
ast = parseAst(code);
|
|
78
|
+
} catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
83
|
+
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
84
|
+
|
|
85
|
+
// Check for file-level "use cache"
|
|
86
|
+
if (hasDirective(ast.body, "use cache")) {
|
|
87
|
+
return transformFileLevelUseCache(
|
|
88
|
+
code,
|
|
89
|
+
ast,
|
|
90
|
+
filePath,
|
|
91
|
+
id,
|
|
92
|
+
isBuild,
|
|
93
|
+
isLayoutOrTemplate,
|
|
94
|
+
transformWrapExport,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check for function-level "use cache" / "use cache: profileName"
|
|
99
|
+
// (only if there's no file-level directive but code still contains the string)
|
|
100
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
101
|
+
code,
|
|
102
|
+
ast,
|
|
103
|
+
filePath,
|
|
104
|
+
id,
|
|
105
|
+
isBuild,
|
|
106
|
+
transformHoistInlineDirective,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Always check for near-miss directives, even when valid directives
|
|
110
|
+
// exist. A file may contain both valid and invalid "use cache" directives
|
|
111
|
+
// in different functions — the invalid ones should still warn.
|
|
112
|
+
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
113
|
+
|
|
114
|
+
if (functionResult) return functionResult;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function transformFileLevelUseCache(
|
|
120
|
+
code: string,
|
|
121
|
+
ast: any,
|
|
122
|
+
filePath: string,
|
|
123
|
+
sourceId: string,
|
|
124
|
+
isBuild: boolean,
|
|
125
|
+
isLayoutOrTemplate: boolean,
|
|
126
|
+
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
127
|
+
) {
|
|
128
|
+
// Collect non-function exports to report after wrapping
|
|
129
|
+
const nonFunctionExports: string[] = [];
|
|
130
|
+
|
|
131
|
+
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
132
|
+
runtime: (value: string, name: string) => {
|
|
133
|
+
const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
134
|
+
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, "default")`;
|
|
135
|
+
},
|
|
136
|
+
rejectNonAsyncFunction: false,
|
|
137
|
+
filter: (name: string, meta: { isFunction?: boolean }) => {
|
|
138
|
+
// Skip default export of layout/template files (they receive children)
|
|
139
|
+
if (name === "default" && isLayoutOrTemplate) return false;
|
|
140
|
+
// Non-function exports cannot be wrapped with registerCachedFunction
|
|
141
|
+
if (meta.isFunction === false) {
|
|
142
|
+
nonFunctionExports.push(name);
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (nonFunctionExports.length > 0) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`[rango:use-cache] File-level "use cache" in ${sourceId} cannot wrap ` +
|
|
152
|
+
`non-function export${nonFunctionExports.length > 1 ? "s" : ""}: ` +
|
|
153
|
+
`${nonFunctionExports.map((n) => `"${n}"`).join(", ")}. ` +
|
|
154
|
+
`Only function exports can be cached. Either remove "use cache" from ` +
|
|
155
|
+
`the file level and add it inside individual functions, or move the ` +
|
|
156
|
+
`non-function exports to a separate module.`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (exportNames.length === 0) {
|
|
161
|
+
// Even if no exports were wrapped, strip the directive
|
|
162
|
+
const s = new MagicString(code);
|
|
163
|
+
const directive = findFileLevelDirective(ast);
|
|
164
|
+
if (directive) {
|
|
165
|
+
s.overwrite(
|
|
166
|
+
directive.start,
|
|
167
|
+
directive.end,
|
|
168
|
+
`/* "use cache" -- wrapped by rango */`,
|
|
169
|
+
);
|
|
170
|
+
return {
|
|
171
|
+
code: s.toString(),
|
|
172
|
+
map: s.generateMap({ source: sourceId, hires: "boundary" }),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Prepend the import
|
|
179
|
+
output.prepend(
|
|
180
|
+
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Replace the directive with a comment
|
|
184
|
+
const directive = findFileLevelDirective(ast);
|
|
185
|
+
if (directive) {
|
|
186
|
+
output.overwrite(
|
|
187
|
+
directive.start,
|
|
188
|
+
directive.end,
|
|
189
|
+
`/* "use cache" -- wrapped by rango */`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
code: output.toString(),
|
|
195
|
+
map: output.generateMap({ source: sourceId, hires: "boundary" }),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function transformFunctionLevelUseCache(
|
|
200
|
+
code: string,
|
|
201
|
+
ast: any,
|
|
202
|
+
filePath: string,
|
|
203
|
+
sourceId: string,
|
|
204
|
+
isBuild: boolean,
|
|
205
|
+
transformHoistInlineDirective: (typeof import("@vitejs/plugin-rsc/transforms"))["transformHoistInlineDirective"],
|
|
206
|
+
) {
|
|
207
|
+
try {
|
|
208
|
+
const { output, names } = transformHoistInlineDirective(code, ast, {
|
|
209
|
+
directive: /^use cache(:\s*[\w-]+)?$/,
|
|
210
|
+
runtime: (
|
|
211
|
+
value: string,
|
|
212
|
+
name: string,
|
|
213
|
+
meta: { directiveMatch: RegExpMatchArray },
|
|
214
|
+
) => {
|
|
215
|
+
const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
216
|
+
const profileMatch = meta.directiveMatch[1];
|
|
217
|
+
const profileName = profileMatch
|
|
218
|
+
? profileMatch.replace(/^:\s*/, "").trim()
|
|
219
|
+
: "default";
|
|
220
|
+
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, ${JSON.stringify(profileName)})`;
|
|
221
|
+
},
|
|
222
|
+
rejectNonAsyncFunction: false,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (names.length === 0) return;
|
|
226
|
+
|
|
227
|
+
// Use a top-level import instead of await import() — the hoisted wrapper
|
|
228
|
+
// may be placed in a non-async context (e.g., inside a synchronous
|
|
229
|
+
// urls() callback) where await is not allowed.
|
|
230
|
+
output.prepend(
|
|
231
|
+
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
code: output.toString(),
|
|
236
|
+
map: output.generateMap({ source: sourceId, hires: "boundary" }),
|
|
237
|
+
};
|
|
238
|
+
} catch {
|
|
239
|
+
// Transform failed (e.g., syntax not supported), skip
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Find the file-level "use cache" directive AST node for removal.
|
|
246
|
+
*/
|
|
247
|
+
function findFileLevelDirective(
|
|
248
|
+
ast: any,
|
|
249
|
+
): { start: number; end: number } | null {
|
|
250
|
+
for (const node of ast.body ?? []) {
|
|
251
|
+
if (
|
|
252
|
+
node.type === "ExpressionStatement" &&
|
|
253
|
+
node.expression?.type === "Literal" &&
|
|
254
|
+
typeof node.expression.value === "string" &&
|
|
255
|
+
node.expression.value.startsWith("use cache")
|
|
256
|
+
) {
|
|
257
|
+
return { start: node.start, end: node.end };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The valid directive regex (must stay in sync with transformFunctionLevelUseCache).
|
|
265
|
+
*/
|
|
266
|
+
const VALID_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Regex for near-miss: starts with "use cache:" but has invalid tokens.
|
|
270
|
+
*/
|
|
271
|
+
const NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Walk the AST looking for string literals that look like malformed
|
|
275
|
+
* "use cache" directives and emit a Vite warning for each.
|
|
276
|
+
*
|
|
277
|
+
* This catches cases like `"use cache: bad.name"` or `"use cache: "`
|
|
278
|
+
* that the transform regex silently ignores.
|
|
279
|
+
*/
|
|
280
|
+
function warnOnNearMissDirectives(
|
|
281
|
+
ast: any,
|
|
282
|
+
fileId: string,
|
|
283
|
+
warn: (message: string) => void,
|
|
284
|
+
): void {
|
|
285
|
+
const visit = (node: any) => {
|
|
286
|
+
if (!node || typeof node !== "object") return;
|
|
287
|
+
|
|
288
|
+
if (
|
|
289
|
+
node.type === "ExpressionStatement" &&
|
|
290
|
+
node.expression?.type === "Literal" &&
|
|
291
|
+
typeof node.expression.value === "string"
|
|
292
|
+
) {
|
|
293
|
+
const value = node.expression.value;
|
|
294
|
+
if (
|
|
295
|
+
value.startsWith("use cache") &&
|
|
296
|
+
NEAR_MISS_RE.test(value) &&
|
|
297
|
+
!VALID_DIRECTIVE_RE.test(value)
|
|
298
|
+
) {
|
|
299
|
+
const profilePart = value.slice("use cache:".length).trim();
|
|
300
|
+
warn(
|
|
301
|
+
`[rango:use-cache] "${value}" in ${fileId} has an invalid profile name "${profilePart}". ` +
|
|
302
|
+
`Profile names must match [a-zA-Z0-9_-]+. This directive will be ignored.`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Walk into function bodies where directives appear
|
|
308
|
+
for (const key of Object.keys(node)) {
|
|
309
|
+
const child = node[key];
|
|
310
|
+
if (Array.isArray(child)) {
|
|
311
|
+
for (const item of child) {
|
|
312
|
+
visit(item);
|
|
313
|
+
}
|
|
314
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
315
|
+
visit(child);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
for (const node of ast.body ?? []) {
|
|
321
|
+
visit(node);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import * as Vite from "vite";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Plugin that auto-injects VERSION and routes-manifest into custom entry.rsc files.
|
|
7
|
+
* If a custom entry.rsc file uses createRSCHandler but doesn't pass version,
|
|
8
|
+
* this transform adds the import and property automatically.
|
|
9
|
+
* Also ensures the routes-manifest virtual module is always imported.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export function createVersionInjectorPlugin(
|
|
13
|
+
rscEntryPath: string | undefined,
|
|
14
|
+
): Plugin {
|
|
15
|
+
let resolvedEntryPath = "";
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
name: "@rangojs/router:version-injector",
|
|
19
|
+
enforce: "pre",
|
|
20
|
+
|
|
21
|
+
configResolved(config) {
|
|
22
|
+
let entryPath = rscEntryPath;
|
|
23
|
+
// Cloudflare preset: read entry from resolved environment config.
|
|
24
|
+
// The @cloudflare/vite-plugin reads wrangler config (toml/json/jsonc)
|
|
25
|
+
// and sets optimizeDeps.entries on the RSC environment.
|
|
26
|
+
if (!entryPath) {
|
|
27
|
+
const rscEnvConfig = (config.environments as any)?.["rsc"];
|
|
28
|
+
const entries = rscEnvConfig?.optimizeDeps?.entries;
|
|
29
|
+
if (typeof entries === "string") {
|
|
30
|
+
entryPath = entries;
|
|
31
|
+
} else if (Array.isArray(entries) && entries.length > 0) {
|
|
32
|
+
entryPath = entries[0];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (entryPath) {
|
|
36
|
+
resolvedEntryPath = resolve(config.root, entryPath);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
transform(code, id) {
|
|
41
|
+
if (!resolvedEntryPath) return null;
|
|
42
|
+
// Only transform the RSC entry file
|
|
43
|
+
const normalizedId = Vite.normalizePath(id);
|
|
44
|
+
const normalizedEntry = Vite.normalizePath(resolvedEntryPath);
|
|
45
|
+
|
|
46
|
+
if (normalizedId !== normalizedEntry) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Prepend imports at the top of the file. ES imports are hoisted
|
|
51
|
+
// by the module system, so source position is irrelevant.
|
|
52
|
+
const prepend: string[] = [];
|
|
53
|
+
let newCode = code;
|
|
54
|
+
|
|
55
|
+
if (!code.includes("virtual:rsc-router/routes-manifest")) {
|
|
56
|
+
prepend.push(`import "virtual:rsc-router/routes-manifest";`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Auto-inject VERSION if file uses createRSCHandler without version
|
|
60
|
+
const needsVersion =
|
|
61
|
+
code.includes("createRSCHandler") &&
|
|
62
|
+
!code.includes("@rangojs/router:version") &&
|
|
63
|
+
/createRSCHandler\s*\(\s*\{/.test(code);
|
|
64
|
+
|
|
65
|
+
if (needsVersion) {
|
|
66
|
+
prepend.push(`import { VERSION } from "@rangojs/router:version";`);
|
|
67
|
+
newCode = newCode.replace(
|
|
68
|
+
/createRSCHandler\s*\(\s*\{/,
|
|
69
|
+
"createRSCHandler({\n version: VERSION,",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (prepend.length === 0 && newCode === code) return null;
|
|
74
|
+
|
|
75
|
+
newCode = prepend.join("\n") + (prepend.length > 0 ? "\n" : "") + newCode;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
code: newCode,
|
|
79
|
+
map: null,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { parseAst, type Plugin } from "vite";
|
|
2
|
+
import { VIRTUAL_IDS, getVirtualVersionContent } from "./virtual-entries.js";
|
|
3
|
+
|
|
4
|
+
interface ClientModuleSignature {
|
|
5
|
+
key: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isCodeModule(id: string): boolean {
|
|
9
|
+
return /\.(tsx?|jsx?)($|\?)/.test(id);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeModuleId(id: string): string {
|
|
13
|
+
return id.split("?", 1)[0];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getClientModuleSignature(
|
|
17
|
+
source: string,
|
|
18
|
+
): ClientModuleSignature | undefined {
|
|
19
|
+
let program: any;
|
|
20
|
+
try {
|
|
21
|
+
program = parseAst(source, { jsx: true });
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let isUseClient = false;
|
|
27
|
+
for (const node of program.body ?? []) {
|
|
28
|
+
if (
|
|
29
|
+
node?.type === "ExpressionStatement" &&
|
|
30
|
+
node.expression?.type === "Literal" &&
|
|
31
|
+
typeof node.expression.value === "string"
|
|
32
|
+
) {
|
|
33
|
+
if (node.expression.value === "use client") {
|
|
34
|
+
isUseClient = true;
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isUseClient) return undefined;
|
|
42
|
+
|
|
43
|
+
const exports = new Set<string>();
|
|
44
|
+
let hasDefault = false;
|
|
45
|
+
let hasExportAll = false;
|
|
46
|
+
|
|
47
|
+
const collectBindingNames = (pattern: any) => {
|
|
48
|
+
if (!pattern) return;
|
|
49
|
+
if (pattern.type === "Identifier") {
|
|
50
|
+
exports.add(pattern.name);
|
|
51
|
+
} else if (pattern.type === "ObjectPattern") {
|
|
52
|
+
for (const prop of pattern.properties ?? []) {
|
|
53
|
+
if (prop?.type === "RestElement") {
|
|
54
|
+
collectBindingNames(prop.argument);
|
|
55
|
+
} else {
|
|
56
|
+
collectBindingNames(prop?.value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} else if (pattern.type === "ArrayPattern") {
|
|
60
|
+
for (const el of pattern.elements ?? []) {
|
|
61
|
+
if (el?.type === "RestElement") {
|
|
62
|
+
collectBindingNames(el.argument);
|
|
63
|
+
} else {
|
|
64
|
+
collectBindingNames(el);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const collectDeclarationNames = (declaration: any) => {
|
|
71
|
+
if (!declaration) return;
|
|
72
|
+
if (declaration.type === "VariableDeclaration") {
|
|
73
|
+
for (const decl of declaration.declarations ?? []) {
|
|
74
|
+
collectBindingNames(decl?.id);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
collectBindingNames(declaration.id);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
for (const node of program.body ?? []) {
|
|
82
|
+
if (node?.type === "ExportDefaultDeclaration") {
|
|
83
|
+
hasDefault = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (node?.type === "ExportAllDeclaration") {
|
|
87
|
+
hasExportAll = true;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (node?.type !== "ExportNamedDeclaration") continue;
|
|
91
|
+
|
|
92
|
+
collectDeclarationNames(node.declaration);
|
|
93
|
+
|
|
94
|
+
for (const specifier of node.specifiers ?? []) {
|
|
95
|
+
const exportedName =
|
|
96
|
+
specifier?.exported?.name ?? specifier?.exported?.value;
|
|
97
|
+
if (exportedName === "default") {
|
|
98
|
+
hasDefault = true;
|
|
99
|
+
} else if (typeof exportedName === "string") {
|
|
100
|
+
exports.add(exportedName);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
key: JSON.stringify({
|
|
107
|
+
default: hasDefault,
|
|
108
|
+
exportAll: hasExportAll,
|
|
109
|
+
exports: [...exports].sort(),
|
|
110
|
+
}),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Plugin providing rsc-router:version virtual module.
|
|
116
|
+
* Exports VERSION that changes when RSC modules change (dev) or at build time (production).
|
|
117
|
+
*
|
|
118
|
+
* The version is used for:
|
|
119
|
+
* 1. Cache invalidation - CFCacheStore uses VERSION to invalidate stale cache
|
|
120
|
+
* 2. Version mismatch detection - client sends version, server reloads on mismatch
|
|
121
|
+
*
|
|
122
|
+
* In dev mode, the version updates when:
|
|
123
|
+
* - Server starts (initial version)
|
|
124
|
+
* - RSC modules change via HMR (triggers version module invalidation)
|
|
125
|
+
*
|
|
126
|
+
* Client-only HMR changes don't update the version since they don't affect
|
|
127
|
+
* server-rendered content or cached RSC payloads.
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
export function createVersionPlugin(): Plugin {
|
|
131
|
+
// Generate version at plugin creation time (build/server start)
|
|
132
|
+
const buildVersion = Date.now().toString(16);
|
|
133
|
+
let currentVersion = buildVersion;
|
|
134
|
+
let isDev = false;
|
|
135
|
+
let server: any = null;
|
|
136
|
+
const clientModuleSignatures = new Map<string, ClientModuleSignature>();
|
|
137
|
+
|
|
138
|
+
const bumpVersion = (reason: string) => {
|
|
139
|
+
currentVersion = Date.now().toString(16);
|
|
140
|
+
console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
|
|
141
|
+
|
|
142
|
+
const rscEnv = server?.environments?.rsc;
|
|
143
|
+
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
144
|
+
"\0" + VIRTUAL_IDS.version,
|
|
145
|
+
);
|
|
146
|
+
if (versionMod) {
|
|
147
|
+
rscEnv.moduleGraph.invalidateModule(versionMod);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
name: "@rangojs/router:version",
|
|
153
|
+
enforce: "pre",
|
|
154
|
+
|
|
155
|
+
configResolved(config) {
|
|
156
|
+
isDev = config.command === "serve";
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
configureServer(devServer) {
|
|
160
|
+
server = devServer;
|
|
161
|
+
|
|
162
|
+
devServer.watcher.on("unlink", (filePath) => {
|
|
163
|
+
if (!isDev) return;
|
|
164
|
+
if (!clientModuleSignatures.has(filePath)) return;
|
|
165
|
+
clientModuleSignatures.delete(filePath);
|
|
166
|
+
bumpVersion("Client module removed");
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
resolveId(id) {
|
|
171
|
+
if (id === VIRTUAL_IDS.version) {
|
|
172
|
+
return "\0" + id;
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
load(id) {
|
|
178
|
+
if (id === "\0" + VIRTUAL_IDS.version) {
|
|
179
|
+
return getVirtualVersionContent(currentVersion);
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
transform(code, id) {
|
|
185
|
+
if (!isDev || !isCodeModule(id)) return null;
|
|
186
|
+
const normalizedId = normalizeModuleId(id);
|
|
187
|
+
if (
|
|
188
|
+
!code.includes("use client") &&
|
|
189
|
+
!clientModuleSignatures.has(normalizedId)
|
|
190
|
+
) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const signature = getClientModuleSignature(code);
|
|
195
|
+
if (signature) {
|
|
196
|
+
clientModuleSignatures.set(normalizedId, signature);
|
|
197
|
+
} else {
|
|
198
|
+
clientModuleSignatures.delete(normalizedId);
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// Track RSC module changes and update version
|
|
204
|
+
async hotUpdate(ctx) {
|
|
205
|
+
if (!isDev) return;
|
|
206
|
+
|
|
207
|
+
// Check if this is an RSC environment update (not client/ssr)
|
|
208
|
+
// RSC modules affect server-rendered content and cached payloads
|
|
209
|
+
// In Vite 6, environment is accessed via `this.environment`
|
|
210
|
+
const isRscModule = this.environment?.name === "rsc";
|
|
211
|
+
|
|
212
|
+
if (!isRscModule) return;
|
|
213
|
+
|
|
214
|
+
if (isCodeModule(ctx.file)) {
|
|
215
|
+
const filePath = normalizeModuleId(ctx.file);
|
|
216
|
+
const previousSignature = clientModuleSignatures.get(filePath);
|
|
217
|
+
try {
|
|
218
|
+
const source = await ctx.read();
|
|
219
|
+
const nextSignature = getClientModuleSignature(source);
|
|
220
|
+
if (nextSignature) {
|
|
221
|
+
// "use client" file — compare export signatures.
|
|
222
|
+
// client-component-hmr may have cleared ctx.modules, so we
|
|
223
|
+
// cannot rely on ctx.modules.length for these files.
|
|
224
|
+
clientModuleSignatures.set(filePath, nextSignature);
|
|
225
|
+
if (
|
|
226
|
+
previousSignature &&
|
|
227
|
+
previousSignature.key === nextSignature.key
|
|
228
|
+
) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
clientModuleSignatures.delete(filePath);
|
|
233
|
+
if (!previousSignature) {
|
|
234
|
+
// Not and never was "use client" — use module graph check.
|
|
235
|
+
// ctx.modules is reliable for pure server files (only
|
|
236
|
+
// client-component-hmr clears it for "use client" modules).
|
|
237
|
+
if (ctx.modules.length === 0) return;
|
|
238
|
+
}
|
|
239
|
+
// Was "use client" but directive removed — boundary changed,
|
|
240
|
+
// bump below.
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
// Fail open: if we can't read or parse the update, invalidate.
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
// Non-code file (json, css, etc.) — only bump if it's actually
|
|
247
|
+
// referenced by the RSC module graph.
|
|
248
|
+
if (ctx.modules.length === 0) return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
bumpVersion("RSC module changed");
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for rsc-router:version virtual module.
|
|
3
|
+
* This module is provided by the Vite plugin at build/dev time.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
declare module "@rangojs/router:version" {
|
|
7
|
+
/**
|
|
8
|
+
* Auto-generated version string for cache invalidation.
|
|
9
|
+
* Changes on server restart (dev) or build (prod).
|
|
10
|
+
*/
|
|
11
|
+
export const VERSION: string;
|
|
12
|
+
}
|