@rangojs/router 0.0.0-experimental.97 → 0.0.0-experimental.98914650
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/dist/bin/rango.js +157 -63
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +1584 -639
- package/package.json +71 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +60 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +222 -30
- package/skills/caching/SKILL.md +263 -8
- 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 +3 -1
- package/skills/hooks/SKILL.md +235 -28
- package/skills/host-router/SKILL.md +122 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +29 -5
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +173 -17
- package/skills/loader/SKILL.md +170 -23
- package/skills/middleware/SKILL.md +16 -10
- package/skills/migrate-nextjs/SKILL.md +38 -16
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +11 -7
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +250 -25
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +114 -47
- package/skills/route/SKILL.md +42 -5
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +78 -42
- 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 +316 -26
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/vercel/SKILL.md +107 -0
- 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 +0 -65
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +37 -143
- 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 +30 -59
- package/src/browser/navigation-client.ts +96 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +32 -82
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/partial-update.ts +60 -127
- package/src/browser/prefetch/cache.ts +82 -72
- package/src/browser/prefetch/fetch.ts +108 -33
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +41 -48
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- 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 +17 -14
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- 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 +20 -5
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +70 -34
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +168 -44
- package/src/browser/types.ts +36 -21
- 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 +3 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +89 -10
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- 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 +122 -22
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +134 -32
- package/src/cache/cache-scope.ts +100 -74
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2255 -238
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +61 -20
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +22 -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/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 +25 -61
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +17 -5
- 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 +31 -23
- 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 +63 -9
- package/src/index.ts +64 -9
- 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-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +9 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +244 -281
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +40 -17
- package/src/route-definition/redirect.ts +43 -9
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +15 -15
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +4 -41
- package/src/router/intercept-resolution.ts +14 -19
- package/src/router/lazy-includes.ts +9 -46
- package/src/router/loader-resolution.ts +91 -46
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +57 -58
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +150 -271
- package/src/router/match-middleware/cache-store.ts +3 -33
- 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 +31 -80
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +5 -112
- package/src/router/middleware.ts +118 -133
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +62 -67
- package/src/router/prerender-match.ts +99 -63
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +50 -56
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +68 -35
- package/src/router/router-options.ts +55 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +44 -63
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +40 -37
- package/src/router/segment-resolution/revalidation.ts +203 -285
- 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 +0 -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 +87 -48
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +80 -41
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +83 -78
- package/src/rsc/helpers.ts +93 -5
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +12 -1
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +76 -62
- package/src/rsc/rsc-rendering.ts +41 -60
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +62 -67
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +10 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +199 -142
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +150 -51
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +165 -87
- package/src/ssr/index.tsx +14 -14
- 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 +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 +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +97 -22
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +18 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +19 -5
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +20 -19
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +1 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +95 -82
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/prerender-collection.ts +26 -34
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/state.ts +39 -1
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +185 -10
- package/src/vite/plugins/cjs-to-esm.ts +3 -18
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +12 -11
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +4 -75
- package/src/vite/plugins/expose-id-utils.ts +3 -54
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +6 -74
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +57 -67
- package/src/vite/plugins/performance-tracks.ts +9 -16
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +26 -49
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +2 -32
- package/src/vite/plugins/version-plugin.ts +32 -23
- package/src/vite/plugins/virtual-entries.ts +35 -17
- package/src/vite/rango.ts +148 -115
- package/src/vite/router-discovery.ts +220 -68
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- 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 +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -34
- package/src/vite/utils/shared-utils.ts +95 -43
- 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
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Find matching close paren in bundled code using depth counting.
|
|
8
|
-
* Uses skipStringOrComment
|
|
9
|
-
* template literal ${...} expressions, comments, and nested strings.
|
|
10
|
-
* Returns the position after the closing paren, or -1 if unmatched.
|
|
8
|
+
* Uses skipStringOrComment to correctly handle template literals, comments, and nested strings.
|
|
11
9
|
* @internal Exported for testing only.
|
|
12
10
|
*/
|
|
13
11
|
export function findMatchingParenInBundle(
|
|
@@ -30,9 +28,8 @@ export function findMatchingParenInBundle(
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
/**
|
|
33
|
-
* Scan a bundled chunk for handler exports
|
|
34
|
-
*
|
|
35
|
-
* @internal Exported for testing only.
|
|
31
|
+
* Scan a bundled chunk for handler exports and extract their names + $$id values.
|
|
32
|
+
* Optionally detects passthrough flag. @internal Exported for testing only.
|
|
36
33
|
*/
|
|
37
34
|
export function extractHandlerExportsFromChunk(
|
|
38
35
|
chunkCode: string,
|
|
@@ -59,7 +56,7 @@ export function extractHandlerExportsFromChunk(
|
|
|
59
56
|
if (detectPassthrough) {
|
|
60
57
|
const eFnName = escapeRegExp(fnName);
|
|
61
58
|
const callStartRe = new RegExp(
|
|
62
|
-
`const\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
59
|
+
`(?:const|let|var)\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
63
60
|
);
|
|
64
61
|
const callStart = callStartRe.exec(chunkCode);
|
|
65
62
|
if (callStart) {
|
|
@@ -67,7 +64,7 @@ export function extractHandlerExportsFromChunk(
|
|
|
67
64
|
const closePos = findMatchingParenInBundle(chunkCode, afterOpen);
|
|
68
65
|
if (closePos !== -1) {
|
|
69
66
|
const callBody = chunkCode.slice(callStart.index, closePos);
|
|
70
|
-
isPassthrough = /passthrough\s*:\s*(!0|true)/.test(callBody);
|
|
67
|
+
isPassthrough = /passthrough\s*:\s*(!0|true)/.test(callBody); // !0 is minified true
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
}
|
|
@@ -79,9 +76,8 @@ export function extractHandlerExportsFromChunk(
|
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
/**
|
|
82
|
-
* Evict handler code from a bundled chunk, replacing full
|
|
83
|
-
*
|
|
84
|
-
* and bytes saved, or null if no changes were made.
|
|
79
|
+
* Evict handler code from a bundled chunk, replacing full call expressions with stubs.
|
|
80
|
+
* Returns the modified code and bytes saved, or null if no changes were made.
|
|
85
81
|
* @internal Exported for testing only.
|
|
86
82
|
*/
|
|
87
83
|
export function evictHandlerCode(
|
|
@@ -98,8 +94,10 @@ export function evictHandlerCode(
|
|
|
98
94
|
if (passthrough) continue;
|
|
99
95
|
|
|
100
96
|
const eName = escapeRegExp(name);
|
|
97
|
+
// Match const/let/var: Rolldown (Vite 8) emits top-level bindings in the
|
|
98
|
+
// non-minified RSC bundle as `var`, whereas Rollup used `const`.
|
|
101
99
|
const callStartRe = new RegExp(
|
|
102
|
-
`const\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
100
|
+
`(?:const|let|var)\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
103
101
|
);
|
|
104
102
|
const startMatch = callStartRe.exec(modified);
|
|
105
103
|
if (!startMatch) continue;
|
|
@@ -108,13 +106,11 @@ export function evictHandlerCode(
|
|
|
108
106
|
const closePos = findMatchingParenInBundle(modified, afterOpen);
|
|
109
107
|
if (closePos === -1) continue;
|
|
110
108
|
|
|
111
|
-
// Skip trailing whitespace and optional semicolon
|
|
112
109
|
let rangeEnd = closePos;
|
|
113
110
|
while (rangeEnd < modified.length && /\s/.test(modified[rangeEnd]))
|
|
114
111
|
rangeEnd++;
|
|
115
112
|
if (modified[rangeEnd] === ";") rangeEnd++;
|
|
116
113
|
|
|
117
|
-
// Validate: matched range must contain the expected handlerId
|
|
118
114
|
const matched = modified.slice(startMatch.index, rangeEnd);
|
|
119
115
|
if (!matched.includes(handlerId)) continue;
|
|
120
116
|
|
|
@@ -122,7 +118,6 @@ export function evictHandlerCode(
|
|
|
122
118
|
modified =
|
|
123
119
|
modified.slice(0, startMatch.index) + stub + modified.slice(rangeEnd);
|
|
124
120
|
|
|
125
|
-
// Remove the now-redundant $$id assignment line.
|
|
126
121
|
modified = modified.replace(
|
|
127
122
|
new RegExp(`\\n${eName}\\.\\$\\$id\\s*=\\s*"[^"]+";`),
|
|
128
123
|
"",
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { ClientChunkMeta, ClientChunks } from "../plugin-types.js";
|
|
2
|
+
import { createRangoDebugger, NS } from "../debug.js";
|
|
3
|
+
import { hashRefKey } from "../plugins/client-ref-hashing.js";
|
|
4
|
+
|
|
5
|
+
/** The callback shape @vitejs/plugin-rsc's `clientChunks` option accepts. */
|
|
6
|
+
export type RscClientChunksFn = (meta: ClientChunkMeta) => string | undefined;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build-time context the discovery pass populates and the built-in strategy
|
|
10
|
+
* reads. It refines how the catch-all (no route-root marker) modules are grouped
|
|
11
|
+
* without touching marker splits or the shared runtime:
|
|
12
|
+
*
|
|
13
|
+
* - `fallbackRefs`: production hashes of the `"use client"` modules a consumer
|
|
14
|
+
* registered as `errorBoundary`/`notFoundBoundary` fallbacks. Pulled into a
|
|
15
|
+
* dedicated `app-fallback` chunk so the error UI is not co-bundled with the
|
|
16
|
+
* very route code it exists to catch failures for (resilience), and so the
|
|
17
|
+
* chunk it would otherwise sit in gets named after a real module rather than
|
|
18
|
+
* the boundary. Populated by reading each fallback element's client-reference
|
|
19
|
+
* `$$id` during discovery (see discover-routers).
|
|
20
|
+
*/
|
|
21
|
+
export interface ClientChunkContext {
|
|
22
|
+
fallbackRefs: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Opt-in observability for the built-in strategy. The route-root marker list is
|
|
27
|
+
* intentionally finite (see {@link ROUTE_ROOT_DIRS}); a consumer whose layout
|
|
28
|
+
* has no recognized marker (e.g. `src/parts/<feature>/…`) silently inherits the
|
|
29
|
+
* default grouping (no per-route split). That silence is the only real downside
|
|
30
|
+
* of a convention-based default, so we make the decision observable: run a build
|
|
31
|
+
* with `DEBUG=rango:chunks` to see, per client module, which route group it was
|
|
32
|
+
* assigned to or why it fell back to the shared grouping. Zero cost when off
|
|
33
|
+
* (the debugger is `undefined` unless the namespace is enabled). For full control
|
|
34
|
+
* over any layout, pass a `clientChunks` function instead of relying on the
|
|
35
|
+
* convention — that is the supported configurability path, not widening the list.
|
|
36
|
+
*/
|
|
37
|
+
const debugChunks = createRangoDebugger(NS.chunks);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Modules that must stay on the default (shared) grouping regardless of strategy:
|
|
41
|
+
* React, the router client runtime, and anything in node_modules. Splitting these
|
|
42
|
+
* out per route would fragment the shared baseline and regress cache reuse — they
|
|
43
|
+
* are loaded on every route, so they belong in shared chunks.
|
|
44
|
+
*
|
|
45
|
+
* The Rango runtime is matched by package root only: `@rangojs/router` (the
|
|
46
|
+
* installed/aliased name) and the workspace `packages/(rangojs-router|rsc-router)/(src|dist)/`.
|
|
47
|
+
* The `(src|dist)` anchor matches the package's own source/build output but NOT
|
|
48
|
+
* consumer apps that merely live under a `packages/rangojs-router/` ancestor (the
|
|
49
|
+
* in-repo e2e apps), so their app components remain splittable. We deliberately do
|
|
50
|
+
* NOT match a bare `/src/browser/`: that is a consumer-owned path (a consumer's own
|
|
51
|
+
* `src/browser/Foo.tsx` must still split).
|
|
52
|
+
*
|
|
53
|
+
* We test BOTH `meta.id` (absolute) and `meta.normalizedId`. `normalizedId` is the
|
|
54
|
+
* project-root-relative form plugin-rsc derives (e.g. `../../src/browser/react/Link.tsx`
|
|
55
|
+
* for the in-repo runtime), which the package-root patterns miss; the absolute `id`
|
|
56
|
+
* always contains the package's real location, so it reliably catches the runtime.
|
|
57
|
+
*/
|
|
58
|
+
function isSharedRuntime(meta: ClientChunkMeta): boolean {
|
|
59
|
+
return [meta.id, meta.normalizedId].some(
|
|
60
|
+
(path) =>
|
|
61
|
+
path.includes("/node_modules/") ||
|
|
62
|
+
/\/@rangojs\/router\//.test(path) ||
|
|
63
|
+
/\/packages\/(rangojs-router|rsc-router)\/(src|dist)\//.test(path),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Sanitize a raw group name into a filesystem/Rollup-safe chunk name fragment. */
|
|
68
|
+
function sanitizeGroup(name: string): string {
|
|
69
|
+
return name.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "app";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Directory names that conventionally hold one sub-directory per route/feature.
|
|
74
|
+
* When a `"use client"` module lives under one of these, the built-in strategy
|
|
75
|
+
* keys the chunk on the segment IMMEDIATELY AFTER the marker (the route id),
|
|
76
|
+
* rather than the module's immediate parent directory. This is what keeps
|
|
77
|
+
* `routes/foo/components/Button.tsx` and `routes/bar/components/Button.tsx` in
|
|
78
|
+
* `app-foo` / `app-bar` instead of colliding in a single `app-components`.
|
|
79
|
+
*
|
|
80
|
+
* Route identity lives in the path PREFIX; the immediate parent (a suffix) is
|
|
81
|
+
* only a reliable proxy for the un-nested `routes/<route>/Widget.tsx` layout.
|
|
82
|
+
*/
|
|
83
|
+
const ROUTE_ROOT_DIRS = new Set([
|
|
84
|
+
"routes",
|
|
85
|
+
"route",
|
|
86
|
+
"pages",
|
|
87
|
+
"page",
|
|
88
|
+
"app",
|
|
89
|
+
"features",
|
|
90
|
+
"feature",
|
|
91
|
+
"views",
|
|
92
|
+
"view",
|
|
93
|
+
"handlers",
|
|
94
|
+
"urls",
|
|
95
|
+
"modules",
|
|
96
|
+
"screens",
|
|
97
|
+
"sections",
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Built-in strategy used when `clientChunks: true` (also the default). Splits app
|
|
102
|
+
* client components by route/feature identity ONLY where it can recognize a route
|
|
103
|
+
* structure; everywhere else it inherits the default grouping (returns undefined).
|
|
104
|
+
* This conservatism is what makes it safe as a default:
|
|
105
|
+
*
|
|
106
|
+
* - A recognized route structure (`routes/<id>/…`, `app/<id>/…`, `handlers/<id>/…`
|
|
107
|
+
* etc.) splits into a per-route chunk `app-<id>`, at any nesting depth.
|
|
108
|
+
* - A flat `src/components/Button.tsx`, or host sub-apps already split by a dynamic
|
|
109
|
+
* `import()` boundary (each app's `serverChunk` differs), get `undefined` and so
|
|
110
|
+
* keep `@vitejs/plugin-rsc`'s default `serverChunk` grouping — i.e. NO change
|
|
111
|
+
* versus not enabling the option. Returning a parent-dir name here would instead
|
|
112
|
+
* merge unrelated modules (e.g. every host app's `components/Layout.tsx` into one
|
|
113
|
+
* `app-components`), re-introducing cross-app leakage.
|
|
114
|
+
*
|
|
115
|
+
* Resolution order:
|
|
116
|
+
* 1. Shared runtime (React / router / node_modules) -> `undefined` (never split).
|
|
117
|
+
* 2. A registered error/notFound fallback (`ctx.fallbackRefs`) -> `app-fallback`,
|
|
118
|
+
* regardless of location, so the error UI is decoupled from the happy path.
|
|
119
|
+
* 3. A {@link ROUTE_ROOT_DIRS} marker with a directory after it -> key on that
|
|
120
|
+
* next segment (the route id), robust to any nesting depth.
|
|
121
|
+
* 4. Otherwise `undefined` (inherit the default `serverChunk` grouping).
|
|
122
|
+
*/
|
|
123
|
+
export function directoryClientChunks(
|
|
124
|
+
meta: ClientChunkMeta,
|
|
125
|
+
ctx?: ClientChunkContext,
|
|
126
|
+
): string | undefined {
|
|
127
|
+
if (isSharedRuntime(meta)) {
|
|
128
|
+
// React / router runtime / node_modules: always shared, expected, uninteresting.
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
// Registered error/notFound fallbacks -> a dedicated chunk. The error UI must
|
|
132
|
+
// not co-bundle with the code it catches failures for, and removing it lets the
|
|
133
|
+
// chunk it would otherwise anchor be named after a real module, not the boundary.
|
|
134
|
+
if (
|
|
135
|
+
ctx?.fallbackRefs.size &&
|
|
136
|
+
ctx.fallbackRefs.has(hashRefKey(meta.normalizedId))
|
|
137
|
+
) {
|
|
138
|
+
debugChunks?.("fallback %s -> app-fallback", meta.normalizedId);
|
|
139
|
+
return "app-fallback";
|
|
140
|
+
}
|
|
141
|
+
const segments = meta.normalizedId.split("/").filter(Boolean);
|
|
142
|
+
const dirCount = segments.length - 1; // exclude the filename
|
|
143
|
+
if (dirCount >= 1) {
|
|
144
|
+
// Route-root marker -> the segment after it is the route id. First marker
|
|
145
|
+
// wins, so a top-level route owns its whole subtree. The `< dirCount - 1`
|
|
146
|
+
// bound guarantees the segment after the marker is a directory, not the file.
|
|
147
|
+
for (let i = 0; i < dirCount - 1; i++) {
|
|
148
|
+
if (ROUTE_ROOT_DIRS.has(segments[i].toLowerCase())) {
|
|
149
|
+
const group = `app-${sanitizeGroup(segments[i + 1])}`;
|
|
150
|
+
debugChunks?.("split %s -> %s", meta.normalizedId, group);
|
|
151
|
+
return group;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// No recognized route structure -> inherit the default serverChunk grouping.
|
|
156
|
+
// This is the actionable "silent" case: app code that did NOT split by route.
|
|
157
|
+
// Surface it (under DEBUG=rango:chunks) so a consumer can see their layout
|
|
158
|
+
// missed the convention and either colocate under a marker dir or pass a fn.
|
|
159
|
+
debugChunks?.(
|
|
160
|
+
"shared %s (no route-root marker; inherits default grouping)",
|
|
161
|
+
meta.normalizedId,
|
|
162
|
+
);
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Resolve a Rango `clientChunks` option into a @vitejs/plugin-rsc `clientChunks`
|
|
168
|
+
* callback, or `undefined` to leave plugin-rsc on its default (serverChunk)
|
|
169
|
+
* grouping.
|
|
170
|
+
*
|
|
171
|
+
* - `false` / `undefined` -> `undefined` (no override).
|
|
172
|
+
* - `true` -> the built-in {@link directoryClientChunks} strategy,
|
|
173
|
+
* bound to the discovery-populated {@link ClientChunkContext} (fallback chunk).
|
|
174
|
+
* - function -> the user's function, used verbatim (full control; the
|
|
175
|
+
* fallback refinement does not apply — the consumer owns the grouping).
|
|
176
|
+
*/
|
|
177
|
+
export function resolveClientChunks(
|
|
178
|
+
option: ClientChunks | undefined,
|
|
179
|
+
ctx?: ClientChunkContext,
|
|
180
|
+
): RscClientChunksFn | undefined {
|
|
181
|
+
if (!option) return undefined;
|
|
182
|
+
if (option === true) return (meta) => directoryClientChunks(meta, ctx);
|
|
183
|
+
return option;
|
|
184
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { Plugin, ResolvedConfig, UserConfig } from "vite";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a user plugin must NOT be forwarded into the discovery temp server.
|
|
5
|
+
*
|
|
6
|
+
* Framework-owned plugins are matched precisely -- by exact name or a
|
|
7
|
+
* namespaced prefix -- rather than by a loose substring/word prefix, so an
|
|
8
|
+
* unrelated user resolver named e.g. `rsc-paths` or `cloudflare-kv-alias` is
|
|
9
|
+
* still forwarded (it would otherwise reproduce issue #500).
|
|
10
|
+
*
|
|
11
|
+
* - `vite:*` Vite core + official plugins (incl.
|
|
12
|
+
* @vitejs/plugin-react's `vite:react-*`). The temp
|
|
13
|
+
* server already provides its own core; forwarding
|
|
14
|
+
* these would duplicate or conflict with it.
|
|
15
|
+
* - `rsc` / `rsc:*` @vitejs/plugin-rsc. createTempRscServer instantiates
|
|
16
|
+
* its own rsc() plugin; forwarding would duplicate it.
|
|
17
|
+
* Matched exactly (`rsc`) or by the `rsc:` namespace --
|
|
18
|
+
* NOT every `rsc`-prefixed name.
|
|
19
|
+
* - `@rangojs/router*` Our own plugins. The discovery plugin spawns the
|
|
20
|
+
* temp server, so forwarding would recurse infinitely.
|
|
21
|
+
* - `@cloudflare/vite-plugin*` / @cloudflare/vite-plugin (emits the scoped
|
|
22
|
+
* `vite-plugin-cloudflare*` `@cloudflare/vite-plugin` and unscoped
|
|
23
|
+
* `vite-plugin-cloudflare` / `vite-plugin-cloudflare:*`).
|
|
24
|
+
* Forwarding re-inits workerd, defeating the Node temp
|
|
25
|
+
* server. Matched specifically so a scoped user resolver
|
|
26
|
+
* like `@cloudflare/kv-alias` is still forwarded.
|
|
27
|
+
*/
|
|
28
|
+
function isDenied(name: string): boolean {
|
|
29
|
+
return (
|
|
30
|
+
name.startsWith("vite:") ||
|
|
31
|
+
name === "rsc" ||
|
|
32
|
+
name.startsWith("rsc:") ||
|
|
33
|
+
name.startsWith("@rangojs/router") ||
|
|
34
|
+
name.startsWith("@cloudflare/vite-plugin") ||
|
|
35
|
+
name.startsWith("vite-plugin-cloudflare")
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A plugin participates in resolution if it exposes `resolveId` or `load`.
|
|
41
|
+
* Plugins that only transform, configure the server, or hook into the build
|
|
42
|
+
* lifecycle do not affect how bare specifiers resolve, so we skip them to keep
|
|
43
|
+
* the forwarded surface minimal.
|
|
44
|
+
*/
|
|
45
|
+
function hasResolutionHooks(p: Plugin): boolean {
|
|
46
|
+
return Boolean((p as any).resolveId || (p as any).load);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Strip a resolved plugin instance down to its resolution hooks plus the
|
|
51
|
+
* gating fields that decide whether/where it runs.
|
|
52
|
+
*
|
|
53
|
+
* We reuse the SAME instance objects captured from `config.plugins`. By the
|
|
54
|
+
* time `configResolved` fires on the discovery plugin, every plugin's own
|
|
55
|
+
* `config`/`configResolved` has already run on the main server, so any state
|
|
56
|
+
* a `resolveId` hook depends on (e.g. vite-tsconfig-paths' compiled path
|
|
57
|
+
* matcher, held in closure) is already populated. Forwarding only the
|
|
58
|
+
* resolution hooks therefore preserves correct resolution while avoiding a
|
|
59
|
+
* second `buildStart`/`configureServer`/`config` lifecycle in the temp server.
|
|
60
|
+
*
|
|
61
|
+
* `enforce` and `applyToEnvironment` are preserved so ordering and per-environment
|
|
62
|
+
* gating match the real pipeline.
|
|
63
|
+
*
|
|
64
|
+
* `apply` is intentionally dropped. Vite filters plugins by `apply` against the
|
|
65
|
+
* command during config resolution, so the `config.plugins` we read here is
|
|
66
|
+
* already command-filtered by the main server (build: `apply: "build"` +
|
|
67
|
+
* unconditional; dev: `apply: "serve"` + unconditional). The discovery temp
|
|
68
|
+
* server is always created with `createServer` (`command === "serve"`), so a
|
|
69
|
+
* forwarded `apply: "build"` plugin would be filtered straight back out -- even
|
|
70
|
+
* during a production build, where build-only resolvers are exactly what
|
|
71
|
+
* static/prerender rendering needs. Since the source list is already correct for
|
|
72
|
+
* the current command, the forwarded copy must carry no `apply` gate.
|
|
73
|
+
*/
|
|
74
|
+
function stripToResolutionHooks(p: Plugin): Plugin {
|
|
75
|
+
const stripped: Plugin = { name: p.name };
|
|
76
|
+
if ((p as any).enforce) (stripped as any).enforce = (p as any).enforce;
|
|
77
|
+
if ((p as any).applyToEnvironment)
|
|
78
|
+
(stripped as any).applyToEnvironment = (p as any).applyToEnvironment;
|
|
79
|
+
if ((p as any).resolveId) (stripped as any).resolveId = (p as any).resolveId;
|
|
80
|
+
if ((p as any).load) (stripped as any).load = (p as any).load;
|
|
81
|
+
return stripped;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Pick the user's resolution plugins from the resolved plugin list, denylist
|
|
86
|
+
* framework-owned plugins, keep only those with resolution hooks, and strip
|
|
87
|
+
* each to its resolution surface. Returns plugin objects safe to drop into the
|
|
88
|
+
* discovery temp server's `plugins` array.
|
|
89
|
+
*/
|
|
90
|
+
export function selectForwardableResolvePlugins(
|
|
91
|
+
plugins: readonly Plugin[] | undefined,
|
|
92
|
+
): Plugin[] {
|
|
93
|
+
if (!plugins) return [];
|
|
94
|
+
const forwarded: Plugin[] = [];
|
|
95
|
+
for (const p of plugins) {
|
|
96
|
+
const name = p?.name;
|
|
97
|
+
if (!name || isDenied(name)) continue;
|
|
98
|
+
if (!hasResolutionHooks(p)) continue;
|
|
99
|
+
forwarded.push(stripToResolutionHooks(p));
|
|
100
|
+
}
|
|
101
|
+
return forwarded;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The resolution-relevant slice of the user's resolved config that is plain
|
|
106
|
+
* data (no plugin re-execution): everything under `resolve` that influences
|
|
107
|
+
* how specifiers map to files, plus `define` and `oxc` so transforms and
|
|
108
|
+
* compile-time constants match request time.
|
|
109
|
+
*/
|
|
110
|
+
export interface ForwardedRunnerConfig {
|
|
111
|
+
resolve: UserConfig["resolve"];
|
|
112
|
+
define: UserConfig["define"];
|
|
113
|
+
oxc: UserConfig["oxc"];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Extract the data-only config slice to mirror into the discovery temp server.
|
|
118
|
+
* `alias` is included here so callers no longer need to thread it separately.
|
|
119
|
+
*
|
|
120
|
+
* `tsconfigPaths` is forwarded so Vite 8's native tsconfig `paths` resolution
|
|
121
|
+
* (a top-level `resolve` flag, off by default) reaches the temp server. The
|
|
122
|
+
* server is created with `configFile: false` and an explicit, allowlisted
|
|
123
|
+
* resolve slice, so a flag that is not copied here is simply absent during
|
|
124
|
+
* discovery — which would make path-aliased imports fail at prerender/static
|
|
125
|
+
* time the same way unforwarded resolveId plugins did (issue #500).
|
|
126
|
+
*
|
|
127
|
+
* `oxc` keeps the user's options but always pins the RSC-required JSX runtime
|
|
128
|
+
* (automatic, react), since the temp server compiles the handler graph as
|
|
129
|
+
* React server components regardless of the user's app-level JSX config. Vite 8
|
|
130
|
+
* replaced the deprecated `esbuild` transform option with `oxc`, so we read and
|
|
131
|
+
* forward `oxc` exclusively — no `esbuild` field is touched.
|
|
132
|
+
*/
|
|
133
|
+
export function pickForwardedRunnerConfig(
|
|
134
|
+
config: ResolvedConfig,
|
|
135
|
+
): ForwardedRunnerConfig {
|
|
136
|
+
const r = config.resolve ?? ({} as ResolvedConfig["resolve"]);
|
|
137
|
+
const resolve: NonNullable<UserConfig["resolve"]> = {};
|
|
138
|
+
if (r.alias !== undefined) resolve.alias = r.alias as any;
|
|
139
|
+
if (r.dedupe !== undefined) resolve.dedupe = r.dedupe;
|
|
140
|
+
if (r.conditions !== undefined) resolve.conditions = r.conditions;
|
|
141
|
+
if (r.mainFields !== undefined) resolve.mainFields = r.mainFields;
|
|
142
|
+
if (r.extensions !== undefined) resolve.extensions = r.extensions;
|
|
143
|
+
if (r.preserveSymlinks !== undefined)
|
|
144
|
+
resolve.preserveSymlinks = r.preserveSymlinks;
|
|
145
|
+
if (r.tsconfigPaths !== undefined) resolve.tsconfigPaths = r.tsconfigPaths;
|
|
146
|
+
|
|
147
|
+
// Pin the RSC JSX runtime on top of the user's oxc options. The user's
|
|
148
|
+
// jsx sub-options (e.g. `development`) are preserved when present; only
|
|
149
|
+
// `runtime`/`importSource` are forced to the values the RSC compile needs.
|
|
150
|
+
const userOxc = config.oxc;
|
|
151
|
+
const userJsx =
|
|
152
|
+
userOxc &&
|
|
153
|
+
typeof userOxc === "object" &&
|
|
154
|
+
typeof userOxc.jsx === "object" &&
|
|
155
|
+
userOxc.jsx !== null
|
|
156
|
+
? userOxc.jsx
|
|
157
|
+
: {};
|
|
158
|
+
const oxc: UserConfig["oxc"] =
|
|
159
|
+
userOxc && typeof userOxc === "object"
|
|
160
|
+
? {
|
|
161
|
+
...userOxc,
|
|
162
|
+
jsx: { ...userJsx, runtime: "automatic", importSource: "react" },
|
|
163
|
+
}
|
|
164
|
+
: { jsx: { runtime: "automatic", importSource: "react" } };
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
resolve,
|
|
168
|
+
define: config.define,
|
|
169
|
+
oxc,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -1,62 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Non-leaf nodes are skipped because they have nested lazy includes that
|
|
6
|
-
* require the handler to run for discovery.
|
|
7
|
-
*/
|
|
8
|
-
export function flattenLeafEntries(
|
|
9
|
-
prefixTree: Record<string, any>,
|
|
10
|
-
routeManifest: Record<string, string>,
|
|
11
|
-
result: Array<{ staticPrefix: string; routes: Record<string, string> }>,
|
|
12
|
-
): void {
|
|
13
|
-
function visit(node: any): void {
|
|
14
|
-
const children = node.children || {};
|
|
15
|
-
if (
|
|
16
|
-
Object.keys(children).length === 0 &&
|
|
17
|
-
node.routes &&
|
|
18
|
-
node.routes.length > 0
|
|
19
|
-
) {
|
|
20
|
-
// Leaf node: collect its routes from the manifest
|
|
21
|
-
const routes: Record<string, string> = {};
|
|
22
|
-
for (const name of node.routes) {
|
|
23
|
-
if (name in routeManifest) {
|
|
24
|
-
routes[name] = routeManifest[name];
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
result.push({ staticPrefix: node.staticPrefix, routes });
|
|
28
|
-
} else {
|
|
29
|
-
// Non-leaf: recurse into children
|
|
30
|
-
for (const child of Object.values(children)) {
|
|
31
|
-
visit(child);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
for (const node of Object.values(prefixTree)) {
|
|
36
|
-
visit(node);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Walk prefix tree to map each route name to its scope's staticPrefix.
|
|
42
|
-
*/
|
|
43
|
-
export function buildRouteToStaticPrefix(
|
|
44
|
-
prefixTree: Record<string, any>,
|
|
45
|
-
result: Record<string, string>,
|
|
46
|
-
): void {
|
|
47
|
-
function visit(node: any): void {
|
|
48
|
-
const sp = node.staticPrefix || "";
|
|
49
|
-
for (const name of node.routes || []) {
|
|
50
|
-
result[name] = sp;
|
|
51
|
-
}
|
|
52
|
-
for (const child of Object.values(node.children || {})) {
|
|
53
|
-
visit(child);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
for (const node of Object.values(prefixTree)) {
|
|
57
|
-
visit(node);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
flattenLeafEntries,
|
|
3
|
+
buildRouteToStaticPrefix,
|
|
4
|
+
} from "../../build/prefix-tree-utils.js";
|
|
60
5
|
|
|
61
6
|
/**
|
|
62
7
|
* Wrap a value as `JSON.parse('...')` instead of a JS object literal.
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package Resolution Utilities
|
|
3
|
-
*
|
|
4
|
-
* Handles detection of workspace vs npm install context and generates
|
|
5
|
-
* appropriate aliases and exclude lists for Vite configuration.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { existsSync } from "node:fs";
|
|
9
2
|
import { createRequire } from "node:module";
|
|
10
3
|
import { resolve } from "node:path";
|
|
@@ -17,44 +10,20 @@ const require = createRequire(import.meta.url);
|
|
|
17
10
|
*/
|
|
18
11
|
const VIRTUAL_PACKAGE_NAME = "@rangojs/router";
|
|
19
12
|
|
|
20
|
-
/**
|
|
21
|
-
* Get the published package name (e.g., "@rangojs/router")
|
|
22
|
-
*/
|
|
23
13
|
export function getPublishedPackageName(): string {
|
|
24
14
|
return packageJson.name;
|
|
25
15
|
}
|
|
26
16
|
|
|
27
|
-
/**
|
|
28
|
-
* Check if the package is installed from npm (scoped) vs workspace (unscoped)
|
|
29
|
-
*
|
|
30
|
-
* In workspace development:
|
|
31
|
-
* - Package is installed as "@rangojs/router" via pnpm workspace alias
|
|
32
|
-
* - The scoped name (@rangojs/router) doesn't exist in node_modules
|
|
33
|
-
*
|
|
34
|
-
* When installed from npm:
|
|
35
|
-
* - Package is installed as "@rangojs/router"
|
|
36
|
-
* - We need aliases to map "@rangojs/router/*" to "@rangojs/router/*"
|
|
37
|
-
*/
|
|
38
17
|
export function isInstalledFromNpm(): boolean {
|
|
39
18
|
const packageName = getPublishedPackageName();
|
|
40
19
|
// Check if the scoped package exists in node_modules
|
|
41
20
|
return existsSync(resolve(process.cwd(), "node_modules", packageName));
|
|
42
21
|
}
|
|
43
22
|
|
|
44
|
-
/**
|
|
45
|
-
* Check if we're in a monorepo/workspace development context
|
|
46
|
-
*/
|
|
47
23
|
export function isWorkspaceDevelopment(): boolean {
|
|
48
24
|
return !isInstalledFromNpm();
|
|
49
25
|
}
|
|
50
26
|
|
|
51
|
-
/**
|
|
52
|
-
* Subpaths derived from package.json exports that use TypeScript source.
|
|
53
|
-
* These must be excluded from Vite's dependency optimization (they ship
|
|
54
|
-
* as .ts/.tsx, not compiled JS) and aliased when installed from npm.
|
|
55
|
-
*
|
|
56
|
-
* Derived automatically from the exports field to prevent drift.
|
|
57
|
-
*/
|
|
58
27
|
const SOURCE_EXPORT_SUBPATHS = Object.keys(packageJson.exports)
|
|
59
28
|
.filter((key) => {
|
|
60
29
|
const entry = (
|
|
@@ -70,12 +39,6 @@ const SOURCE_EXPORT_SUBPATHS = Object.keys(packageJson.exports)
|
|
|
70
39
|
})
|
|
71
40
|
.map((key) => key.replace(/^\./, ""));
|
|
72
41
|
|
|
73
|
-
/**
|
|
74
|
-
* Generate the list of modules to exclude from Vite's dependency optimization.
|
|
75
|
-
*
|
|
76
|
-
* We include both the published name and the virtual name because
|
|
77
|
-
* Vite's optimizer runs before alias resolution.
|
|
78
|
-
*/
|
|
79
42
|
export function getExcludeDeps(): string[] {
|
|
80
43
|
const packageName = getPublishedPackageName();
|
|
81
44
|
const excludes: string[] = [];
|
|
@@ -92,24 +55,10 @@ export function getExcludeDeps(): string[] {
|
|
|
92
55
|
return excludes;
|
|
93
56
|
}
|
|
94
57
|
|
|
95
|
-
/**
|
|
96
|
-
* Subpaths that need aliasing — same as SOURCE_EXPORT_SUBPATHS.
|
|
97
|
-
* When installed from npm, virtual entries may use a different package name
|
|
98
|
-
* than the published one; aliases bridge them.
|
|
99
|
-
*/
|
|
100
58
|
const ALIAS_SUBPATHS = SOURCE_EXPORT_SUBPATHS;
|
|
101
59
|
|
|
102
|
-
/**
|
|
103
|
-
* Generate aliases to map virtual package paths to the actual published package.
|
|
104
|
-
*
|
|
105
|
-
* Only needed when installed from npm, where the package is under @rangojs/router
|
|
106
|
-
* but virtual entries import from rsc-router/*.
|
|
107
|
-
*
|
|
108
|
-
* Returns empty object in workspace development where rsc-router resolves directly.
|
|
109
|
-
*/
|
|
110
60
|
export function getPackageAliases(): Record<string, string> {
|
|
111
61
|
if (isWorkspaceDevelopment()) {
|
|
112
|
-
// No aliases needed - rsc-router resolves directly
|
|
113
62
|
return {};
|
|
114
63
|
}
|
|
115
64
|
|
|
@@ -123,28 +72,7 @@ export function getPackageAliases(): Record<string, string> {
|
|
|
123
72
|
return aliases;
|
|
124
73
|
}
|
|
125
74
|
|
|
126
|
-
/**
|
|
127
|
-
* Plugin-rsc pushes bare specs like
|
|
128
|
-
* `@vitejs/plugin-rsc/vendor/react-server-dom/client.edge` into
|
|
129
|
-
* `optimizeDeps.include` for the ssr and rsc environments. In strict pnpm
|
|
130
|
-
* consumer apps, `@vitejs/plugin-rsc` is only reachable from @rangojs/router's
|
|
131
|
-
* node_modules, so Vite's optimizer — which resolves from the project root —
|
|
132
|
-
* can't find them and emits "Failed to resolve dependency" warnings.
|
|
133
|
-
*
|
|
134
|
-
* We resolve those specs from this plugin's location (where plugin-rsc is
|
|
135
|
-
* guaranteed to be installed as our dep) and expose them as `resolve.alias`
|
|
136
|
-
* entries. The optimizer's resolver honors aliases, so the bare specs map to
|
|
137
|
-
* absolute paths and resolve cleanly.
|
|
138
|
-
*/
|
|
139
75
|
export function getVendorAliases(): Record<string, string> {
|
|
140
|
-
// client.browser is intentionally NOT aliased. plugin-rsc injects it into
|
|
141
|
-
// the client env's optimizeDeps.include; Vite's manual-include path resolves
|
|
142
|
-
// and pre-bundles regardless of optimizeDeps.exclude, so aliasing would
|
|
143
|
-
// trigger esbuild pre-bundling of the CJS vendor file and bypass the
|
|
144
|
-
// cjs-to-esm transform that patches `require('react'|'react-dom')` into
|
|
145
|
-
// real ESM imports. The consumer may still see a single "Failed to resolve"
|
|
146
|
-
// warning for client.browser; runtime resolution from plugin-rsc's own
|
|
147
|
-
// importer works because Vite resolves relative to the importer (not root).
|
|
148
76
|
const specs = [
|
|
149
77
|
"@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
|
|
150
78
|
"@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
|
|
@@ -154,7 +82,7 @@ export function getVendorAliases(): Record<string, string> {
|
|
|
154
82
|
try {
|
|
155
83
|
aliases[spec] = require.resolve(spec);
|
|
156
84
|
} catch {
|
|
157
|
-
//
|
|
85
|
+
// Non-fatal; Vite will warn if it cannot resolve the spec
|
|
158
86
|
}
|
|
159
87
|
}
|
|
160
88
|
return aliases;
|