@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,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel Build Output (Build Output API v3) emitter for `preset: "vercel"`.
|
|
3
|
+
*
|
|
4
|
+
* After the full app build, restructures dist/ into .vercel/output:
|
|
5
|
+
*
|
|
6
|
+
* .vercel/output/
|
|
7
|
+
* config.json routing: static first, else the function
|
|
8
|
+
* static/ dist/client (browser assets, served at /)
|
|
9
|
+
* functions/<name>.func/
|
|
10
|
+
* .vc-config.json Node serverless, response streaming
|
|
11
|
+
* index.mjs bundled launcher (srvx + @vercel/functions)
|
|
12
|
+
* rsc/ dist/rsc (self-contained RSC server bundle)
|
|
13
|
+
* ssr/ dist/ssr (rsc imports ../ssr/index.js)
|
|
14
|
+
*
|
|
15
|
+
* A prebuilt .vercel/output gets no `npm install`, so everything the function
|
|
16
|
+
* imports must physically live inside the .func directory. This relies on two
|
|
17
|
+
* things the vercel preset arranges (each is a failure only a real deploy — or
|
|
18
|
+
* the isolated smoke test — catches, since a local in-place run is masked by the
|
|
19
|
+
* app's own package.json + node_modules up the tree):
|
|
20
|
+
*
|
|
21
|
+
* 1. The rsc/ssr builds are fully bundled (`resolve.noExternal`, set in
|
|
22
|
+
* rango.ts for this preset). The node default externalizes node_modules
|
|
23
|
+
* deps, which works under `vite preview` but leaves bare imports
|
|
24
|
+
* (@vercel/functions, react-dom/server.edge, ...) that have no node_modules
|
|
25
|
+
* to resolve against on Vercel.
|
|
26
|
+
* 2. A `package.json` with `"type": "module"` is written into the .func dir.
|
|
27
|
+
* The rsc/ssr bundles are ESM but use a `.js` extension; without a
|
|
28
|
+
* type:module in scope the deployed (isolated) function loads them as
|
|
29
|
+
* CommonJS and fails on the first `import`.
|
|
30
|
+
*
|
|
31
|
+
* The launcher is bundled with srvx (the Web->Node streaming bridge, a
|
|
32
|
+
* @rangojs/router dependency) and @vercel/functions (resolved from the app)
|
|
33
|
+
* inlined, keeping the RSC bundle a runtime-relative external.
|
|
34
|
+
*
|
|
35
|
+
* Timing: this runs in the `buildApp` hook (order "post"), which fires once
|
|
36
|
+
* after every environment has built, so dist/{client,rsc,ssr} all exist.
|
|
37
|
+
* closeBundle is unusable here — it fires per environment, and twice for ssr
|
|
38
|
+
* (the server-reference scan and the real build), so it would run before
|
|
39
|
+
* dist/client exists. rango's own prerender/static emitters hardcode dist/rsc,
|
|
40
|
+
* so we build to dist/ and restructure here rather than retargeting outDir.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import type { Plugin } from "vite";
|
|
44
|
+
import { rm, mkdir, cp, writeFile } from "node:fs/promises";
|
|
45
|
+
import { existsSync } from "node:fs";
|
|
46
|
+
import { resolve, join } from "node:path";
|
|
47
|
+
import { createRequire } from "node:module";
|
|
48
|
+
import { pathToFileURL } from "node:url";
|
|
49
|
+
import type { RangoVercelOptions } from "../plugin-types.js";
|
|
50
|
+
|
|
51
|
+
// Minimal structural types for the esbuild API we use, resolved dynamically from
|
|
52
|
+
// the app so @rangojs/router does not depend on esbuild's type package.
|
|
53
|
+
interface EsbuildPluginBuild {
|
|
54
|
+
onResolve(
|
|
55
|
+
options: { filter: RegExp },
|
|
56
|
+
callback: () => { path: string; external: boolean },
|
|
57
|
+
): void;
|
|
58
|
+
}
|
|
59
|
+
type EsbuildBuild = (options: Record<string, unknown>) => Promise<unknown>;
|
|
60
|
+
interface EsbuildModule {
|
|
61
|
+
build?: EsbuildBuild;
|
|
62
|
+
default?: { build?: EsbuildBuild };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const LAUNCHER_SOURCE = `import { toNodeHandler } from "srvx/node";
|
|
66
|
+
import { waitUntil } from "@vercel/functions";
|
|
67
|
+
import rscHandler from "./rsc/index.js";
|
|
68
|
+
|
|
69
|
+
// The Vercel Node launcher invokes a Node (req, res) handler, not a Web fetch
|
|
70
|
+
// handler. srvx's toNodeHandler bridges the Rango Web fetch handler and pipes
|
|
71
|
+
// the streamed Response to the Node response (set supportsResponseStreaming).
|
|
72
|
+
const onVercel = Boolean(process.env.VERCEL);
|
|
73
|
+
|
|
74
|
+
const fetchHandler = (request) =>
|
|
75
|
+
rscHandler(request, {
|
|
76
|
+
env: process.env,
|
|
77
|
+
// Forward Vercel's waitUntil so cache writes / revalidation run off the
|
|
78
|
+
// response path. Omitted off-platform so those writes settle inline.
|
|
79
|
+
ctx: onVercel ? { waitUntil } : undefined,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export default toNodeHandler(fetchHandler);
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
async function assemble(
|
|
86
|
+
root: string,
|
|
87
|
+
options: RangoVercelOptions,
|
|
88
|
+
): Promise<void> {
|
|
89
|
+
const dist = join(root, "dist");
|
|
90
|
+
for (const dir of ["client", "rsc", "ssr"]) {
|
|
91
|
+
if (!existsSync(join(dist, dir))) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`[rango] preset "vercel": missing dist/${dir}. Run the production build first.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const vercel = options.vercel ?? {};
|
|
99
|
+
const functionName = vercel.functionName ?? "index";
|
|
100
|
+
const out = join(root, ".vercel", "output");
|
|
101
|
+
const funcDir = join(out, "functions", `${functionName}.func`);
|
|
102
|
+
|
|
103
|
+
await rm(out, { recursive: true, force: true });
|
|
104
|
+
await mkdir(funcDir, { recursive: true });
|
|
105
|
+
|
|
106
|
+
// 1. Static client assets -> served from the CDN at the root.
|
|
107
|
+
await cp(join(dist, "client"), join(out, "static"), { recursive: true });
|
|
108
|
+
|
|
109
|
+
// 2. Server bundle into the function. Preserve the rsc -> ../ssr/index.js
|
|
110
|
+
// relative import by mirroring the dist/{rsc,ssr} layout inside the func.
|
|
111
|
+
await cp(join(dist, "rsc"), join(funcDir, "rsc"), { recursive: true });
|
|
112
|
+
await cp(join(dist, "ssr"), join(funcDir, "ssr"), { recursive: true });
|
|
113
|
+
|
|
114
|
+
// Prerender/static payload manifests (present only when routes are prerendered).
|
|
115
|
+
if (existsSync(join(dist, "static"))) {
|
|
116
|
+
await cp(join(dist, "static"), join(funcDir, "static"), {
|
|
117
|
+
recursive: true,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 3. Bundle the Node launcher. srvx (a @rangojs/router dependency) is aliased
|
|
122
|
+
// to its resolved path; @vercel/functions resolves from the app; the RSC
|
|
123
|
+
// server bundle stays a runtime-relative external.
|
|
124
|
+
const rangoRequire = createRequire(import.meta.url);
|
|
125
|
+
let srvxNodePath: string;
|
|
126
|
+
try {
|
|
127
|
+
srvxNodePath = rangoRequire.resolve("srvx/node");
|
|
128
|
+
} catch {
|
|
129
|
+
throw new Error(
|
|
130
|
+
'[rango] preset "vercel" requires "srvx" (a dependency of @rangojs/router). Reinstall dependencies.',
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// esbuild ships with Vite; resolve it from the app so we never add it as a
|
|
135
|
+
// @rangojs/router dependency. Minimal structural types avoid coupling to
|
|
136
|
+
// esbuild's type package at compile time.
|
|
137
|
+
const appRequire = createRequire(join(root, "package.json"));
|
|
138
|
+
let esbuildModule: EsbuildModule;
|
|
139
|
+
try {
|
|
140
|
+
esbuildModule = (await import(
|
|
141
|
+
pathToFileURL(appRequire.resolve("esbuild")).href
|
|
142
|
+
)) as EsbuildModule;
|
|
143
|
+
} catch {
|
|
144
|
+
throw new Error(
|
|
145
|
+
'[rango] preset "vercel" requires "esbuild" to bundle the function launcher. It ships with Vite; reinstall dependencies.',
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
const esbuildBuild = esbuildModule.build ?? esbuildModule.default?.build;
|
|
149
|
+
if (typeof esbuildBuild !== "function") {
|
|
150
|
+
throw new Error('[rango] preset "vercel": could not load esbuild.build.');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await esbuildBuild({
|
|
155
|
+
stdin: {
|
|
156
|
+
contents: LAUNCHER_SOURCE,
|
|
157
|
+
resolveDir: root,
|
|
158
|
+
sourcefile: "func-entry.mjs",
|
|
159
|
+
loader: "js",
|
|
160
|
+
},
|
|
161
|
+
outfile: join(funcDir, "index.mjs"),
|
|
162
|
+
bundle: true,
|
|
163
|
+
format: "esm",
|
|
164
|
+
platform: "node",
|
|
165
|
+
target: "node18",
|
|
166
|
+
alias: { "srvx/node": srvxNodePath },
|
|
167
|
+
plugins: [
|
|
168
|
+
{
|
|
169
|
+
name: "external-rsc-entry",
|
|
170
|
+
setup(b: EsbuildPluginBuild) {
|
|
171
|
+
b.onResolve({ filter: /^\.\/rsc\/index\.js$/ }, () => ({
|
|
172
|
+
path: "./rsc/index.js",
|
|
173
|
+
external: true,
|
|
174
|
+
}));
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
181
|
+
if (/@vercel\/functions/.test(message)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
'[rango] preset "vercel": could not resolve "@vercel/functions". Add it to your app dependencies (it also backs VercelCacheStore).\n' +
|
|
184
|
+
message,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 3b. Mark the function as ESM. The rsc/ssr bundles are .js ESM files with no
|
|
191
|
+
// package.json in scope on the deployed function (it is isolated at
|
|
192
|
+
// /var/task), so Node would load them as CommonJS and fail on `import`.
|
|
193
|
+
// Locally this is masked because the func inherits the app's
|
|
194
|
+
// "type": "module" up the tree; the deployed func has nothing above it.
|
|
195
|
+
await writeFile(
|
|
196
|
+
join(funcDir, "package.json"),
|
|
197
|
+
JSON.stringify({ type: "module" }, null, 2) + "\n",
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// 4. Function config: Node serverless with response streaming.
|
|
201
|
+
const vcConfig: Record<string, unknown> = {
|
|
202
|
+
runtime: vercel.runtime ?? "nodejs22.x",
|
|
203
|
+
handler: "index.mjs",
|
|
204
|
+
launcherType: "Nodejs",
|
|
205
|
+
shouldAddHelpers: false,
|
|
206
|
+
supportsResponseStreaming: true,
|
|
207
|
+
maxDuration: vercel.maxDuration ?? 30,
|
|
208
|
+
};
|
|
209
|
+
if (vercel.memory != null) vcConfig.memory = vercel.memory;
|
|
210
|
+
if (vercel.regions != null) vcConfig.regions = vercel.regions;
|
|
211
|
+
await writeFile(
|
|
212
|
+
join(funcDir, ".vc-config.json"),
|
|
213
|
+
JSON.stringify(vcConfig, null, 2) + "\n",
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// 5. Routing: filesystem (static/) first, then everything to the function.
|
|
217
|
+
await writeFile(
|
|
218
|
+
join(out, "config.json"),
|
|
219
|
+
JSON.stringify(
|
|
220
|
+
{
|
|
221
|
+
version: 3,
|
|
222
|
+
routes: [
|
|
223
|
+
{ handle: "filesystem" },
|
|
224
|
+
{ src: "/(.*)", dest: `/${functionName}` },
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
null,
|
|
228
|
+
2,
|
|
229
|
+
) + "\n",
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
console.log(
|
|
233
|
+
`[rango] assembled .vercel/output (function: ${functionName}.func)`,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function createVercelOutputPlugin(options: RangoVercelOptions): Plugin {
|
|
238
|
+
let root = process.cwd();
|
|
239
|
+
let isBuild = false;
|
|
240
|
+
return {
|
|
241
|
+
name: "@rangojs/router:vercel-output",
|
|
242
|
+
configResolved(config) {
|
|
243
|
+
root = resolve(config.root);
|
|
244
|
+
isBuild = config.command === "build";
|
|
245
|
+
},
|
|
246
|
+
// buildApp runs once after the whole multi-environment build (rsc, client,
|
|
247
|
+
// ssr), so dist/ is complete here. closeBundle is unusable for this: it
|
|
248
|
+
// fires per environment, and twice for ssr (the server-reference scan and
|
|
249
|
+
// the real build), so it would run before dist/client exists.
|
|
250
|
+
buildApp: {
|
|
251
|
+
order: "post",
|
|
252
|
+
async handler() {
|
|
253
|
+
if (!isBuild) return;
|
|
254
|
+
await assemble(root, options);
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
@@ -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
|
}
|
|
@@ -39,7 +29,6 @@ export function createVersionInjectorPlugin(
|
|
|
39
29
|
|
|
40
30
|
transform(code, id) {
|
|
41
31
|
if (!resolvedEntryPath) return null;
|
|
42
|
-
// Only transform the RSC entry file
|
|
43
32
|
const normalizedId = Vite.normalizePath(id);
|
|
44
33
|
const normalizedEntry = Vite.normalizePath(resolvedEntryPath);
|
|
45
34
|
|
|
@@ -47,16 +36,11 @@ export function createVersionInjectorPlugin(
|
|
|
47
36
|
return null;
|
|
48
37
|
}
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let newCode = code;
|
|
54
|
-
|
|
55
|
-
if (!code.includes("virtual:rsc-router/routes-manifest")) {
|
|
56
|
-
prepend.push(`import "virtual:rsc-router/routes-manifest";`);
|
|
57
|
-
}
|
|
39
|
+
const prepend: string[] = [
|
|
40
|
+
`import "virtual:rsc-router/routes-manifest";`,
|
|
41
|
+
];
|
|
58
42
|
|
|
59
|
-
|
|
43
|
+
let newCode = code;
|
|
60
44
|
const needsVersion =
|
|
61
45
|
code.includes("createRSCHandler") &&
|
|
62
46
|
!code.includes("@rangojs/router:version") &&
|
|
@@ -70,9 +54,21 @@ export function createVersionInjectorPlugin(
|
|
|
70
54
|
);
|
|
71
55
|
}
|
|
72
56
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
const lines = newCode.split("\n");
|
|
58
|
+
let insertAt = 0;
|
|
59
|
+
while (insertAt < lines.length) {
|
|
60
|
+
const trimmed = lines[insertAt]!.trim();
|
|
61
|
+
if (trimmed === "" || /^\/\/\/\s*<reference\b/.test(trimmed)) {
|
|
62
|
+
insertAt++;
|
|
63
|
+
} else {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
newCode = [
|
|
68
|
+
...lines.slice(0, insertAt),
|
|
69
|
+
...prepend,
|
|
70
|
+
...lines.slice(insertAt),
|
|
71
|
+
].join("\n");
|
|
76
72
|
|
|
77
73
|
return {
|
|
78
74
|
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,11 +133,13 @@ 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
|
|
|
139
|
+
let versionCounter = 0;
|
|
138
140
|
const bumpVersion = (reason: string) => {
|
|
139
|
-
currentVersion = Date.now().toString(16);
|
|
140
|
-
console.log(`[
|
|
141
|
+
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
142
|
+
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
141
143
|
|
|
142
144
|
const rscEnv = server?.environments?.rsc;
|
|
143
145
|
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
@@ -154,6 +156,9 @@ export function createVersionPlugin(): Plugin {
|
|
|
154
156
|
|
|
155
157
|
configResolved(config) {
|
|
156
158
|
isDev = config.command === "serve";
|
|
159
|
+
resolvedCacheDir = config.cacheDir
|
|
160
|
+
? String(config.cacheDir).replace(/\\/g, "/")
|
|
161
|
+
: undefined;
|
|
157
162
|
},
|
|
158
163
|
|
|
159
164
|
configureServer(devServer) {
|
|
@@ -200,17 +205,22 @@ export function createVersionPlugin(): Plugin {
|
|
|
200
205
|
return null;
|
|
201
206
|
},
|
|
202
207
|
|
|
203
|
-
// Track RSC module changes and update version
|
|
204
208
|
async hotUpdate(ctx) {
|
|
205
209
|
if (!isDev) return;
|
|
206
210
|
|
|
207
|
-
// Check if this is an RSC environment update (not client/ssr)
|
|
208
|
-
// RSC modules affect server-rendered content and cached payloads
|
|
209
|
-
// In Vite 6, environment is accessed via `this.environment`
|
|
210
211
|
const isRscModule = this.environment?.name === "rsc";
|
|
211
212
|
|
|
212
213
|
if (!isRscModule) return;
|
|
213
214
|
|
|
215
|
+
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
ctx.modules.length === 1 &&
|
|
219
|
+
ctx.modules[0].id === "\0" + VIRTUAL_IDS.version
|
|
220
|
+
) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
214
224
|
if (isCodeModule(ctx.file)) {
|
|
215
225
|
const filePath = normalizeModuleId(ctx.file);
|
|
216
226
|
const previousSignature = clientModuleSignatures.get(filePath);
|
|
@@ -218,9 +228,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
218
228
|
const source = await ctx.read();
|
|
219
229
|
const nextSignature = getClientModuleSignature(source);
|
|
220
230
|
if (nextSignature) {
|
|
221
|
-
// "use client" file — compare export signatures.
|
|
222
|
-
// client-component-hmr may have cleared ctx.modules, so we
|
|
223
|
-
// cannot rely on ctx.modules.length for these files.
|
|
224
231
|
clientModuleSignatures.set(filePath, nextSignature);
|
|
225
232
|
if (
|
|
226
233
|
previousSignature &&
|
|
@@ -231,20 +238,11 @@ export function createVersionPlugin(): Plugin {
|
|
|
231
238
|
} else {
|
|
232
239
|
clientModuleSignatures.delete(filePath);
|
|
233
240
|
if (!previousSignature) {
|
|
234
|
-
// Not and never was "use client" — use module graph check.
|
|
235
|
-
// ctx.modules is reliable for pure server files (only
|
|
236
|
-
// client-component-hmr clears it for "use client" modules).
|
|
237
241
|
if (ctx.modules.length === 0) return;
|
|
238
242
|
}
|
|
239
|
-
// Was "use client" but directive removed — boundary changed,
|
|
240
|
-
// bump below.
|
|
241
243
|
}
|
|
242
|
-
} catch {
|
|
243
|
-
// Fail open: if we can't read or parse the update, invalidate.
|
|
244
|
-
}
|
|
244
|
+
} catch {}
|
|
245
245
|
} else {
|
|
246
|
-
// Non-code file (json, css, etc.) — only bump if it's actually
|
|
247
|
-
// referenced by the RSC module graph.
|
|
248
246
|
if (ctx.modules.length === 0) return;
|
|
249
247
|
}
|
|
250
248
|
|
|
@@ -252,3 +250,26 @@ export function createVersionPlugin(): Plugin {
|
|
|
252
250
|
},
|
|
253
251
|
};
|
|
254
252
|
}
|
|
253
|
+
|
|
254
|
+
export function isViteDepCachePath(
|
|
255
|
+
filePath: string | undefined,
|
|
256
|
+
cacheDir?: string,
|
|
257
|
+
): boolean {
|
|
258
|
+
if (!filePath) return false;
|
|
259
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
260
|
+
|
|
261
|
+
if (cacheDir) {
|
|
262
|
+
const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
263
|
+
if (
|
|
264
|
+
normalized === normalizedCacheDir ||
|
|
265
|
+
normalized.startsWith(normalizedCacheDir + "/")
|
|
266
|
+
) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
/\/node_modules\/\.vite[^/]*\//.test(normalized) ||
|
|
273
|
+
normalized.includes("/.vite-isolated/")
|
|
274
|
+
);
|
|
275
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default virtual entry file contents for rsc-router.
|
|
3
|
-
* These are used when users don't provide their own entry files.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export const VIRTUAL_ENTRY_BROWSER: string = `
|
|
7
2
|
import {
|
|
8
3
|
createFromReadableStream,
|
|
@@ -14,7 +9,7 @@ import {
|
|
|
14
9
|
import { createElement, StrictMode } from "react";
|
|
15
10
|
import { hydrateRoot } from "react-dom/client";
|
|
16
11
|
import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
|
|
17
|
-
import { initBrowserApp,
|
|
12
|
+
import { initBrowserApp, Rango } from "@rangojs/router/browser";
|
|
18
13
|
|
|
19
14
|
async function initializeApp() {
|
|
20
15
|
const deps = {
|
|
@@ -29,7 +24,7 @@ async function initializeApp() {
|
|
|
29
24
|
|
|
30
25
|
hydrateRoot(
|
|
31
26
|
document,
|
|
32
|
-
createElement(StrictMode, null, createElement(
|
|
27
|
+
createElement(StrictMode, null, createElement(Rango))
|
|
33
28
|
);
|
|
34
29
|
}
|
|
35
30
|
|
|
@@ -51,9 +46,6 @@ export const renderHTML = createSSRHandler({
|
|
|
51
46
|
});
|
|
52
47
|
`.trim();
|
|
53
48
|
|
|
54
|
-
/**
|
|
55
|
-
* Generate the RSC entry content with the specified router path
|
|
56
|
-
*/
|
|
57
49
|
export function getVirtualEntryRSC(routerPath: string): string {
|
|
58
50
|
return `
|
|
59
51
|
import {
|
|
@@ -104,9 +96,6 @@ export default function handler(request, env) {
|
|
|
104
96
|
`.trim();
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
/**
|
|
108
|
-
* Virtual module IDs
|
|
109
|
-
*/
|
|
110
99
|
export const VIRTUAL_IDS = {
|
|
111
100
|
browser: "virtual:rsc-router/entry.browser.js",
|
|
112
101
|
ssr: "virtual:rsc-router/entry.ssr.js",
|
|
@@ -114,10 +103,6 @@ export const VIRTUAL_IDS = {
|
|
|
114
103
|
version: "@rangojs/router:version",
|
|
115
104
|
} as const;
|
|
116
105
|
|
|
117
|
-
/**
|
|
118
|
-
* Virtual module content for version.
|
|
119
|
-
* Exports VERSION - a timestamp that changes on server restart (dev) or at build time (production).
|
|
120
|
-
*/
|
|
121
106
|
export function getVirtualVersionContent(version: string): string {
|
|
122
107
|
return `export const VERSION = ${JSON.stringify(version)};`;
|
|
123
108
|
}
|