@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c
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 +8 -0
- package/README.md +126 -16
- package/dist/bin/rango.js +319 -95
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2724 -1053
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +11 -9
- package/skills/hooks/SKILL.md +243 -29
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +68 -19
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +190 -23
- package/skills/loader/SKILL.md +235 -9
- package/skills/middleware/SKILL.md +18 -10
- package/skills/migrate-nextjs/SKILL.md +43 -19
- package/skills/migrate-react-router/SKILL.md +8 -2
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +18 -7
- package/skills/prerender/SKILL.md +65 -60
- package/skills/rango/SKILL.md +251 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +115 -48
- package/skills/route/SKILL.md +46 -5
- package/skills/router-setup/SKILL.md +30 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -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 +122 -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 +123 -0
- package/skills/typesafety/SKILL.md +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +0 -65
- 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/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +192 -150
- 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/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +121 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +115 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +147 -128
- package/src/browser/prefetch/cache.ts +107 -56
- package/src/browser/prefetch/fetch.ts +204 -34
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +30 -7
- package/src/browser/react/NavigationProvider.tsx +283 -118
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -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 +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +11 -11
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +25 -3
- 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 +99 -8
- package/src/browser/rsc-router.tsx +91 -24
- package/src/browser/scroll-restoration.ts +30 -17
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +214 -55
- package/src/browser/types.ts +80 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- 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 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +117 -14
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +117 -23
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +55 -28
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +224 -41
- package/src/cache/cache-scope.ts +188 -82
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +1989 -378
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +36 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +31 -23
- package/src/handles/MetaTags.tsx +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- 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 +96 -12
- package/src/index.ts +94 -14
- package/src/internal-debug.ts +11 -10
- 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/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +34 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +82 -55
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +27 -43
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +10 -47
- package/src/router/loader-resolution.ts +155 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +9 -24
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +44 -74
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +247 -166
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +85 -94
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +104 -65
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +83 -35
- package/src/router/router-options.ts +136 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +97 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +272 -320
- 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 +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +162 -64
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +110 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +264 -220
- package/src/rsc/helpers.ts +100 -6
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +117 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +88 -188
- package/src/rsc/rsc-rendering.ts +98 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +281 -117
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +23 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +31 -4
- package/src/segment-system.tsx +254 -143
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +163 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +287 -178
- package/src/ssr/index.tsx +21 -16
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -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 +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -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 +357 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -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 +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +29 -12
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +126 -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 +96 -68
- 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 +44 -0
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +2 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +126 -8
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- 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-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +109 -118
- package/src/vite/router-discovery.ts +718 -119
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -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 +54 -39
- package/src/vite/utils/shared-utils.ts +90 -41
- 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/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -49,7 +49,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
49
49
|
);
|
|
50
50
|
genFileVars.push(varName);
|
|
51
51
|
} else {
|
|
52
|
-
// Routers without sourceFile: inline their manifest data directly
|
|
53
52
|
routersWithoutGenFile.push({
|
|
54
53
|
id: entry.id,
|
|
55
54
|
manifest: entry.routeManifest,
|
|
@@ -58,7 +57,7 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
const lines = [
|
|
61
|
-
`import { setCachedManifest,
|
|
60
|
+
`import { setCachedManifest, setRouterManifest, registerRouterManifestLoader, clearAllRouterData } from "@rangojs/router/server";`,
|
|
62
61
|
...genFileImports,
|
|
63
62
|
// Clear stale per-router cached data (manifest, trie, precomputed entries)
|
|
64
63
|
// before re-populating. In Cloudflare dev mode, program reloads re-evaluate
|
|
@@ -68,14 +67,12 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
68
67
|
`clearAllRouterData();`,
|
|
69
68
|
];
|
|
70
69
|
|
|
71
|
-
// Flatten NamedRoutes entries: search schema objects -> plain string paths
|
|
72
70
|
if (genFileVars.length > 0) {
|
|
73
71
|
lines.push(
|
|
74
72
|
`function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`,
|
|
75
73
|
);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
// Build the merged manifest from gen file imports + inlined data
|
|
79
76
|
if (genFileVars.length === 1 && routersWithoutGenFile.length === 0) {
|
|
80
77
|
lines.push(`setCachedManifest(__flat(${genFileVars[0]}));`);
|
|
81
78
|
} else {
|
|
@@ -86,7 +83,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
86
83
|
lines.push(`setCachedManifest({ ${parts.join(", ")} });`);
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
// Set per-router manifests
|
|
90
86
|
let genVarIdx = 0;
|
|
91
87
|
for (const entry of state.perRouterManifests) {
|
|
92
88
|
if (entry.sourceFile) {
|
|
@@ -101,31 +97,19 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
101
97
|
}
|
|
102
98
|
}
|
|
103
99
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
lines.push(
|
|
117
|
-
`setPrecomputedEntries(${jsonParseExpression(state.mergedPrecomputedEntries)});`,
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
if (state.mergedRouteTrie) {
|
|
121
|
-
lines.push(
|
|
122
|
-
`setRouteTrie(${jsonParseExpression(state.mergedRouteTrie)});`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
100
|
+
// Per-router trie and precomputedEntries are NOT inlined eagerly.
|
|
101
|
+
// They live in the per-router lazy chunks (generatePerRouterModule) and
|
|
102
|
+
// are loaded via ensureRouterManifest(routerId), which is awaited before
|
|
103
|
+
// every request in router.fetch() and before findMatch is reached.
|
|
104
|
+
// Inlining the merged versions here would duplicate the per-router data
|
|
105
|
+
// (the merged trie/precomputedEntries equal the per-router data for
|
|
106
|
+
// single-router apps; for multi-router, the merged trie is dead code
|
|
107
|
+
// because find-match.ts only consumes per-router tries).
|
|
108
|
+
//
|
|
109
|
+
// In dev mode, the handler also falls back to Phase 2 regex matching
|
|
110
|
+
// against live router.urlpatterns, which is always correct after a
|
|
111
|
+
// program reload.
|
|
126
112
|
|
|
127
|
-
// Register lazy loaders for per-router manifest modules.
|
|
128
|
-
// Each import() uses a static string literal so Rollup creates separate chunks.
|
|
129
113
|
for (const routerId of state.perRouterManifestDataMap.keys()) {
|
|
130
114
|
lines.push(
|
|
131
115
|
`registerRouterManifestLoader(${JSON.stringify(routerId)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId)}));`,
|
|
@@ -139,9 +123,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
139
123
|
return lines.join("\n");
|
|
140
124
|
}
|
|
141
125
|
|
|
142
|
-
// No manifest: either discovery hasn't completed or no runner (Cloudflare dev).
|
|
143
|
-
// Still inject __PRERENDER_DEV_URL so the prerender store can fetch on-demand.
|
|
144
|
-
// Re-resolve origin now since the server is listening by module load time.
|
|
145
126
|
if (!state.isBuildMode) {
|
|
146
127
|
const origin =
|
|
147
128
|
state.devServerOrigin ||
|
|
@@ -170,7 +151,6 @@ export function generatePerRouterModule(
|
|
|
170
151
|
const lines: string[] = [];
|
|
171
152
|
|
|
172
153
|
if (routerEntry?.sourceFile) {
|
|
173
|
-
// Import manifest from the gen file so HMR auto-propagates
|
|
174
154
|
const routerDir = dirname(routerEntry.sourceFile);
|
|
175
155
|
const routerBasename = basename(routerEntry.sourceFile).replace(
|
|
176
156
|
/\.(tsx?|jsx?)$/,
|
|
@@ -199,5 +179,5 @@ export function generatePerRouterModule(
|
|
|
199
179
|
`export const precomputedEntries = ${jsonParseExpression(entries)};`,
|
|
200
180
|
);
|
|
201
181
|
}
|
|
202
|
-
return lines.join("\n") || "
|
|
182
|
+
return lines.join("\n") || "";
|
|
203
183
|
}
|
package/src/vite/index.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bake the resolved INTERNAL_RANGO_DEBUG value into the router's `internal-debug`
|
|
3
|
+
* module so the flag reaches the CLIENT debug logs by just setting the env var.
|
|
4
|
+
*
|
|
5
|
+
* internal-debug.ts normally reads the flag via `typeof __RANGO_DEBUG__`, a Vite
|
|
6
|
+
* define. That delivery is unreliable on the client: in dev Vite ships the define
|
|
7
|
+
* only as an injected global whose presence varies across consumer setups, so the
|
|
8
|
+
* module can fall through to `process.env` (undefined in the browser) and the FE
|
|
9
|
+
* debug flag silently stays false while the server logs work. A Vite `transform`
|
|
10
|
+
* runs on the module regardless of how (or whether) the define is delivered, in
|
|
11
|
+
* both dev and build and for every environment, so the discovery plugin uses this
|
|
12
|
+
* to replace the module with the resolved literal.
|
|
13
|
+
*
|
|
14
|
+
* Returns null for any module that is not the router's internal-debug module.
|
|
15
|
+
*/
|
|
16
|
+
export function injectClientDebugFlag(
|
|
17
|
+
id: string,
|
|
18
|
+
): { code: string; map: null } | null {
|
|
19
|
+
// Cheap early-out: this hook runs on every module in every environment.
|
|
20
|
+
if (!id.includes("internal-debug")) return null;
|
|
21
|
+
const norm = id.replace(/\\/g, "/");
|
|
22
|
+
// Scope to the router's own internal-debug module: the published package
|
|
23
|
+
// (`/@rangojs/router/`, incl. pnpm's nested layout) or the monorepo workspace
|
|
24
|
+
// (`/packages/rangojs-router/`). The package-anchored path avoids matching a
|
|
25
|
+
// consumer file that merely sits under a directory named `rangojs-router`.
|
|
26
|
+
const isInternalDebug =
|
|
27
|
+
/\/internal-debug\.[cm]?[jt]sx?(\?|$)/.test(norm) &&
|
|
28
|
+
(norm.includes("/@rangojs/router/") ||
|
|
29
|
+
norm.includes("/packages/rangojs-router/"));
|
|
30
|
+
if (!isInternalDebug) return null;
|
|
31
|
+
// Emit the whole module: internal-debug.ts has a single export, kept in sync.
|
|
32
|
+
return {
|
|
33
|
+
code: `export const INTERNAL_RANGO_DEBUG = ${!!process.env.INTERNAL_RANGO_DEBUG};\n`,
|
|
34
|
+
map: null,
|
|
35
|
+
};
|
|
36
|
+
}
|
package/src/vite/plugin-types.ts
CHANGED
|
@@ -47,6 +47,64 @@ export type BuildEnvOption =
|
|
|
47
47
|
| Record<string, unknown>
|
|
48
48
|
| BuildEnvFactory;
|
|
49
49
|
|
|
50
|
+
// -- Client chunking --------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Metadata for one client ("use client") module, passed to a {@link ClientChunks}
|
|
54
|
+
* function. Mirrors the shape `@vitejs/plugin-rsc` passes to its own
|
|
55
|
+
* `clientChunks` option.
|
|
56
|
+
*/
|
|
57
|
+
export interface ClientChunkMeta {
|
|
58
|
+
/** Absolute module id of the "use client" file. */
|
|
59
|
+
id: string;
|
|
60
|
+
/** Normalized (posix) module id — convenient for path-based matching. */
|
|
61
|
+
normalizedId: string;
|
|
62
|
+
/**
|
|
63
|
+
* The RSC/server chunk that statically imports this client reference. This is
|
|
64
|
+
* the key used for the default grouping when no override is supplied: a single
|
|
65
|
+
* router that statically imports every route yields ONE `serverChunk`, hence
|
|
66
|
+
* one client chunk for all routes.
|
|
67
|
+
*/
|
|
68
|
+
serverChunk: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Controls how client ("use client") components are grouped into browser
|
|
73
|
+
* chunks, i.e. per-route / per-feature code splitting of the client bundle.
|
|
74
|
+
*
|
|
75
|
+
* Without splitting, a single router ships ONE client chunk containing every
|
|
76
|
+
* route's client components (and their CSS) — navigating to one route downloads
|
|
77
|
+
* every other route's client code. (Host sub-apps loaded via a dynamic `import()`
|
|
78
|
+
* are the exception: each forms its own chunk.) This option controls how that
|
|
79
|
+
* monolith is split.
|
|
80
|
+
*
|
|
81
|
+
* Behavior branches:
|
|
82
|
+
* - `true` / omitted (**default**, pre-1.0): Rango's built-in **directory
|
|
83
|
+
* strategy**. It splits app `"use client"` modules by **route id** — the segment
|
|
84
|
+
* after a route-root directory (`routes`, `app`, `pages`, `features`, `handlers`,
|
|
85
|
+
* …) — so `routes/dashboard/**` becomes `app-dashboard` at any nesting depth.
|
|
86
|
+
* Where it finds NO route structure (a flat `src/components/`, or host sub-apps
|
|
87
|
+
* already split by a dynamic `import()`), it inherits the default grouping
|
|
88
|
+
* unchanged — so the shared `src/components` chunk stays shared and host apps do
|
|
89
|
+
* not leak across each other. Shared runtime (React, the router, `node_modules`)
|
|
90
|
+
* is never split.
|
|
91
|
+
* - `false`: opt out — inherit `@vitejs/plugin-rsc`'s default grouping everywhere
|
|
92
|
+
* (one chunk per router / per host sub-app).
|
|
93
|
+
* - function: full override. Return a chunk group name, or `undefined` to fall
|
|
94
|
+
* back to the default grouping for that one module. Forwarded directly to
|
|
95
|
+
* `@vitejs/plugin-rsc`'s `clientChunks`.
|
|
96
|
+
*
|
|
97
|
+
* Every module maps to exactly one group, so there is no byte duplication: a
|
|
98
|
+
* component used by two routes lives in one group and is fetched whenever it
|
|
99
|
+
* renders. Put genuinely shared client components OUTSIDE route directories so
|
|
100
|
+
* they land in the shared group rather than one route's chunk.
|
|
101
|
+
*
|
|
102
|
+
* @default true
|
|
103
|
+
*/
|
|
104
|
+
export type ClientChunks =
|
|
105
|
+
| boolean
|
|
106
|
+
| ((meta: ClientChunkMeta) => string | undefined);
|
|
107
|
+
|
|
50
108
|
// -- Plugin options ---------------------------------------------------------
|
|
51
109
|
|
|
52
110
|
/**
|
|
@@ -60,16 +118,52 @@ interface RangoBaseOptions {
|
|
|
60
118
|
banner?: boolean;
|
|
61
119
|
|
|
62
120
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
121
|
+
* Group client ("use client") components into browser chunks for per-route /
|
|
122
|
+
* per-feature code splitting. On by default (pre-1.0); pass `false` to opt out.
|
|
123
|
+
* See {@link ClientChunks}.
|
|
66
124
|
*
|
|
67
|
-
*
|
|
68
|
-
|
|
125
|
+
* @default true
|
|
126
|
+
*/
|
|
127
|
+
clientChunks?: ClientChunks;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Filter which files route discovery scans, by glob. Paths are matched
|
|
131
|
+
* root-relative (e.g. `src/routes/**`). `include` restricts discovery to
|
|
132
|
+
* matching files; `exclude` removes matches (the defaults cover tests, dist,
|
|
133
|
+
* coverage, etc.). Mirrors the CLI's `--include`/`--exclude`.
|
|
69
134
|
*
|
|
70
|
-
* @
|
|
135
|
+
* @example
|
|
136
|
+
* rango({ discovery: { include: ["src/routes/**"] } })
|
|
71
137
|
*/
|
|
72
|
-
|
|
138
|
+
discovery?: {
|
|
139
|
+
include?: string[];
|
|
140
|
+
exclude?: string[];
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* What to do when a `Prerender` route's or `Static` handler's render throws at
|
|
145
|
+
* build time. Otherwise the route error boundary catches it and the rendered
|
|
146
|
+
* error page is baked as the artifact, then served as an HTTP 200 — a silent,
|
|
147
|
+
* user-visible breakage (issue #587). Independent of this setting, a render may
|
|
148
|
+
* `throw new Skip()` (from `@rangojs/router`) to skip a single URL/handler.
|
|
149
|
+
*
|
|
150
|
+
* - `"fail"` (**default**): fail the build, naming the URL/handler and the
|
|
151
|
+
* original render error.
|
|
152
|
+
* - `"warn"`: log a warning and skip baking the artifact — it is never served as a
|
|
153
|
+
* baked 200 error page. `"warn"` is a build-unblock, NOT a runtime contract for
|
|
154
|
+
* the skipped entry: the route falls through to normal resolution, which may
|
|
155
|
+
* render live (its handler is still bundled — e.g. when nothing else baked) or
|
|
156
|
+
* 404 (once prerender handler eviction has run for other baked entries), so the
|
|
157
|
+
* outcome depends on the rest of the build, and a skipped `Static()` handler's
|
|
158
|
+
* evicted code can surface as an error. For DEFINED runtime behavior use
|
|
159
|
+
* `Passthrough()` (a live fallback) or `throw new Skip()` (an intentional skip);
|
|
160
|
+
* otherwise prefer the default `"fail"`.
|
|
161
|
+
*
|
|
162
|
+
* @default "fail"
|
|
163
|
+
*/
|
|
164
|
+
prerender?: {
|
|
165
|
+
onError?: "fail" | "warn";
|
|
166
|
+
};
|
|
73
167
|
}
|
|
74
168
|
|
|
75
169
|
/**
|
|
@@ -80,6 +174,17 @@ export interface RangoNodeOptions extends RangoBaseOptions {
|
|
|
80
174
|
* Deployment preset. Defaults to 'node' when not specified.
|
|
81
175
|
*/
|
|
82
176
|
preset?: "node";
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Environment bindings available to Prerender and Static handlers at build
|
|
180
|
+
* time via `ctx.env`. Shared across all prerender invocations for the build.
|
|
181
|
+
*
|
|
182
|
+
* `"auto"` is Cloudflare-only (it resolves the wrangler platform proxy), so it
|
|
183
|
+
* is not accepted on the Node preset — pass an object or a factory instead.
|
|
184
|
+
*
|
|
185
|
+
* @default false
|
|
186
|
+
*/
|
|
187
|
+
buildEnv?: Exclude<BuildEnvOption, "auto">;
|
|
83
188
|
}
|
|
84
189
|
|
|
85
190
|
/**
|
|
@@ -89,12 +194,25 @@ export interface RangoCloudflareOptions extends RangoBaseOptions {
|
|
|
89
194
|
/**
|
|
90
195
|
* Deployment preset for Cloudflare Workers.
|
|
91
196
|
* When using cloudflare preset:
|
|
92
|
-
* - @vitejs/plugin-rsc
|
|
197
|
+
* - @vitejs/plugin-rsc IS still added by rango(), but with `serverHandler: false`
|
|
198
|
+
* (the cloudflare plugin owns the RSC worker/server entry); only `client` and
|
|
199
|
+
* `ssr` virtual entries are configured, no rsc entry
|
|
93
200
|
* - Your worker entry (e.g., worker.rsc.tsx) imports the router directly
|
|
94
201
|
* - Browser and SSR use virtual entries
|
|
95
202
|
* - Build-time manifest generation is auto-detected from the resolved RSC environment config
|
|
96
203
|
*/
|
|
97
204
|
preset: "cloudflare";
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Environment bindings available to Prerender and Static handlers at build
|
|
208
|
+
* time via `ctx.env`. Shared across all prerender invocations for the build.
|
|
209
|
+
*
|
|
210
|
+
* `"auto"` resolves the Cloudflare platform proxy via wrangler
|
|
211
|
+
* `getPlatformProxy()`.
|
|
212
|
+
*
|
|
213
|
+
* @default false
|
|
214
|
+
*/
|
|
215
|
+
buildEnv?: BuildEnvOption;
|
|
98
216
|
}
|
|
99
217
|
|
|
100
218
|
/**
|
|
@@ -1,86 +1,83 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
import { createRangoDebugger, NS } from "../debug.js";
|
|
3
|
+
|
|
4
|
+
const debug = createRangoDebugger(NS.transform);
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Transform CJS vendor files from @vitejs/plugin-rsc to ESM for browser compatibility.
|
|
5
8
|
* The react-server-dom vendor files are shipped as CJS which doesn't work in browsers.
|
|
6
9
|
*/
|
|
7
10
|
export function createCjsToEsmPlugin(): Plugin {
|
|
11
|
+
// Picked from Vite's resolved mode, not process.env.NODE_ENV, so the dev vs
|
|
12
|
+
// production vendor variant tracks the build mode the user actually ran.
|
|
13
|
+
let isProduction = false;
|
|
14
|
+
|
|
8
15
|
return {
|
|
9
16
|
name: "@rangojs/router:cjs-to-esm",
|
|
10
17
|
enforce: "pre",
|
|
18
|
+
configResolved(config) {
|
|
19
|
+
isProduction = config.isProduction;
|
|
20
|
+
},
|
|
11
21
|
transform(code, id) {
|
|
12
|
-
const cleanId = id.split("?")[0];
|
|
22
|
+
const cleanId = id.split("?")[0].replaceAll("\\", "/");
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
cleanId.includes("vendor/react-server-dom/client.browser.js") ||
|
|
17
|
-
cleanId.includes("vendor\\react-server-dom\\client.browser.js")
|
|
18
|
-
) {
|
|
19
|
-
const isProd = process.env.NODE_ENV === "production";
|
|
24
|
+
if (cleanId.includes("vendor/react-server-dom/client.browser.js")) {
|
|
25
|
+
const isProd = isProduction;
|
|
20
26
|
const cjsFile = isProd
|
|
21
27
|
? "./cjs/react-server-dom-webpack-client.browser.production.js"
|
|
22
28
|
: "./cjs/react-server-dom-webpack-client.browser.development.js";
|
|
23
29
|
|
|
30
|
+
debug?.("cjs-to-esm entry redirect %s", id);
|
|
24
31
|
return {
|
|
25
32
|
code: `export * from "${cjsFile}";`,
|
|
26
33
|
map: null,
|
|
27
34
|
};
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
// Transform the actual CJS files to ESM
|
|
31
37
|
if (
|
|
32
|
-
|
|
33
|
-
cleanId.includes("vendor\\react-server-dom\\cjs\\")) &&
|
|
38
|
+
cleanId.includes("vendor/react-server-dom/cjs/") &&
|
|
34
39
|
cleanId.includes("client.browser")
|
|
35
40
|
) {
|
|
36
41
|
let transformed = code;
|
|
37
42
|
|
|
38
|
-
// Extract the license comment to preserve it
|
|
39
43
|
const licenseMatch = transformed.match(/^\/\*\*[\s\S]*?\*\//);
|
|
40
44
|
const license = licenseMatch ? licenseMatch[0] : "";
|
|
41
45
|
if (license) {
|
|
42
46
|
transformed = transformed.slice(license.length);
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
// Remove "use strict" (both dev and prod have this)
|
|
46
49
|
transformed = transformed.replace(/^\s*["']use strict["'];\s*/, "");
|
|
47
50
|
|
|
48
|
-
// Remove the conditional IIFE wrapper (development only)
|
|
49
51
|
transformed = transformed.replace(
|
|
50
52
|
/^\s*["']production["']\s*!==\s*process\.env\.NODE_ENV\s*&&\s*\(function\s*\(\)\s*\{/,
|
|
51
53
|
"",
|
|
52
54
|
);
|
|
53
55
|
|
|
54
|
-
// Remove the closing of the conditional IIFE at the end (development only)
|
|
55
56
|
transformed = transformed.replace(/\}\)\(\);?\s*$/, "");
|
|
56
57
|
|
|
57
|
-
// Replace require('react') and require('react-dom') with imports (development)
|
|
58
58
|
transformed = transformed.replace(
|
|
59
59
|
/var\s+React\s*=\s*require\s*\(\s*["']react["']\s*\)\s*,[\s\n]+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
|
|
60
60
|
'import React from "react";\nimport ReactDOM from "react-dom";\nvar ',
|
|
61
61
|
);
|
|
62
62
|
|
|
63
|
-
// Replace require('react-dom') only (production - doesn't import React)
|
|
64
63
|
transformed = transformed.replace(
|
|
65
64
|
/var\s+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
|
|
66
65
|
'import ReactDOM from "react-dom";\nvar ',
|
|
67
66
|
);
|
|
68
67
|
|
|
69
|
-
// Transform exports.xyz = function() to export function xyz()
|
|
70
68
|
transformed = transformed.replace(
|
|
71
69
|
/exports\.(\w+)\s*=\s*function\s*\(/g,
|
|
72
70
|
"export function $1(",
|
|
73
71
|
);
|
|
74
72
|
|
|
75
|
-
// Transform exports.xyz = value to export const xyz = value
|
|
76
73
|
transformed = transformed.replace(
|
|
77
74
|
/exports\.(\w+)\s*=/g,
|
|
78
75
|
"export const $1 =",
|
|
79
76
|
);
|
|
80
77
|
|
|
81
|
-
// Reconstruct with license at the top
|
|
82
78
|
transformed = license + "\n" + transformed;
|
|
83
79
|
|
|
80
|
+
debug?.("cjs-to-esm body rewrite %s", id);
|
|
84
81
|
return {
|
|
85
82
|
code: transformed,
|
|
86
83
|
map: null,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Plugin, ResolvedConfig } from "vite";
|
|
2
|
+
import { createRangoDebugger, NS } from "../debug.js";
|
|
3
|
+
|
|
4
|
+
const debug = createRangoDebugger(NS.transform);
|
|
2
5
|
|
|
3
6
|
const CLIENT_IN_SERVER_PROXY_PREFIX =
|
|
4
7
|
"virtual:vite-rsc/client-in-server-package-proxy/";
|
|
@@ -62,6 +65,7 @@ export function extractPackageName(absolutePath: string): string | null {
|
|
|
62
65
|
*/
|
|
63
66
|
export function clientRefDedup(): Plugin {
|
|
64
67
|
let clientExclude: string[] = [];
|
|
68
|
+
const dedupedPackages = new Set<string>();
|
|
65
69
|
|
|
66
70
|
return {
|
|
67
71
|
name: "@rangojs/router:client-ref-dedup",
|
|
@@ -69,33 +73,35 @@ export function clientRefDedup(): Plugin {
|
|
|
69
73
|
apply: "serve",
|
|
70
74
|
|
|
71
75
|
configResolved(config: ResolvedConfig) {
|
|
72
|
-
// Respect user's optimizeDeps.exclude — if a package is explicitly
|
|
73
|
-
// excluded from pre-bundling, we shouldn't redirect it there.
|
|
74
76
|
const clientEnv = config.environments?.["client"];
|
|
75
77
|
clientExclude =
|
|
76
78
|
clientEnv?.optimizeDeps?.exclude ?? config.optimizeDeps?.exclude ?? [];
|
|
77
79
|
},
|
|
78
80
|
|
|
81
|
+
buildEnd() {
|
|
82
|
+
if (debug && dedupedPackages.size > 0) {
|
|
83
|
+
debug(
|
|
84
|
+
"client-ref-dedup: redirected %d package(s) (%s)",
|
|
85
|
+
dedupedPackages.size,
|
|
86
|
+
[...dedupedPackages].join(","),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
79
91
|
resolveId(source, importer, options) {
|
|
80
|
-
// Only intercept in the client environment
|
|
81
92
|
if (this.environment?.name !== "client") return;
|
|
82
93
|
|
|
83
|
-
// Only handle imports from client-in-server-package-proxy virtual modules
|
|
84
94
|
if (!importer?.includes(CLIENT_IN_SERVER_PROXY_PREFIX)) return;
|
|
85
95
|
|
|
86
|
-
// Only handle absolute node_modules paths
|
|
87
96
|
if (!source.includes("/node_modules/")) return;
|
|
88
97
|
|
|
89
|
-
// Must have an importer
|
|
90
|
-
if (!importer) return;
|
|
91
|
-
|
|
92
98
|
const packageName = extractPackageName(source);
|
|
93
99
|
if (!packageName) return;
|
|
94
100
|
|
|
95
|
-
// Don't redirect packages that are excluded from optimization
|
|
96
101
|
if (clientExclude.includes(packageName)) return;
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
if (debug) dedupedPackages.add(packageName);
|
|
104
|
+
|
|
99
105
|
return `\0rango:dedup/${packageName}`;
|
|
100
106
|
},
|
|
101
107
|
|
|
@@ -104,7 +110,6 @@ export function clientRefDedup(): Plugin {
|
|
|
104
110
|
|
|
105
111
|
const packageName = id.slice("\0rango:dedup/".length);
|
|
106
112
|
|
|
107
|
-
// Re-export via bare specifier so Vite routes through pre-bundling
|
|
108
113
|
return [
|
|
109
114
|
`export * from ${JSON.stringify(packageName)};`,
|
|
110
115
|
`import * as __all__ from ${JSON.stringify(packageName)};`,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
import { relative } from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
+
import { createRangoDebugger, createCounter, NS } from "../debug.js";
|
|
5
|
+
|
|
6
|
+
const debug = createRangoDebugger(NS.transform);
|
|
4
7
|
|
|
5
|
-
// Dev-mode client-reference key prefixes emitted by @vitejs/plugin-rsc
|
|
6
8
|
const CLIENT_PKG_PROXY_PREFIX =
|
|
7
9
|
"/@id/__x00__virtual:vite-rsc/client-package-proxy/";
|
|
8
10
|
const CLIENT_IN_SERVER_PKG_PROXY_PREFIX =
|
|
@@ -19,6 +21,17 @@ const FS_PREFIX = "/@fs/";
|
|
|
19
21
|
* Returns the input unchanged if it doesn't match a known dev-mode pattern
|
|
20
22
|
* (e.g., already a production hash).
|
|
21
23
|
*/
|
|
24
|
+
/**
|
|
25
|
+
* The production client-reference key hash: `sha256(relativeId).slice(0,12)`,
|
|
26
|
+
* matching @vitejs/plugin-rsc's `hashString`. Exported so the client-chunks
|
|
27
|
+
* strategy can hash a `clientChunks` callback's `meta.normalizedId` (already the
|
|
28
|
+
* project-root-relative id) and compare it against fallback hashes collected
|
|
29
|
+
* during discovery.
|
|
30
|
+
*/
|
|
31
|
+
export function hashRefKey(relativeId: string): string {
|
|
32
|
+
return createHash("sha256").update(relativeId).digest("hex").slice(0, 12);
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
export function computeProductionHash(
|
|
23
36
|
projectRoot: string,
|
|
24
37
|
refKey: string,
|
|
@@ -26,32 +39,24 @@ export function computeProductionHash(
|
|
|
26
39
|
let toHash: string;
|
|
27
40
|
|
|
28
41
|
if (refKey.startsWith(CLIENT_PKG_PROXY_PREFIX)) {
|
|
29
|
-
// /@id/__x00__virtual:vite-rsc/client-package-proxy/<pkg> -> hash("<pkg>")
|
|
30
42
|
toHash = refKey.slice(CLIENT_PKG_PROXY_PREFIX.length);
|
|
31
43
|
} else if (refKey.startsWith(CLIENT_IN_SERVER_PKG_PROXY_PREFIX)) {
|
|
32
|
-
// /@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/<encodedAbsPath>
|
|
33
44
|
const absPath = decodeURIComponent(
|
|
34
45
|
refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length),
|
|
35
46
|
);
|
|
36
47
|
toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
|
|
37
48
|
} else if (refKey.startsWith(FS_PREFIX)) {
|
|
38
|
-
// /@fs/abs/path.tsx -> hash(relative(root, "/abs/path.tsx"))
|
|
39
49
|
const absPath = refKey.slice(FS_PREFIX.length - 1); // keep leading /
|
|
40
50
|
toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
|
|
41
51
|
} else if (refKey.startsWith("/")) {
|
|
42
|
-
// /src/Button.tsx -> hash("src/Button.tsx")
|
|
43
52
|
toHash = refKey.slice(1);
|
|
44
53
|
} else {
|
|
45
|
-
// Already hashed or unknown format — return unchanged
|
|
46
54
|
return refKey;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
return
|
|
57
|
+
return hashRefKey(toHash);
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
// Regex to match registerClientReference() calls as emitted by @vitejs/plugin-rsc.
|
|
53
|
-
// Captures the reference key (second argument) from the call.
|
|
54
|
-
// Handles two proxy forms: parenthesized expression `(expr)` and arrow-throw `() => { ... }`.
|
|
55
60
|
const REGISTER_CLIENT_REF_RE =
|
|
56
61
|
/registerClientReference\(\s*(?:(?:\([^)]*\))|(?:\(\)[\s\S]*?\}))\s*,\s*"([^"]+)"\s*,\s*"[^"]+"\s*\)/g;
|
|
57
62
|
|
|
@@ -89,17 +94,25 @@ export function transformClientRefs(
|
|
|
89
94
|
* regex replacement of Flight payloads.
|
|
90
95
|
*/
|
|
91
96
|
export function hashClientRefs(projectRoot: string): Plugin {
|
|
97
|
+
const counter = createCounter(debug, "hash-client-refs");
|
|
92
98
|
return {
|
|
93
99
|
name: "@rangojs/router:hash-client-refs",
|
|
94
|
-
// Run after the RSC plugin's transform (default enforce is normal)
|
|
95
100
|
enforce: "post",
|
|
96
101
|
applyToEnvironment(env) {
|
|
97
102
|
return env.name === "rsc";
|
|
98
103
|
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
buildEnd() {
|
|
105
|
+
counter?.flush();
|
|
106
|
+
},
|
|
107
|
+
transform(code, id) {
|
|
108
|
+
const start = counter ? performance.now() : 0;
|
|
109
|
+
try {
|
|
110
|
+
const result = transformClientRefs(code, projectRoot);
|
|
111
|
+
if (result === null) return;
|
|
112
|
+
return { code: result, map: null };
|
|
113
|
+
} finally {
|
|
114
|
+
counter?.record(id, performance.now() - start);
|
|
115
|
+
}
|
|
103
116
|
},
|
|
104
117
|
};
|
|
105
118
|
}
|
|
@@ -32,14 +32,6 @@ const IMPORT_NODE_TYPES = new Set([
|
|
|
32
32
|
"ExportAllDeclaration",
|
|
33
33
|
]);
|
|
34
34
|
|
|
35
|
-
// Keep in sync with `STUBS` in cloudflare-protocol-loader-hook.mjs —
|
|
36
|
-
// both paths (Vite transform and Node loader) need to hand out the same
|
|
37
|
-
// classes. Unknown `cloudflare:*` modules fall back to an empty default
|
|
38
|
-
// export so third-party packages (e.g. the Cloudflare Agents SDK) can
|
|
39
|
-
// pull them into the graph without crashing discovery. Discovery only
|
|
40
|
-
// evaluates module top-level code — no handlers run — so missing named
|
|
41
|
-
// exports only fail if something does `class X extends Missing {}` at
|
|
42
|
-
// module scope, which is rare outside the already-stubbed classes.
|
|
43
35
|
const STUBS: Record<string, string> = {
|
|
44
36
|
"cloudflare:workers": `
|
|
45
37
|
export class DurableObject { constructor(_ctx, _env) {} }
|
|
@@ -65,15 +57,6 @@ export default {};
|
|
|
65
57
|
`,
|
|
66
58
|
};
|
|
67
59
|
|
|
68
|
-
// Policy: unknown `cloudflare:*` specifiers resolve permissively (empty
|
|
69
|
-
// default export) rather than throwing. We prioritize dependency-graph
|
|
70
|
-
// resilience over strict validation of user imports because third-party
|
|
71
|
-
// packages can pull `cloudflare:*` modules we haven't curated, and
|
|
72
|
-
// discovery should not fail just because those modules appear in the graph.
|
|
73
|
-
// Tradeoff: unsupported user-authored `cloudflare:*` imports may fail later
|
|
74
|
-
// with a generic JS/module error instead of a tailored rango-branded hint.
|
|
75
|
-
// The test below pins this behavior so dependency compatibility is not
|
|
76
|
-
// regressed accidentally.
|
|
77
60
|
const FALLBACK_STUB = `export default {};\n`;
|
|
78
61
|
|
|
79
62
|
interface AstNode {
|
|
@@ -139,7 +122,7 @@ export function createCloudflareProtocolStubPlugin(): Plugin {
|
|
|
139
122
|
|
|
140
123
|
let ast: AstNode;
|
|
141
124
|
try {
|
|
142
|
-
ast = this.parse(code) as unknown as AstNode;
|
|
125
|
+
ast = this.parse(code, { lang: "tsx" }) as unknown as AstNode;
|
|
143
126
|
} catch {
|
|
144
127
|
// Malformed source — let a downstream plugin surface the parse error.
|
|
145
128
|
return null;
|
|
@@ -163,9 +146,6 @@ export function createCloudflareProtocolStubPlugin(): Plugin {
|
|
|
163
146
|
|
|
164
147
|
if (hits.length === 0) return null;
|
|
165
148
|
|
|
166
|
-
// Rewrite from last to first so earlier offsets stay valid. `start`/
|
|
167
|
-
// `end` span the full literal including quotes, so we re-emit the
|
|
168
|
-
// same quote character around the new specifier.
|
|
169
149
|
hits.sort((a, b) => b.start - a.start);
|
|
170
150
|
let out = code;
|
|
171
151
|
for (const hit of hits) {
|