@rangojs/router 0.0.0-experimental.0f44aca1
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 +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +365 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import MagicString from "magic-string";
|
|
2
|
+
import { hashId } from "../expose-id-utils.js";
|
|
3
|
+
import type { HandlerTransformConfig, CreateExportBinding } from "./types.js";
|
|
4
|
+
import { isExportOnlyFile } from "./export-analysis.js";
|
|
5
|
+
|
|
6
|
+
function analyzeCreateHandleArgs(
|
|
7
|
+
code: string,
|
|
8
|
+
startPos: number,
|
|
9
|
+
endPos: number,
|
|
10
|
+
): { hasArgs: boolean } {
|
|
11
|
+
const content = code.slice(startPos, endPos).trim();
|
|
12
|
+
return { hasArgs: content.length > 0 };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function transformHandles(
|
|
16
|
+
bindings: CreateExportBinding[],
|
|
17
|
+
s: MagicString,
|
|
18
|
+
code: string,
|
|
19
|
+
filePath: string,
|
|
20
|
+
isBuild: boolean,
|
|
21
|
+
): boolean {
|
|
22
|
+
let hasChanges = false;
|
|
23
|
+
for (const binding of bindings) {
|
|
24
|
+
const exportName = binding.exportNames[0];
|
|
25
|
+
const args = analyzeCreateHandleArgs(
|
|
26
|
+
code,
|
|
27
|
+
binding.callOpenParenPos + 1,
|
|
28
|
+
binding.callCloseParenPos,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const handleId = isBuild
|
|
32
|
+
? hashId(filePath, exportName)
|
|
33
|
+
: `${filePath}#${exportName}`;
|
|
34
|
+
|
|
35
|
+
let paramInjection: string;
|
|
36
|
+
if (!args.hasArgs) {
|
|
37
|
+
paramInjection = `undefined, "${handleId}"`;
|
|
38
|
+
} else {
|
|
39
|
+
paramInjection = `, "${handleId}"`;
|
|
40
|
+
}
|
|
41
|
+
s.appendLeft(binding.callCloseParenPos, paramInjection);
|
|
42
|
+
|
|
43
|
+
const propInjection = `\n${binding.localName}.$$id = "${handleId}";`;
|
|
44
|
+
s.appendRight(binding.statementEnd, propInjection);
|
|
45
|
+
hasChanges = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return hasChanges;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function transformLocationState(
|
|
52
|
+
bindings: CreateExportBinding[],
|
|
53
|
+
s: MagicString,
|
|
54
|
+
filePath: string,
|
|
55
|
+
isBuild: boolean,
|
|
56
|
+
): boolean {
|
|
57
|
+
let hasChanges = false;
|
|
58
|
+
for (const binding of bindings) {
|
|
59
|
+
const exportName = binding.exportNames[0];
|
|
60
|
+
|
|
61
|
+
const stateKey = isBuild
|
|
62
|
+
? hashId(filePath, exportName)
|
|
63
|
+
: `${filePath}#${exportName}`;
|
|
64
|
+
|
|
65
|
+
// Key is injected as a property assignment (not as a function argument).
|
|
66
|
+
// This allows createLocationState to accept options like { flash: true }
|
|
67
|
+
// without conflicting with key injection.
|
|
68
|
+
const propInjection = `\n${binding.localName}.__rsc_ls_key = "__rsc_ls_${stateKey}";`;
|
|
69
|
+
s.appendRight(binding.statementEnd, propInjection);
|
|
70
|
+
hasChanges = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return hasChanges;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Replace the entire file with lightweight stubs when ALL non-type exports are
|
|
78
|
+
* handler calls of the given type. Returns null for files with mixed exports.
|
|
79
|
+
*/
|
|
80
|
+
export function generateWholeFileStubs(
|
|
81
|
+
cfg: HandlerTransformConfig,
|
|
82
|
+
bindings: CreateExportBinding[],
|
|
83
|
+
code: string,
|
|
84
|
+
filePath: string,
|
|
85
|
+
isBuild: boolean,
|
|
86
|
+
): { code: string; map: null } | null {
|
|
87
|
+
if (!isExportOnlyFile(code, bindings)) return null;
|
|
88
|
+
|
|
89
|
+
const exportNames = bindings.flatMap((b) => b.exportNames);
|
|
90
|
+
const stubs = exportNames.map((name) => {
|
|
91
|
+
const handlerId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
92
|
+
return `export const ${name} = { __brand: "${cfg.brand}", $$id: "${handlerId}" };`;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return { code: stubs.join("\n") + "\n", map: null };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Replace handler call expressions with lightweight stub objects in non-RSC
|
|
100
|
+
* environments. Other exports, imports, and module-level code remain untouched.
|
|
101
|
+
*/
|
|
102
|
+
export function generateExprStubs(
|
|
103
|
+
cfg: HandlerTransformConfig,
|
|
104
|
+
bindings: CreateExportBinding[],
|
|
105
|
+
code: string,
|
|
106
|
+
filePath: string,
|
|
107
|
+
sourceId: string,
|
|
108
|
+
isBuild: boolean,
|
|
109
|
+
): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
|
|
110
|
+
if (bindings.length === 0) return null;
|
|
111
|
+
|
|
112
|
+
const s = new MagicString(code);
|
|
113
|
+
let hasChanges = false;
|
|
114
|
+
|
|
115
|
+
for (const binding of bindings) {
|
|
116
|
+
const exportName = binding.exportNames[0];
|
|
117
|
+
const handlerId = isBuild
|
|
118
|
+
? hashId(filePath, exportName)
|
|
119
|
+
: `${filePath}#${exportName}`;
|
|
120
|
+
|
|
121
|
+
s.overwrite(
|
|
122
|
+
binding.callExprStart,
|
|
123
|
+
binding.callCloseParenPos + 1,
|
|
124
|
+
`{ __brand: "${cfg.brand}", $$id: "${handlerId}" }`,
|
|
125
|
+
);
|
|
126
|
+
hasChanges = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!hasChanges) return null;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
code: s.toString(),
|
|
133
|
+
map: s.generateMap({
|
|
134
|
+
source: sourceId,
|
|
135
|
+
includeContent: true,
|
|
136
|
+
hires: "boundary",
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Inject $$id into export const handler calls in RSC environments.
|
|
143
|
+
*/
|
|
144
|
+
export function transformHandlerIds(
|
|
145
|
+
cfg: HandlerTransformConfig,
|
|
146
|
+
bindings: CreateExportBinding[],
|
|
147
|
+
s: MagicString,
|
|
148
|
+
filePath: string,
|
|
149
|
+
isBuild: boolean,
|
|
150
|
+
): boolean {
|
|
151
|
+
let hasChanges = false;
|
|
152
|
+
for (const binding of bindings) {
|
|
153
|
+
const exportName = binding.exportNames[0];
|
|
154
|
+
|
|
155
|
+
const handlerId = isBuild
|
|
156
|
+
? hashId(filePath, exportName)
|
|
157
|
+
: `${filePath}#${exportName}`;
|
|
158
|
+
|
|
159
|
+
// Injection strategy matches the runtime overload signatures:
|
|
160
|
+
// 0 args -> inject undefined, "id"
|
|
161
|
+
// 1 arg (handler) -> inject , undefined, "id"
|
|
162
|
+
// 2+ args -> inject , "id"
|
|
163
|
+
let paramInjection: string;
|
|
164
|
+
if (binding.argCount === 0) {
|
|
165
|
+
paramInjection = `undefined, "${handlerId}"`;
|
|
166
|
+
} else if (binding.argCount === 1) {
|
|
167
|
+
paramInjection = `, undefined, "${handlerId}"`;
|
|
168
|
+
} else {
|
|
169
|
+
paramInjection = `, "${handlerId}"`;
|
|
170
|
+
}
|
|
171
|
+
s.appendLeft(binding.callCloseParenPos, paramInjection);
|
|
172
|
+
|
|
173
|
+
const propInjection = `\n${binding.localName}.$$id = "${handlerId}";`;
|
|
174
|
+
s.appendRight(binding.statementEnd, propInjection);
|
|
175
|
+
hasChanges = true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return hasChanges;
|
|
179
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type MagicString from "magic-string";
|
|
2
|
+
import { hashId } from "../expose-id-utils.js";
|
|
3
|
+
import type { CreateExportBinding } from "./types.js";
|
|
4
|
+
import { isExportOnlyFile } from "./export-analysis.js";
|
|
5
|
+
|
|
6
|
+
export function hasCreateLoaderImport(code: string): boolean {
|
|
7
|
+
return /import\s*\{[^}]*\bcreateLoader\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
|
|
8
|
+
code,
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generate lightweight client stubs for loader files.
|
|
14
|
+
*
|
|
15
|
+
* When a loader file is imported from a client component (e.g., for useLoader()),
|
|
16
|
+
* the client only needs { __brand: "loader", $$id: "..." } objects.
|
|
17
|
+
* This function replaces the entire file contents with just those stub exports,
|
|
18
|
+
* preventing server-only data (constants, DB queries, etc.) from leaking into
|
|
19
|
+
* the client bundle.
|
|
20
|
+
*
|
|
21
|
+
* Only applies when ALL named exports are createLoader() calls (plus type exports
|
|
22
|
+
* which are erased at compile time). Files with mixed exports are left untouched.
|
|
23
|
+
*/
|
|
24
|
+
export function generateClientLoaderStubs(
|
|
25
|
+
bindings: CreateExportBinding[],
|
|
26
|
+
code: string,
|
|
27
|
+
filePath: string,
|
|
28
|
+
isBuild: boolean,
|
|
29
|
+
): { code: string; map?: undefined } | null {
|
|
30
|
+
if (!isExportOnlyFile(code, bindings)) return null;
|
|
31
|
+
|
|
32
|
+
const lines: string[] = [];
|
|
33
|
+
|
|
34
|
+
for (const binding of bindings) {
|
|
35
|
+
for (const name of binding.exportNames) {
|
|
36
|
+
const loaderId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
37
|
+
lines.push(
|
|
38
|
+
`export const ${name} = { __brand: "loader", $$id: "${loaderId}" };`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { code: lines.join("\n") + "\n" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function transformLoaders(
|
|
47
|
+
bindings: CreateExportBinding[],
|
|
48
|
+
s: MagicString,
|
|
49
|
+
filePath: string,
|
|
50
|
+
isBuild: boolean,
|
|
51
|
+
): boolean {
|
|
52
|
+
let hasChanges = false;
|
|
53
|
+
|
|
54
|
+
for (const binding of bindings) {
|
|
55
|
+
const exportName = binding.exportNames[0];
|
|
56
|
+
|
|
57
|
+
const loaderId = isBuild
|
|
58
|
+
? hashId(filePath, exportName)
|
|
59
|
+
: `${filePath}#${exportName}`;
|
|
60
|
+
|
|
61
|
+
// Inject $$id as hidden third parameter.
|
|
62
|
+
// createLoader(fn) -> createLoader(fn, undefined, "id")
|
|
63
|
+
// createLoader(fn, true) -> createLoader(fn, true, "id")
|
|
64
|
+
const paramInjection =
|
|
65
|
+
binding.argCount === 1 ? `, undefined, "${loaderId}"` : `, "${loaderId}"`;
|
|
66
|
+
s.appendLeft(binding.callCloseParenPos, paramInjection);
|
|
67
|
+
|
|
68
|
+
const propInjection = `\n${binding.localName}.$$id = "${loaderId}";`;
|
|
69
|
+
s.appendRight(binding.statementEnd, propInjection);
|
|
70
|
+
hasChanges = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return hasChanges;
|
|
74
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import MagicString from "magic-string";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { normalizePath, findMatchingParen } from "../expose-id-utils.js";
|
|
6
|
+
import { getImportedFnNames } from "./export-analysis.js";
|
|
7
|
+
|
|
8
|
+
export function transformRouter(
|
|
9
|
+
code: string,
|
|
10
|
+
filePath: string,
|
|
11
|
+
routerFnNames: string[],
|
|
12
|
+
absolutePath?: string,
|
|
13
|
+
): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
|
|
14
|
+
const pat = new RegExp(
|
|
15
|
+
`\\b(?:${routerFnNames.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
16
|
+
"g",
|
|
17
|
+
);
|
|
18
|
+
let match: RegExpExecArray | null;
|
|
19
|
+
const s = new MagicString(code);
|
|
20
|
+
let changed = false;
|
|
21
|
+
|
|
22
|
+
// Compute the import path for the generated route names file.
|
|
23
|
+
// filePath is relative to project root (e.g., "src/router.tsx")
|
|
24
|
+
const basename = path.basename(filePath).replace(/\.(tsx?|jsx?)$/, "");
|
|
25
|
+
const routeNamesImport = `./${basename}.named-routes.gen.js`;
|
|
26
|
+
const routeNamesVar = `__rsc_rn`;
|
|
27
|
+
|
|
28
|
+
while ((match = pat.exec(code)) !== null) {
|
|
29
|
+
const callStart = match.index;
|
|
30
|
+
const parenPos = match.index + match[0].length - 1;
|
|
31
|
+
|
|
32
|
+
// Scope the $$id check to within this call's arguments only,
|
|
33
|
+
// not the entire remaining file.
|
|
34
|
+
const closeParen = findMatchingParen(code, parenPos + 1);
|
|
35
|
+
const callArgs = code.slice(parenPos + 1, closeParen);
|
|
36
|
+
|
|
37
|
+
// Skip if $$id is already present in this call
|
|
38
|
+
if (callArgs.includes("$$id")) continue;
|
|
39
|
+
|
|
40
|
+
// Compute line number for this call
|
|
41
|
+
const lineNumber = code.slice(0, callStart).split("\n").length;
|
|
42
|
+
const hash = createHash("sha256")
|
|
43
|
+
.update(`${filePath}:${lineNumber}`)
|
|
44
|
+
.digest("hex")
|
|
45
|
+
.slice(0, 8);
|
|
46
|
+
|
|
47
|
+
changed = true;
|
|
48
|
+
// $$sourceFile uses the absolute path so that downstream consumers
|
|
49
|
+
// (virtual-module-codegen, runtime-discovery) can resolve gen file
|
|
50
|
+
// imports correctly via path.dirname / path.join.
|
|
51
|
+
const sourceFilePath = absolutePath ?? filePath;
|
|
52
|
+
const injected = ` $$id: "${hash}", $$sourceFile: "${sourceFilePath}", $$routeNames: ${routeNamesVar},`;
|
|
53
|
+
|
|
54
|
+
const afterParen = callArgs.trimStart();
|
|
55
|
+
if (afterParen.startsWith("{")) {
|
|
56
|
+
const bracePos = code.indexOf("{", parenPos + 1);
|
|
57
|
+
s.appendRight(bracePos + 1, injected);
|
|
58
|
+
} else if (afterParen.startsWith(")")) {
|
|
59
|
+
s.appendRight(parenPos + 1, `{${injected} }`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!changed) return null;
|
|
64
|
+
|
|
65
|
+
// Prepend the static import as the first line. MagicString tracks the
|
|
66
|
+
// offset so all downstream source maps remain correct.
|
|
67
|
+
s.prepend(
|
|
68
|
+
`import { NamedRoutes as ${routeNamesVar} } from "${routeNamesImport}";\n`,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
code: s.toString(),
|
|
73
|
+
map: s.generateMap({ hires: true }),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Inject stable $$id into createRouter() calls at compile time.
|
|
79
|
+
* This must be a separate plugin without enforce:"post" because running
|
|
80
|
+
* at "post" priority changes Vite's dep optimization timing and can cause
|
|
81
|
+
* ERR_OUTDATED_OPTIMIZED_DEP / React dual-instance issues.
|
|
82
|
+
*/
|
|
83
|
+
export function exposeRouterId(): Plugin {
|
|
84
|
+
let projectRoot = "";
|
|
85
|
+
return {
|
|
86
|
+
name: "@rangojs/router:expose-router-id",
|
|
87
|
+
configResolved(config) {
|
|
88
|
+
projectRoot = config.root;
|
|
89
|
+
},
|
|
90
|
+
transform(code, id) {
|
|
91
|
+
if (!code.includes("createRouter")) return null;
|
|
92
|
+
// Accepts both @rangojs/router and @rangojs/router/server subpath.
|
|
93
|
+
// NOTE: detectImports in expose-id-utils has a stricter check that
|
|
94
|
+
// excludes /server for its router flag -- that's intentional since
|
|
95
|
+
// detectImports is only used in exposeInternalIds, not here.
|
|
96
|
+
if (
|
|
97
|
+
!/import\s*\{[^}]*\bcreateRouter\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
|
|
98
|
+
code,
|
|
99
|
+
)
|
|
100
|
+
) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (id.includes("node_modules")) return null;
|
|
104
|
+
|
|
105
|
+
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
106
|
+
const routerFnNames = getImportedFnNames(code, "createRouter");
|
|
107
|
+
return transformRouter(code, filePath, routerFnNames, normalizePath(id));
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface HandlerTransformConfig {
|
|
2
|
+
fnName: string;
|
|
3
|
+
brand: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface CreateExportBinding {
|
|
7
|
+
localName: string;
|
|
8
|
+
exportNames: string[];
|
|
9
|
+
callExprStart: number;
|
|
10
|
+
callOpenParenPos: number;
|
|
11
|
+
callCloseParenPos: number;
|
|
12
|
+
argCount: number;
|
|
13
|
+
statementEnd: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface StrictCreateTransformConfig {
|
|
17
|
+
fnName: "createLoader" | "createHandle" | "createLocationState";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const PRERENDER_CONFIG: HandlerTransformConfig = {
|
|
21
|
+
fnName: "Prerender",
|
|
22
|
+
brand: "prerenderHandler",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const STATIC_CONFIG: HandlerTransformConfig = {
|
|
26
|
+
fnName: "Static",
|
|
27
|
+
brand: "staticHandler",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const STRICT_CREATE_CONFIGS: StrictCreateTransformConfig[] = [
|
|
31
|
+
{ fnName: "createLoader" },
|
|
32
|
+
{ fnName: "createHandle" },
|
|
33
|
+
{ fnName: "createLocationState" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export interface ExposeInternalIdsApi {
|
|
37
|
+
/** Tracks absolute module IDs that contain prerender handler exports.
|
|
38
|
+
* key: absolute module ID (filesystem path)
|
|
39
|
+
* value: array of export names (e.g., ["ArticlesIndex", "ArticleDetail"]) */
|
|
40
|
+
prerenderHandlerModules: Map<string, string[]>;
|
|
41
|
+
/** Tracks absolute module IDs that contain static handler exports.
|
|
42
|
+
* key: absolute module ID (filesystem path)
|
|
43
|
+
* value: array of export names (e.g., ["DocsNav", "DocShell"]) */
|
|
44
|
+
staticHandlerModules: Map<string, string[]>;
|
|
45
|
+
}
|