@rangojs/router 0.0.0-experimental.d7eeaa75 → 0.0.0-experimental.dacec167
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 +120 -25
- package/dist/bin/rango.js +147 -57
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2151 -846
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +57 -11
- package/skills/breadcrumbs/SKILL.md +3 -1
- 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 +364 -0
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +45 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +46 -4
- package/skills/layout/SKILL.md +28 -7
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +219 -9
- package/skills/middleware/SKILL.md +47 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +71 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +242 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +66 -9
- package/skills/route/SKILL.md +57 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/testing/SKILL.md +778 -0
- package/skills/typesafety/SKILL.md +319 -27
- 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/app-shell.ts +52 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +84 -11
- package/src/browser/navigation-client.ts +76 -28
- package/src/browser/navigation-store.ts +32 -9
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +64 -26
- package/src/browser/prefetch/cache.ts +129 -21
- package/src/browser/prefetch/fetch.ts +148 -16
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +30 -2
- package/src/browser/react/NavigationProvider.tsx +72 -31
- package/src/browser/react/filter-segment-order.ts +51 -7
- 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-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +22 -2
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +64 -22
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +23 -30
- package/src/browser/types.ts +21 -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-trie.ts +52 -25
- 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 +54 -13
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +92 -182
- 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 +9 -4
- package/src/index.ts +53 -15
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +21 -6
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender.ts +4 -4
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -36
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +384 -257
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +100 -28
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +26 -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 +21 -38
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +8 -8
- package/src/router/loader-resolution.ts +19 -2
- package/src/router/manifest.ts +22 -13
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +63 -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 +53 -32
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +15 -26
- package/src/router/middleware.ts +99 -84
- package/src/router/pattern-matching.ts +101 -17
- 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 +58 -2
- 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 +27 -6
- package/src/router/segment-resolution/revalidation.ts +147 -106
- 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/trie-matching.ts +18 -13
- package/src/router/types.ts +8 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +38 -23
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +28 -69
- package/src/rsc/helpers.ts +91 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-route-handler.ts +46 -53
- package/src/rsc/rsc-rendering.ts +35 -51
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +17 -37
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +8 -2
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +143 -53
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +20 -42
- package/src/ssr/index.tsx +5 -1
- 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 +57 -0
- package/src/testing/flight-tree.ts +320 -0
- package/src/testing/flight.entry.ts +39 -0
- package/src/testing/flight.ts +197 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +106 -0
- package/src/testing/internal/context.ts +331 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/render-route.tsx +565 -0
- package/src/testing/run-loader.ts +341 -0
- package/src/testing/run-middleware.ts +188 -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 +270 -0
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +68 -50
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +5 -6
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +35 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper-types.ts +41 -7
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- 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 +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +101 -51
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +67 -26
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- 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 +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +54 -30
- 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-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +496 -486
- package/src/vite/plugins/performance-tracks.ts +29 -25
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +116 -29
- package/src/vite/router-discovery.ts +750 -100
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- 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/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +21 -6
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
import type { Plugin } from "vite";
|
|
16
16
|
import { readFile } from "node:fs/promises";
|
|
17
|
+
import { createRangoDebugger, createCounter, NS } from "../debug.js";
|
|
18
|
+
|
|
19
|
+
const debug = createRangoDebugger(NS.transform);
|
|
17
20
|
|
|
18
21
|
const RSDW_PATCH_RE =
|
|
19
22
|
/((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
|
|
@@ -45,44 +48,45 @@ export function patchRsdwClientDebugInfoRecovery(code: string): {
|
|
|
45
48
|
|
|
46
49
|
export function performanceTracksOptimizeDepsPlugin(): {
|
|
47
50
|
name: string;
|
|
48
|
-
|
|
51
|
+
load(id: string): Promise<{ code: string } | null>;
|
|
49
52
|
} {
|
|
53
|
+
const RSDW_CLIENT_RE =
|
|
54
|
+
/react-server-dom-webpack-client\.browser\.(development|production)\.js$/;
|
|
50
55
|
return {
|
|
51
56
|
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
contents: patched.code,
|
|
63
|
-
loader: "js",
|
|
64
|
-
};
|
|
65
|
-
},
|
|
66
|
-
);
|
|
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
|
+
async load(id: string): Promise<{ code: string } | null> {
|
|
61
|
+
const cleanId = id.split("?")[0] ?? id;
|
|
62
|
+
if (!RSDW_CLIENT_RE.test(cleanId)) return null;
|
|
63
|
+
const code = await readFile(cleanId, "utf8");
|
|
64
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
65
|
+
return { code: patched.code };
|
|
67
66
|
},
|
|
68
67
|
};
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
export function performanceTracksPlugin(): Plugin {
|
|
71
|
+
const counter = createCounter(debug, "performance-tracks");
|
|
72
72
|
return {
|
|
73
73
|
name: "@rangojs/router:performance-tracks",
|
|
74
74
|
|
|
75
|
+
buildEnd() {
|
|
76
|
+
counter?.flush();
|
|
77
|
+
},
|
|
78
|
+
|
|
75
79
|
transform(code, id) {
|
|
76
80
|
if (!id.includes("react-server-dom") || !id.includes("client")) return;
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
85
|
-
|
|
81
|
+
const start = counter ? performance.now() : 0;
|
|
82
|
+
try {
|
|
83
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
84
|
+
if (!patched.debugInfoVar) return;
|
|
85
|
+
debug?.("patched RSDW client (var: %s)", patched.debugInfoVar);
|
|
86
|
+
return patched.code;
|
|
87
|
+
} finally {
|
|
88
|
+
counter?.record(id, performance.now() - start);
|
|
89
|
+
}
|
|
86
90
|
},
|
|
87
91
|
};
|
|
88
92
|
}
|
|
@@ -20,6 +20,9 @@ import type { Plugin } from "vite";
|
|
|
20
20
|
import path from "node:path";
|
|
21
21
|
import MagicString from "magic-string";
|
|
22
22
|
import { normalizePath, hashId } from "./expose-id-utils.js";
|
|
23
|
+
import { createRangoDebugger, createCounter, NS } from "../debug.js";
|
|
24
|
+
|
|
25
|
+
const debug = createRangoDebugger(NS.transform);
|
|
23
26
|
|
|
24
27
|
const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
25
28
|
|
|
@@ -27,11 +30,19 @@ const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
|
27
30
|
// and should not be wrapped (children can't be cache-keyed).
|
|
28
31
|
const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
29
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
|
+
export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
|
|
39
|
+
|
|
30
40
|
export function useCacheTransform(): Plugin {
|
|
31
41
|
let projectRoot = "";
|
|
32
42
|
let isBuild = false;
|
|
33
43
|
let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null =
|
|
34
44
|
null;
|
|
45
|
+
const counter = createCounter(debug, "use-cache");
|
|
35
46
|
|
|
36
47
|
return {
|
|
37
48
|
name: "@rangojs/router:use-cache",
|
|
@@ -42,6 +53,10 @@ export function useCacheTransform(): Plugin {
|
|
|
42
53
|
isBuild = config.command === "build";
|
|
43
54
|
},
|
|
44
55
|
|
|
56
|
+
buildEnd() {
|
|
57
|
+
counter?.flush();
|
|
58
|
+
},
|
|
59
|
+
|
|
45
60
|
async transform(code, id) {
|
|
46
61
|
// Only process in RSC environment
|
|
47
62
|
if (this.environment?.name !== "rsc") return;
|
|
@@ -55,63 +70,68 @@ export function useCacheTransform(): Plugin {
|
|
|
55
70
|
// Only JS/TS files
|
|
56
71
|
if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
const start = counter ? performance.now() : 0;
|
|
74
|
+
try {
|
|
75
|
+
// Lazy-load transform helpers
|
|
76
|
+
if (!rscTransforms) {
|
|
77
|
+
try {
|
|
78
|
+
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
79
|
+
} catch {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const {
|
|
85
|
+
hasDirective,
|
|
86
|
+
transformWrapExport,
|
|
87
|
+
transformHoistInlineDirective,
|
|
88
|
+
} = rscTransforms;
|
|
89
|
+
|
|
90
|
+
// Parse AST
|
|
91
|
+
let ast: any;
|
|
60
92
|
try {
|
|
61
|
-
|
|
93
|
+
const { parseAst } = await import("vite");
|
|
94
|
+
ast = parseAst(code, { lang: "tsx" });
|
|
62
95
|
} catch {
|
|
63
96
|
return;
|
|
64
97
|
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const {
|
|
68
|
-
hasDirective,
|
|
69
|
-
transformWrapExport,
|
|
70
|
-
transformHoistInlineDirective,
|
|
71
|
-
} = rscTransforms;
|
|
72
98
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
ast
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
100
|
+
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
101
|
+
|
|
102
|
+
// Check for file-level "use cache"
|
|
103
|
+
if (hasDirective(ast.body, "use cache")) {
|
|
104
|
+
return transformFileLevelUseCache(
|
|
105
|
+
code,
|
|
106
|
+
ast,
|
|
107
|
+
filePath,
|
|
108
|
+
id,
|
|
109
|
+
isBuild,
|
|
110
|
+
isLayoutOrTemplate,
|
|
111
|
+
transformWrapExport,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
84
114
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
88
118
|
code,
|
|
89
119
|
ast,
|
|
90
120
|
filePath,
|
|
91
121
|
id,
|
|
92
122
|
isBuild,
|
|
93
|
-
|
|
94
|
-
transformWrapExport,
|
|
123
|
+
transformHoistInlineDirective,
|
|
95
124
|
);
|
|
96
|
-
}
|
|
97
125
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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));
|
|
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
|
+
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
113
130
|
|
|
114
|
-
|
|
131
|
+
if (functionResult) return functionResult;
|
|
132
|
+
} finally {
|
|
133
|
+
counter?.record(id, performance.now() - start);
|
|
134
|
+
}
|
|
115
135
|
},
|
|
116
136
|
};
|
|
117
137
|
}
|
|
@@ -206,7 +226,7 @@ function transformFunctionLevelUseCache(
|
|
|
206
226
|
) {
|
|
207
227
|
try {
|
|
208
228
|
const { output, names } = transformHoistInlineDirective(code, ast, {
|
|
209
|
-
directive:
|
|
229
|
+
directive: USE_CACHE_DIRECTIVE_RE,
|
|
210
230
|
runtime: (
|
|
211
231
|
value: string,
|
|
212
232
|
name: string,
|
|
@@ -260,11 +280,6 @@ function findFileLevelDirective(
|
|
|
260
280
|
return null;
|
|
261
281
|
}
|
|
262
282
|
|
|
263
|
-
/**
|
|
264
|
-
* The valid directive regex (must stay in sync with transformFunctionLevelUseCache).
|
|
265
|
-
*/
|
|
266
|
-
const VALID_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
|
|
267
|
-
|
|
268
283
|
/**
|
|
269
284
|
* Regex for near-miss: starts with "use cache:" but has invalid tokens.
|
|
270
285
|
*/
|
|
@@ -294,7 +309,7 @@ function warnOnNearMissDirectives(
|
|
|
294
309
|
if (
|
|
295
310
|
value.startsWith("use cache") &&
|
|
296
311
|
NEAR_MISS_RE.test(value) &&
|
|
297
|
-
!
|
|
312
|
+
!USE_CACHE_DIRECTIVE_RE.test(value)
|
|
298
313
|
) {
|
|
299
314
|
const profilePart = value.slice("use cache:".length).trim();
|
|
300
315
|
warn(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import * as Vite from "vite";
|
|
4
|
+
import { resolveRscEntryFromConfig } from "../utils/shared-utils.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Plugin that auto-injects VERSION and routes-manifest into custom entry.rsc files.
|
|
@@ -20,18 +21,7 @@ export function createVersionInjectorPlugin(
|
|
|
20
21
|
|
|
21
22
|
configResolved(config) {
|
|
22
23
|
let entryPath = rscEntryPath;
|
|
23
|
-
|
|
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
|
-
}
|
|
24
|
+
if (!entryPath) entryPath = resolveRscEntryFromConfig(config);
|
|
35
25
|
if (entryPath) {
|
|
36
26
|
resolvedEntryPath = resolve(config.root, entryPath);
|
|
37
27
|
}
|
|
@@ -47,16 +37,26 @@ export function createVersionInjectorPlugin(
|
|
|
47
37
|
return null;
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
+
const prepend: string[] = [
|
|
55
|
+
`import "virtual:rsc-router/routes-manifest";`,
|
|
56
|
+
];
|
|
58
57
|
|
|
59
58
|
// Auto-inject VERSION if file uses createRSCHandler without version
|
|
59
|
+
let newCode = code;
|
|
60
60
|
const needsVersion =
|
|
61
61
|
code.includes("createRSCHandler") &&
|
|
62
62
|
!code.includes("@rangojs/router:version") &&
|
|
@@ -70,9 +70,25 @@ export function createVersionInjectorPlugin(
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
+
const lines = newCode.split("\n");
|
|
78
|
+
let insertAt = 0;
|
|
79
|
+
while (insertAt < lines.length) {
|
|
80
|
+
const trimmed = lines[insertAt]!.trim();
|
|
81
|
+
if (trimmed === "" || /^\/\/\/\s*<reference\b/.test(trimmed)) {
|
|
82
|
+
insertAt++;
|
|
83
|
+
} else {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
newCode = [
|
|
88
|
+
...lines.slice(0, insertAt),
|
|
89
|
+
...prepend,
|
|
90
|
+
...lines.slice(insertAt),
|
|
91
|
+
].join("\n");
|
|
76
92
|
|
|
77
93
|
return {
|
|
78
94
|
code: newCode,
|
|
@@ -18,7 +18,7 @@ function getClientModuleSignature(
|
|
|
18
18
|
): ClientModuleSignature | undefined {
|
|
19
19
|
let program: any;
|
|
20
20
|
try {
|
|
21
|
-
program = parseAst(source, {
|
|
21
|
+
program = parseAst(source, { lang: "tsx" });
|
|
22
22
|
} catch {
|
|
23
23
|
return undefined;
|
|
24
24
|
}
|
|
@@ -133,6 +133,7 @@ export function createVersionPlugin(): Plugin {
|
|
|
133
133
|
let currentVersion = buildVersion;
|
|
134
134
|
let isDev = false;
|
|
135
135
|
let server: any = null;
|
|
136
|
+
let resolvedCacheDir: string | undefined;
|
|
136
137
|
const clientModuleSignatures = new Map<string, ClientModuleSignature>();
|
|
137
138
|
|
|
138
139
|
let versionCounter = 0;
|
|
@@ -140,7 +141,7 @@ export function createVersionPlugin(): Plugin {
|
|
|
140
141
|
// Use timestamp + counter to guarantee uniqueness even when multiple
|
|
141
142
|
// bumps happen within the same millisecond (e.g. cascading HMR events).
|
|
142
143
|
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
143
|
-
console.log(`[
|
|
144
|
+
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
144
145
|
|
|
145
146
|
const rscEnv = server?.environments?.rsc;
|
|
146
147
|
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
@@ -157,6 +158,12 @@ export function createVersionPlugin(): Plugin {
|
|
|
157
158
|
|
|
158
159
|
configResolved(config) {
|
|
159
160
|
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
|
+
resolvedCacheDir = config.cacheDir
|
|
165
|
+
? String(config.cacheDir).replace(/\\/g, "/")
|
|
166
|
+
: undefined;
|
|
160
167
|
},
|
|
161
168
|
|
|
162
169
|
configureServer(devServer) {
|
|
@@ -214,6 +221,14 @@ export function createVersionPlugin(): Plugin {
|
|
|
214
221
|
|
|
215
222
|
if (!isRscModule) return;
|
|
216
223
|
|
|
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
|
+
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
231
|
+
|
|
217
232
|
// Skip re-bumping when the version virtual module itself is invalidated
|
|
218
233
|
// (our own bumpVersion() invalidates it, which re-triggers hotUpdate).
|
|
219
234
|
if (
|
|
@@ -264,3 +279,45 @@ export function createVersionPlugin(): Plugin {
|
|
|
264
279
|
},
|
|
265
280
|
};
|
|
266
281
|
}
|
|
282
|
+
|
|
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
|
+
export function isViteDepCachePath(
|
|
298
|
+
filePath: string | undefined,
|
|
299
|
+
cacheDir?: string,
|
|
300
|
+
): boolean {
|
|
301
|
+
if (!filePath) return false;
|
|
302
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
303
|
+
|
|
304
|
+
if (cacheDir) {
|
|
305
|
+
const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
306
|
+
if (
|
|
307
|
+
normalized === normalizedCacheDir ||
|
|
308
|
+
normalized.startsWith(normalizedCacheDir + "/")
|
|
309
|
+
) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
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
|
+
return (
|
|
320
|
+
/\/node_modules\/\.vite[^/]*\//.test(normalized) ||
|
|
321
|
+
normalized.includes("/.vite-isolated/")
|
|
322
|
+
);
|
|
323
|
+
}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import { createElement, StrictMode } from "react";
|
|
15
15
|
import { hydrateRoot } from "react-dom/client";
|
|
16
16
|
import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
|
|
17
|
-
import { initBrowserApp,
|
|
17
|
+
import { initBrowserApp, Rango } from "@rangojs/router/browser";
|
|
18
18
|
|
|
19
19
|
async function initializeApp() {
|
|
20
20
|
const deps = {
|
|
@@ -29,7 +29,7 @@ async function initializeApp() {
|
|
|
29
29
|
|
|
30
30
|
hydrateRoot(
|
|
31
31
|
document,
|
|
32
|
-
createElement(StrictMode, null, createElement(
|
|
32
|
+
createElement(StrictMode, null, createElement(Rango))
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|