@rangojs/router 0.0.0-experimental.32 → 0.0.0-experimental.3232cd17
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 +4 -0
- package/README.md +198 -44
- package/dist/bin/rango.js +287 -105
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3248 -1117
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +73 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +107 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +245 -21
- package/skills/caching/SKILL.md +302 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +270 -30
- package/skills/host-router/SKILL.md +82 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +294 -30
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -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 +203 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +97 -5
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +67 -40
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +86 -147
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +148 -19
- package/src/browser/navigation-client.ts +187 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/partial-update.ts +123 -94
- package/src/browser/prefetch/cache.ts +214 -36
- package/src/browser/prefetch/fetch.ts +260 -38
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +93 -11
- package/src/browser/react/NavigationProvider.tsx +115 -34
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +215 -76
- package/src/browser/scroll-restoration.ts +46 -39
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -50
- package/src/browser/types.ts +95 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +149 -43
- package/src/cache/cache-scope.ts +148 -81
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2550 -93
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +78 -27
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +136 -37
- 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/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- 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 +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +52 -26
- package/src/index.ts +100 -38
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +437 -274
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +52 -10
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -41
- package/src/router/intercept-resolution.ts +25 -23
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +213 -30
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +27 -6
- package/src/router/match-middleware/cache-lookup.ts +205 -249
- package/src/router/match-middleware/cache-store.ts +45 -32
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +173 -143
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +190 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +6 -28
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +242 -75
- package/src/router/segment-resolution/helpers.ts +63 -24
- package/src/router/segment-resolution/loader-cache.ts +41 -37
- package/src/router/segment-resolution/revalidation.ts +456 -372
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +134 -43
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -383
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +30 -3
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +56 -54
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -67
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +25 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +272 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +338 -126
- package/src/ssr/index.tsx +23 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -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 +305 -0
- 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/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- 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 +192 -99
- 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 +51 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +187 -69
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- 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 +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +41 -20
- package/src/vite/plugins/virtual-entries.ts +2 -17
- package/src/vite/rango.ts +257 -289
- package/src/vite/router-discovery.ts +930 -140
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Performance Tracks — RSDW client patch
|
|
3
|
+
*
|
|
4
|
+
* Patches the RSDW client so _debugInfo recovery works for plain-object
|
|
5
|
+
* payloads (our RscPayload shape). Without this, the Server Components
|
|
6
|
+
* track in Chrome DevTools stays empty.
|
|
7
|
+
*
|
|
8
|
+
* React's flushComponentPerformance uses splice(0) to empty _debugInfo
|
|
9
|
+
* after resolution, then recovers it from the resolved value — but only
|
|
10
|
+
* for arrays, async iterables, React elements, and lazy types. Since our
|
|
11
|
+
* RscPayload is a plain object, _debugInfo is lost. This patch relaxes
|
|
12
|
+
* the check so _debugInfo is recovered from any object.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Plugin } from "vite";
|
|
16
|
+
import { readFile } from "node:fs/promises";
|
|
17
|
+
import { createRangoDebugger, createCounter, NS } from "../debug.js";
|
|
18
|
+
|
|
19
|
+
const debug = createRangoDebugger(NS.transform);
|
|
20
|
+
|
|
21
|
+
const RSDW_PATCH_RE =
|
|
22
|
+
/((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
|
|
23
|
+
|
|
24
|
+
function buildPatchReplacement(match: string, debugInfoVar: string): string {
|
|
25
|
+
return `${match}
|
|
26
|
+
if (${debugInfoVar} && 0 === ${debugInfoVar}.length && "fulfilled" === root.status) {
|
|
27
|
+
var _resolved = "function" === typeof resolveLazy ? resolveLazy(root.value) : root.value;
|
|
28
|
+
if ("object" === typeof _resolved && null !== _resolved && isArrayImpl(_resolved._debugInfo)) {
|
|
29
|
+
${debugInfoVar} = _resolved._debugInfo;
|
|
30
|
+
}
|
|
31
|
+
}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function patchRsdwClientDebugInfoRecovery(code: string): {
|
|
35
|
+
code: string;
|
|
36
|
+
debugInfoVar: string | null;
|
|
37
|
+
} {
|
|
38
|
+
const match = code.match(RSDW_PATCH_RE);
|
|
39
|
+
if (!match) {
|
|
40
|
+
return { code, debugInfoVar: null };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
code: code.replace(match[1]!, buildPatchReplacement(match[1]!, match[2]!)),
|
|
45
|
+
debugInfoVar: match[2]!,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function performanceTracksOptimizeDepsPlugin(): {
|
|
50
|
+
name: string;
|
|
51
|
+
load(id: string): Promise<{ code: string } | null>;
|
|
52
|
+
} {
|
|
53
|
+
const RSDW_CLIENT_RE =
|
|
54
|
+
/react-server-dom-webpack-client\.browser\.(development|production)\.js$/;
|
|
55
|
+
return {
|
|
56
|
+
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
57
|
+
async load(id: string): Promise<{ code: string } | null> {
|
|
58
|
+
const cleanId = id.split("?")[0] ?? id;
|
|
59
|
+
if (!RSDW_CLIENT_RE.test(cleanId)) return null;
|
|
60
|
+
const code = await readFile(cleanId, "utf8");
|
|
61
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
62
|
+
return { code: patched.code };
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function performanceTracksPlugin(): Plugin {
|
|
68
|
+
const counter = createCounter(debug, "performance-tracks");
|
|
69
|
+
return {
|
|
70
|
+
name: "@rangojs/router:performance-tracks",
|
|
71
|
+
|
|
72
|
+
buildEnd() {
|
|
73
|
+
counter?.flush();
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
transform(code, id) {
|
|
77
|
+
if (!id.includes("react-server-dom") || !id.includes("client")) return;
|
|
78
|
+
const start = counter ? performance.now() : 0;
|
|
79
|
+
try {
|
|
80
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
81
|
+
if (!patched.debugInfoVar) return;
|
|
82
|
+
debug?.("patched RSDW client (var: %s)", patched.debugInfoVar);
|
|
83
|
+
return patched.code;
|
|
84
|
+
} finally {
|
|
85
|
+
counter?.record(id, performance.now() - start);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Vite plugin that triggers a full browser reload
|
|
5
|
-
*
|
|
4
|
+
* Vite plugin that triggers a full browser reload from terminal input.
|
|
5
|
+
*
|
|
6
|
+
* This plugin is intentionally passive:
|
|
7
|
+
* - it never enables raw mode on stdin
|
|
8
|
+
* - it never restores terminal state
|
|
9
|
+
* - it reacts to Ctrl+R when that raw byte reaches the process
|
|
10
|
+
* - it also supports safe line-based fallbacks like "e" + Enter
|
|
6
11
|
*
|
|
7
12
|
* Usage:
|
|
8
13
|
* ```ts
|
|
@@ -15,40 +20,100 @@ import type { Plugin } from "vite";
|
|
|
15
20
|
*/
|
|
16
21
|
export function poke(): Plugin {
|
|
17
22
|
return {
|
|
18
|
-
name: "
|
|
23
|
+
name: "@rangojs/router:poke",
|
|
19
24
|
apply: "serve",
|
|
20
25
|
|
|
21
26
|
configureServer(server) {
|
|
22
27
|
const stdin = process.stdin;
|
|
28
|
+
const debug = process.env.RANGO_POKE_DEBUG === "1";
|
|
29
|
+
|
|
30
|
+
const triggerReload = (source: string) => {
|
|
31
|
+
server.hot.send({ type: "full-reload", path: "*" });
|
|
32
|
+
server.config.logger.info(` browser reload (${source})`, {
|
|
33
|
+
timestamp: true,
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const toBuffer = (chunk: string | Buffer): Buffer => {
|
|
38
|
+
return typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const formatChunk = (chunk: string | Buffer): string => {
|
|
42
|
+
const data = toBuffer(chunk);
|
|
43
|
+
const hex = Array.from(data)
|
|
44
|
+
.map((byte) => `0x${byte.toString(16).padStart(2, "0")}`)
|
|
45
|
+
.join(" ");
|
|
46
|
+
const ascii = Array.from(data)
|
|
47
|
+
.map((byte) => {
|
|
48
|
+
if (byte >= 0x20 && byte <= 0x7e) return String.fromCharCode(byte);
|
|
49
|
+
if (byte === 0x0a) return "\\n";
|
|
50
|
+
if (byte === 0x0d) return "\\r";
|
|
51
|
+
if (byte === 0x09) return "\\t";
|
|
52
|
+
return ".";
|
|
53
|
+
})
|
|
54
|
+
.join("");
|
|
55
|
+
return `len=${data.length} hex=[${hex}] ascii="${ascii}"`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const readCtrlR = (chunk: string | Buffer): boolean => {
|
|
59
|
+
const data =
|
|
60
|
+
typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
61
|
+
return data.length === 1 && data[0] === 0x12;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const readSubmittedCommands = (chunk: string | Buffer): string[] => {
|
|
65
|
+
const text = toBuffer(chunk)
|
|
66
|
+
.toString("utf8")
|
|
67
|
+
.replace(/\r\n/g, "\n")
|
|
68
|
+
.replace(/\r/g, "\n");
|
|
69
|
+
|
|
70
|
+
if (!text.includes("\n")) return [];
|
|
71
|
+
|
|
72
|
+
const lines = text.split("\n");
|
|
73
|
+
lines.pop();
|
|
74
|
+
return lines;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (debug) {
|
|
78
|
+
server.config.logger.info(
|
|
79
|
+
` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? (stdin.isRaw ? "yes" : "no") : "n/a"})`,
|
|
80
|
+
{ timestamp: true },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
23
83
|
|
|
24
|
-
// Raw mode delivers individual keystrokes as immediate single-byte
|
|
25
|
-
// events instead of waiting for Enter (cooked/line-buffered mode).
|
|
26
|
-
// Without it, Ctrl+R (0x12) is never delivered as a discrete byte.
|
|
27
|
-
// When stdin is a pipe (CI, spawned process) setRawMode is unavailable
|
|
28
|
-
// but data already arrives unbuffered, so the isTTY guard suffices.
|
|
29
|
-
const previousRawMode = stdin.isTTY ? stdin.isRaw : null;
|
|
30
84
|
if (stdin.isTTY) {
|
|
31
|
-
|
|
85
|
+
server.config.logger.info(
|
|
86
|
+
" poke ready: press e + enter to reload browser (ctrl+r also works when available)",
|
|
87
|
+
{ timestamp: true },
|
|
88
|
+
);
|
|
32
89
|
}
|
|
33
90
|
|
|
34
|
-
const onData = (data: Buffer) => {
|
|
35
|
-
if (
|
|
91
|
+
const onData = (data: string | Buffer) => {
|
|
92
|
+
if (debug) {
|
|
93
|
+
server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
|
|
94
|
+
timestamp: true,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
36
97
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
if (data
|
|
42
|
-
|
|
98
|
+
// Only react to the exact Ctrl+R byte when some host terminal or
|
|
99
|
+
// wrapper already delivers it to this process. We intentionally do
|
|
100
|
+
// not enable raw mode here because that can steal Vite shortcuts
|
|
101
|
+
// like "r" / "q" and interfere with terminal-level controls.
|
|
102
|
+
if (readCtrlR(data)) {
|
|
103
|
+
triggerReload("ctrl+r");
|
|
43
104
|
return;
|
|
44
105
|
}
|
|
45
106
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
107
|
+
for (const command of readSubmittedCommands(data)) {
|
|
108
|
+
if (command === "e") {
|
|
109
|
+
triggerReload("e+enter");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (command === "\u001br") {
|
|
114
|
+
triggerReload("option+r+enter");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
52
117
|
}
|
|
53
118
|
};
|
|
54
119
|
|
|
@@ -56,9 +121,6 @@ export function poke(): Plugin {
|
|
|
56
121
|
|
|
57
122
|
server.httpServer?.on("close", () => {
|
|
58
123
|
stdin.off("data", onData);
|
|
59
|
-
if (stdin.isTTY && previousRawMode !== null) {
|
|
60
|
-
stdin.setRawMode(previousRawMode);
|
|
61
|
-
}
|
|
62
124
|
});
|
|
63
125
|
},
|
|
64
126
|
};
|
|
@@ -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,14 @@ 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
|
+
export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
|
|
34
|
+
|
|
30
35
|
export function useCacheTransform(): Plugin {
|
|
31
36
|
let projectRoot = "";
|
|
32
37
|
let isBuild = false;
|
|
33
38
|
let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null =
|
|
34
39
|
null;
|
|
40
|
+
const counter = createCounter(debug, "use-cache");
|
|
35
41
|
|
|
36
42
|
return {
|
|
37
43
|
name: "@rangojs/router:use-cache",
|
|
@@ -42,6 +48,10 @@ export function useCacheTransform(): Plugin {
|
|
|
42
48
|
isBuild = config.command === "build";
|
|
43
49
|
},
|
|
44
50
|
|
|
51
|
+
buildEnd() {
|
|
52
|
+
counter?.flush();
|
|
53
|
+
},
|
|
54
|
+
|
|
45
55
|
async transform(code, id) {
|
|
46
56
|
// Only process in RSC environment
|
|
47
57
|
if (this.environment?.name !== "rsc") return;
|
|
@@ -55,63 +65,60 @@ export function useCacheTransform(): Plugin {
|
|
|
55
65
|
// Only JS/TS files
|
|
56
66
|
if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
const start = counter ? performance.now() : 0;
|
|
69
|
+
try {
|
|
70
|
+
if (!rscTransforms) {
|
|
71
|
+
try {
|
|
72
|
+
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
hasDirective,
|
|
80
|
+
transformWrapExport,
|
|
81
|
+
transformHoistInlineDirective,
|
|
82
|
+
} = rscTransforms;
|
|
83
|
+
|
|
84
|
+
let ast: any;
|
|
60
85
|
try {
|
|
61
|
-
|
|
86
|
+
const { parseAst } = await import("vite");
|
|
87
|
+
ast = parseAst(code, { lang: "tsx" });
|
|
62
88
|
} catch {
|
|
63
89
|
return;
|
|
64
90
|
}
|
|
65
|
-
}
|
|
66
91
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
83
|
-
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
92
|
+
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
93
|
+
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
94
|
+
|
|
95
|
+
if (hasDirective(ast.body, "use cache")) {
|
|
96
|
+
return transformFileLevelUseCache(
|
|
97
|
+
code,
|
|
98
|
+
ast,
|
|
99
|
+
filePath,
|
|
100
|
+
id,
|
|
101
|
+
isBuild,
|
|
102
|
+
isLayoutOrTemplate,
|
|
103
|
+
transformWrapExport,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
84
106
|
|
|
85
|
-
|
|
86
|
-
if (hasDirective(ast.body, "use cache")) {
|
|
87
|
-
return transformFileLevelUseCache(
|
|
107
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
88
108
|
code,
|
|
89
109
|
ast,
|
|
90
110
|
filePath,
|
|
91
111
|
id,
|
|
92
112
|
isBuild,
|
|
93
|
-
|
|
94
|
-
transformWrapExport,
|
|
113
|
+
transformHoistInlineDirective,
|
|
95
114
|
);
|
|
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
115
|
|
|
109
|
-
|
|
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));
|
|
116
|
+
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
113
117
|
|
|
114
|
-
|
|
118
|
+
if (functionResult) return functionResult;
|
|
119
|
+
} finally {
|
|
120
|
+
counter?.record(id, performance.now() - start);
|
|
121
|
+
}
|
|
115
122
|
},
|
|
116
123
|
};
|
|
117
124
|
}
|
|
@@ -125,8 +132,7 @@ function transformFileLevelUseCache(
|
|
|
125
132
|
isLayoutOrTemplate: boolean,
|
|
126
133
|
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
127
134
|
) {
|
|
128
|
-
|
|
129
|
-
const nonFunctionExports: string[] = [];
|
|
135
|
+
const unconfirmedExports: string[] = [];
|
|
130
136
|
|
|
131
137
|
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
132
138
|
runtime: (value: string, name: string) => {
|
|
@@ -135,30 +141,38 @@ function transformFileLevelUseCache(
|
|
|
135
141
|
},
|
|
136
142
|
rejectNonAsyncFunction: false,
|
|
137
143
|
filter: (name: string, meta: { isFunction?: boolean }) => {
|
|
138
|
-
// Skip default export of layout/template files (they receive children)
|
|
139
144
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
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);
|
|
143
154
|
return false;
|
|
144
155
|
}
|
|
145
156
|
return true;
|
|
146
157
|
},
|
|
147
158
|
});
|
|
148
159
|
|
|
149
|
-
if (
|
|
160
|
+
if (unconfirmedExports.length > 0) {
|
|
161
|
+
const plural = unconfirmedExports.length > 1;
|
|
150
162
|
throw new Error(
|
|
151
|
-
`[rango:use-cache] File-level "use cache" in ${sourceId}
|
|
152
|
-
`
|
|
153
|
-
`${
|
|
154
|
-
|
|
155
|
-
`
|
|
156
|
-
`
|
|
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.`,
|
|
157
172
|
);
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
if (exportNames.length === 0) {
|
|
161
|
-
// Even if no exports were wrapped, strip the directive
|
|
162
176
|
const s = new MagicString(code);
|
|
163
177
|
const directive = findFileLevelDirective(ast);
|
|
164
178
|
if (directive) {
|
|
@@ -175,12 +189,10 @@ function transformFileLevelUseCache(
|
|
|
175
189
|
return;
|
|
176
190
|
}
|
|
177
191
|
|
|
178
|
-
// Prepend the import
|
|
179
192
|
output.prepend(
|
|
180
193
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
181
194
|
);
|
|
182
195
|
|
|
183
|
-
// Replace the directive with a comment
|
|
184
196
|
const directive = findFileLevelDirective(ast);
|
|
185
197
|
if (directive) {
|
|
186
198
|
output.overwrite(
|
|
@@ -206,7 +218,7 @@ function transformFunctionLevelUseCache(
|
|
|
206
218
|
) {
|
|
207
219
|
try {
|
|
208
220
|
const { output, names } = transformHoistInlineDirective(code, ast, {
|
|
209
|
-
directive:
|
|
221
|
+
directive: USE_CACHE_DIRECTIVE_RE,
|
|
210
222
|
runtime: (
|
|
211
223
|
value: string,
|
|
212
224
|
name: string,
|
|
@@ -224,9 +236,6 @@ function transformFunctionLevelUseCache(
|
|
|
224
236
|
|
|
225
237
|
if (names.length === 0) return;
|
|
226
238
|
|
|
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
239
|
output.prepend(
|
|
231
240
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
232
241
|
);
|
|
@@ -241,9 +250,6 @@ function transformFunctionLevelUseCache(
|
|
|
241
250
|
}
|
|
242
251
|
}
|
|
243
252
|
|
|
244
|
-
/**
|
|
245
|
-
* Find the file-level "use cache" directive AST node for removal.
|
|
246
|
-
*/
|
|
247
253
|
function findFileLevelDirective(
|
|
248
254
|
ast: any,
|
|
249
255
|
): { start: number; end: number } | null {
|
|
@@ -260,23 +266,8 @@ function findFileLevelDirective(
|
|
|
260
266
|
return null;
|
|
261
267
|
}
|
|
262
268
|
|
|
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
269
|
const NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
272
270
|
|
|
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
271
|
function warnOnNearMissDirectives(
|
|
281
272
|
ast: any,
|
|
282
273
|
fileId: string,
|
|
@@ -294,7 +285,7 @@ function warnOnNearMissDirectives(
|
|
|
294
285
|
if (
|
|
295
286
|
value.startsWith("use cache") &&
|
|
296
287
|
NEAR_MISS_RE.test(value) &&
|
|
297
|
-
!
|
|
288
|
+
!USE_CACHE_DIRECTIVE_RE.test(value)
|
|
298
289
|
) {
|
|
299
290
|
const profilePart = value.slice("use cache:".length).trim();
|
|
300
291
|
warn(
|
|
@@ -304,7 +295,6 @@ function warnOnNearMissDirectives(
|
|
|
304
295
|
}
|
|
305
296
|
}
|
|
306
297
|
|
|
307
|
-
// Walk into function bodies where directives appear
|
|
308
298
|
for (const key of Object.keys(node)) {
|
|
309
299
|
const child = node[key];
|
|
310
300
|
if (Array.isArray(child)) {
|