@rangojs/router 0.0.0-experimental.13 → 0.0.0-experimental.13221847
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 +9 -0
- package/README.md +884 -4
- package/dist/bin/rango.js +1531 -212
- package/dist/vite/index.js +3995 -2489
- package/package.json +57 -52
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +85 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/hooks/SKILL.md +328 -70
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +62 -15
- package/skills/loader/SKILL.md +368 -42
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +14 -10
- package/skills/parallel/SKILL.md +137 -1
- package/skills/prerender/SKILL.md +366 -28
- package/skills/rango/SKILL.md +85 -21
- package/skills/response-routes/SKILL.md +136 -83
- package/skills/route/SKILL.md +195 -21
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +240 -102
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +92 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +266 -558
- package/src/browser/navigation-client.ts +132 -75
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +303 -309
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +144 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +190 -70
- package/src/browser/react/NavigationProvider.tsx +78 -11
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- 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 +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +29 -70
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- 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 +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +188 -57
- package/src/browser/scroll-restoration.ts +117 -44
- package/src/browser/segment-reconciler.ts +221 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +488 -606
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +116 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +63 -21
- package/src/build/generate-route-types.ts +36 -1038
- package/src/build/index.ts +2 -5
- package/src/build/route-trie.ts +38 -12
- 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 +479 -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 +342 -0
- package/src/cache/cache-scope.ts +122 -303
- package/src/cache/cf/cf-cache-store.ts +571 -17
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- 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 +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +84 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +12 -7
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +104 -40
- package/src/index.ts +122 -67
- package/src/internal-debug.ts +9 -3
- package/src/loader.rsc.ts +18 -93
- package/src/loader.ts +26 -9
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +121 -17
- package/src/prerender.ts +325 -20
- package/src/reverse.ts +144 -124
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +959 -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 -1450
- package/src/route-map-builder.ts +87 -133
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +41 -6
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +324 -116
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +179 -133
- package/src/router/logging.ts +112 -6
- package/src/router/manifest.ts +58 -19
- package/src/router/match-api.ts +89 -88
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +86 -89
- package/src/router/match-middleware/cache-lookup.ts +295 -49
- package/src/router/match-middleware/cache-store.ts +56 -13
- package/src/router/match-middleware/intercept-resolution.ts +45 -22
- package/src/router/match-middleware/segment-resolution.ts +20 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +44 -21
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +327 -369
- package/src/router/pattern-matching.ts +169 -31
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +105 -14
- package/src/router/router-context.ts +40 -21
- 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 +677 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1296 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1354
- package/src/router/segment-wrappers.ts +291 -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 +96 -29
- package/src/router/types.ts +15 -9
- package/src/router.ts +642 -2366
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +639 -1027
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -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 +237 -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 +38 -11
- package/src/search-params.ts +66 -54
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +237 -54
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +438 -71
- package/src/server.ts +26 -164
- package/src/ssr/index.tsx +101 -31
- package/src/static-handler.ts +22 -4
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- 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 +773 -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 +109 -0
- package/src/types/segments.ts +150 -0
- package/src/types.ts +1 -1795
- 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 -1323
- package/src/use-loader.tsx +85 -77
- 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 +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -2259
- package/src/vite/plugin-types.ts +48 -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/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -47
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +8 -43
- 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 +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +181 -9
- 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/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type MagicString from "magic-string";
|
|
2
|
-
import { hashInlineId, buildExportMap } from "
|
|
2
|
+
import { hashInlineId, buildExportMap } from "../plugins/expose-id-utils.js";
|
|
3
3
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Types
|
|
@@ -27,14 +27,14 @@ export interface HandlerCallSite {
|
|
|
27
27
|
export interface VirtualHandlerEntry {
|
|
28
28
|
originalModuleId: string;
|
|
29
29
|
imports: string[];
|
|
30
|
+
declarations: string[];
|
|
30
31
|
handlerCode: string;
|
|
31
32
|
exportName: string;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function isDirectivePrologueStatement(node: any): boolean {
|
|
35
36
|
return (
|
|
36
|
-
node?.type === "ExpressionStatement" &&
|
|
37
|
-
typeof node.directive === "string"
|
|
37
|
+
node?.type === "ExpressionStatement" && typeof node.directive === "string"
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -99,7 +99,11 @@ function walkNode(
|
|
|
99
99
|
walkNode(item, node, ancestors, enter);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
} else if (
|
|
102
|
+
} else if (
|
|
103
|
+
child &&
|
|
104
|
+
typeof child === "object" &&
|
|
105
|
+
typeof child.type === "string"
|
|
106
|
+
) {
|
|
103
107
|
walkNode(child, node, ancestors, enter);
|
|
104
108
|
}
|
|
105
109
|
}
|
|
@@ -158,8 +162,10 @@ export function findHandlerCalls(
|
|
|
158
162
|
|
|
159
163
|
if (parent?.type === "VariableDeclarator" && parent.init === node) {
|
|
160
164
|
// ancestors: [..., ExportNamedDecl, VarDecl, VarDeclarator, CallExpr]
|
|
161
|
-
const grandParent =
|
|
162
|
-
|
|
165
|
+
const grandParent =
|
|
166
|
+
ancestors.length >= 3 ? ancestors[ancestors.length - 3] : null;
|
|
167
|
+
const greatGrandParent =
|
|
168
|
+
ancestors.length >= 4 ? ancestors[ancestors.length - 4] : null;
|
|
163
169
|
|
|
164
170
|
if (
|
|
165
171
|
grandParent?.type === "VariableDeclaration" &&
|
|
@@ -264,6 +270,162 @@ export function extractImportDeclarations(
|
|
|
264
270
|
return imports;
|
|
265
271
|
}
|
|
266
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Check if an expression AST subtree is "inert" -- safe to evaluate eagerly
|
|
275
|
+
* without referencing import bindings. Inert expressions contain only literals,
|
|
276
|
+
* arrays/objects of inert values, template literals with inert expressions,
|
|
277
|
+
* and unary/binary operators on inert operands.
|
|
278
|
+
*
|
|
279
|
+
* Function/arrow expressions are NOT inert (they're lazy -- handled separately).
|
|
280
|
+
* Identifiers and member expressions are NOT inert (may reference imports).
|
|
281
|
+
*
|
|
282
|
+
* This check prevents TDZ errors when declarations are moved to virtual
|
|
283
|
+
* modules that end up in separate Rollup chunks with circular dependencies.
|
|
284
|
+
*/
|
|
285
|
+
function isInertExpression(node: any): boolean {
|
|
286
|
+
if (!node) return false;
|
|
287
|
+
switch (node.type) {
|
|
288
|
+
case "Literal":
|
|
289
|
+
return true;
|
|
290
|
+
case "TemplateLiteral":
|
|
291
|
+
return (node.expressions ?? []).every((e: any) => isInertExpression(e));
|
|
292
|
+
case "ArrayExpression":
|
|
293
|
+
return (node.elements ?? []).every(
|
|
294
|
+
(e: any) => e === null || isInertExpression(e),
|
|
295
|
+
);
|
|
296
|
+
case "ObjectExpression":
|
|
297
|
+
return (node.properties ?? []).every(
|
|
298
|
+
(p: any) =>
|
|
299
|
+
p.type === "Property" &&
|
|
300
|
+
(!p.computed || isInertExpression(p.key)) &&
|
|
301
|
+
isInertExpression(p.value),
|
|
302
|
+
);
|
|
303
|
+
case "UnaryExpression":
|
|
304
|
+
return isInertExpression(node.argument);
|
|
305
|
+
case "BinaryExpression":
|
|
306
|
+
return isInertExpression(node.left) && isInertExpression(node.right);
|
|
307
|
+
case "ConditionalExpression":
|
|
308
|
+
return (
|
|
309
|
+
isInertExpression(node.test) &&
|
|
310
|
+
isInertExpression(node.consequent) &&
|
|
311
|
+
isInertExpression(node.alternate)
|
|
312
|
+
);
|
|
313
|
+
case "SpreadElement":
|
|
314
|
+
return isInertExpression(node.argument);
|
|
315
|
+
default:
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Check if a variable declarator's init is safe for inclusion in a virtual
|
|
322
|
+
* module. Safe initializers are:
|
|
323
|
+
* 1. Function/arrow expressions (body is lazy, no TDZ risk)
|
|
324
|
+
* 2. Inert expressions (no identifier references, no TDZ risk)
|
|
325
|
+
*
|
|
326
|
+
* Declarations that reference identifiers at the top level (e.g.
|
|
327
|
+
* `const VT = React.Fragment`) are NOT safe -- when the virtual module
|
|
328
|
+
* is bundled into a separate Rollup chunk, circular chunk dependencies
|
|
329
|
+
* can cause "Cannot access X before initialization" TDZ errors.
|
|
330
|
+
*/
|
|
331
|
+
function isSafeDeclaratorInit(init: any): boolean {
|
|
332
|
+
if (!init) return true; // `let x;` with no init is safe
|
|
333
|
+
if (
|
|
334
|
+
init.type === "ArrowFunctionExpression" ||
|
|
335
|
+
init.type === "FunctionExpression"
|
|
336
|
+
) {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
return isInertExpression(init);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Check if all declarators in a VariableDeclaration have safe initializers
|
|
344
|
+
* and none is a handler call (Static/Prerender).
|
|
345
|
+
*/
|
|
346
|
+
function isSafeVariableDeclaration(
|
|
347
|
+
node: any,
|
|
348
|
+
handlerNames: Set<string>,
|
|
349
|
+
): boolean {
|
|
350
|
+
if (node.type !== "VariableDeclaration") return false;
|
|
351
|
+
return node.declarations.every(
|
|
352
|
+
(d: any) =>
|
|
353
|
+
isSafeDeclaratorInit(d.init) &&
|
|
354
|
+
!(
|
|
355
|
+
d.init?.type === "CallExpression" &&
|
|
356
|
+
d.init.callee?.type === "Identifier" &&
|
|
357
|
+
handlerNames.has(d.init.callee.name)
|
|
358
|
+
),
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Extract module-level declarations that are safe for inclusion in virtual
|
|
364
|
+
* modules. "Safe" means the declaration can be eagerly evaluated without
|
|
365
|
+
* referencing import bindings, preventing TDZ errors in separate chunks.
|
|
366
|
+
*
|
|
367
|
+
* Included: function declarations, arrow/function expression inits, and
|
|
368
|
+
* variable inits that are inert (pure literals, arrays, objects).
|
|
369
|
+
* Excluded: declarations that reference identifiers at init time (may
|
|
370
|
+
* reference imports causing TDZ), handler call declarations (circular),
|
|
371
|
+
* class declarations (field initializers can reference imports).
|
|
372
|
+
*
|
|
373
|
+
* Strips export keywords so declarations work as plain locals.
|
|
374
|
+
* Rollup tree-shakes unused declarations from virtual modules.
|
|
375
|
+
*/
|
|
376
|
+
export function extractModuleLevelDeclarations(
|
|
377
|
+
code: string,
|
|
378
|
+
parseAst: (code: string, options?: any) => ProgramNode,
|
|
379
|
+
handlerNames: Set<string>,
|
|
380
|
+
): string[] {
|
|
381
|
+
let program: ProgramNode;
|
|
382
|
+
try {
|
|
383
|
+
program = parseAst(code, { jsx: true });
|
|
384
|
+
} catch {
|
|
385
|
+
return [];
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const declarations: string[] = [];
|
|
389
|
+
for (const node of program.body as any[]) {
|
|
390
|
+
// Skip imports (handled by extractImportDeclarations)
|
|
391
|
+
if (node.type === "ImportDeclaration") continue;
|
|
392
|
+
|
|
393
|
+
// VariableDeclaration -- include only if all declarators are safe
|
|
394
|
+
if (node.type === "VariableDeclaration") {
|
|
395
|
+
if (isSafeVariableDeclaration(node, handlerNames)) {
|
|
396
|
+
declarations.push(code.slice(node.start, node.end));
|
|
397
|
+
}
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// FunctionDeclaration -- always safe (body is lazy)
|
|
402
|
+
if (node.type === "FunctionDeclaration") {
|
|
403
|
+
declarations.push(code.slice(node.start, node.end));
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ExportNamedDeclaration with a declaration inside -- strip the export
|
|
408
|
+
if (node.type === "ExportNamedDeclaration" && node.declaration) {
|
|
409
|
+
const decl = node.declaration;
|
|
410
|
+
if (decl.type === "VariableDeclaration") {
|
|
411
|
+
if (isSafeVariableDeclaration(decl, handlerNames)) {
|
|
412
|
+
declarations.push(code.slice(decl.start, decl.end));
|
|
413
|
+
}
|
|
414
|
+
} else if (decl.type === "FunctionDeclaration") {
|
|
415
|
+
declarations.push(code.slice(decl.start, decl.end));
|
|
416
|
+
}
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Skip: ClassDeclaration (field initializers can reference imports),
|
|
421
|
+
// ExportDefaultDeclaration, ExportAllDeclaration,
|
|
422
|
+
// ExportNamedDeclaration without declaration (re-exports),
|
|
423
|
+
// ExpressionStatement (side effects), etc.
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return declarations;
|
|
427
|
+
}
|
|
428
|
+
|
|
267
429
|
// ---------------------------------------------------------------------------
|
|
268
430
|
// Transform
|
|
269
431
|
// ---------------------------------------------------------------------------
|
|
@@ -295,6 +457,17 @@ export function transformInlineHandlers(
|
|
|
295
457
|
|
|
296
458
|
const imports = extractImportDeclarations(code, parseAst);
|
|
297
459
|
|
|
460
|
+
// Collect local names for both Static and Prerender to exclude their
|
|
461
|
+
// declarations from virtual modules (avoids circular extraction).
|
|
462
|
+
const staticNames = getImportedLocalNames(code, "Static", parseAst);
|
|
463
|
+
const prerenderNames = getImportedLocalNames(code, "Prerender", parseAst);
|
|
464
|
+
const handlerNames = new Set([...staticNames, ...prerenderNames]);
|
|
465
|
+
const declarations = extractModuleLevelDeclarations(
|
|
466
|
+
code,
|
|
467
|
+
parseAst,
|
|
468
|
+
handlerNames,
|
|
469
|
+
);
|
|
470
|
+
|
|
298
471
|
// Track line occurrences for same-line collision handling
|
|
299
472
|
const lineCounts = new Map<number, number>();
|
|
300
473
|
|
|
@@ -316,6 +489,7 @@ export function transformInlineHandlers(
|
|
|
316
489
|
virtualRegistry.set(virtualId, {
|
|
317
490
|
originalModuleId: moduleId,
|
|
318
491
|
imports,
|
|
492
|
+
declarations,
|
|
319
493
|
handlerCode,
|
|
320
494
|
exportName,
|
|
321
495
|
});
|
|
@@ -325,9 +499,7 @@ export function transformInlineHandlers(
|
|
|
325
499
|
|
|
326
500
|
// Build the import specifier for this virtual module
|
|
327
501
|
const importId = `${virtualPrefix}${filePath}:${site.lineNumber}${lineCount > 0 ? `:${lineCount}` : ""}`;
|
|
328
|
-
importStatements.push(
|
|
329
|
-
`import { ${exportName} } from "${importId}";`,
|
|
330
|
-
);
|
|
502
|
+
importStatements.push(`import { ${exportName} } from "${importId}";`);
|
|
331
503
|
}
|
|
332
504
|
|
|
333
505
|
// Insert imports after directive prologue + existing import block
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import packageJson from "../../../package.json" with { type: "json" };
|
|
2
|
+
|
|
3
|
+
export const rangoVersion: string = packageJson.version;
|
|
4
|
+
|
|
5
|
+
let _bannerPrinted = false;
|
|
6
|
+
|
|
7
|
+
export function printBanner(
|
|
8
|
+
mode: "dev" | "build" | "preview",
|
|
9
|
+
preset: string,
|
|
10
|
+
version: string,
|
|
11
|
+
): void {
|
|
12
|
+
if (_bannerPrinted) return;
|
|
13
|
+
_bannerPrinted = true;
|
|
14
|
+
|
|
15
|
+
// ANSI codes
|
|
16
|
+
const dim = "\x1b[2m";
|
|
17
|
+
const bold = "\x1b[1m";
|
|
18
|
+
const reset = "\x1b[0m";
|
|
19
|
+
|
|
20
|
+
const banner = `
|
|
21
|
+
${dim} ✦ ✦ ✧. . .${reset}
|
|
22
|
+
${dim} ╱${reset} ${bold}╔═╗${reset}${dim} * ╱ ✦ *${reset}
|
|
23
|
+
${dim} ${reset}${bold}║ ║${reset} ${bold}╔═╗${reset}${dim} * ✧. ╱${reset}
|
|
24
|
+
${dim} ${reset}${bold}╔╗ ║ ║ ║ ║${reset}${dim} * ╱${reset}
|
|
25
|
+
${dim} ${reset}${bold}║║ ║ ║ ║ ║ ╦═╗╔═╗╔╗╔╔═╗╔═╗${reset}${dim} ✧ ✦${reset}
|
|
26
|
+
${dim} ${reset}${bold}║║ ║ ╠═╝ ║ ╠╦╝╠═╣║║║║ ╦║ ║${reset}${dim} * ✧${reset}
|
|
27
|
+
${dim} ${reset}${bold}║╚═╝ ╔═══╝ ╩╚═╩ ╩╝╚╝╚═╝╚═╝${reset}${dim} ✦ . *${reset}
|
|
28
|
+
${dim} ${reset}${bold}╚══╗ ║${reset}${dim} * RSC Wrangler ✧ ✦${reset}
|
|
29
|
+
${dim} * ${reset}${bold}║ ║${reset}${dim} * ✧. ╱${reset}
|
|
30
|
+
${dim} ${reset}${bold}═══╝ ╚════${reset}${dim} ✦ *${reset}
|
|
31
|
+
|
|
32
|
+
v${version} · ${preset} · ${mode}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
console.log(banner);
|
|
36
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
skipStringOrComment,
|
|
3
|
+
escapeRegExp,
|
|
4
|
+
} from "../plugins/expose-id-utils.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Find matching close paren in bundled code using depth counting.
|
|
8
|
+
* Uses skipStringOrComment from expose-id-utils to correctly handle
|
|
9
|
+
* template literal ${...} expressions, comments, and nested strings.
|
|
10
|
+
* Returns the position after the closing paren, or -1 if unmatched.
|
|
11
|
+
* @internal Exported for testing only.
|
|
12
|
+
*/
|
|
13
|
+
export function findMatchingParenInBundle(
|
|
14
|
+
code: string,
|
|
15
|
+
openParenPos: number,
|
|
16
|
+
): number {
|
|
17
|
+
let depth = 1;
|
|
18
|
+
let pos = openParenPos;
|
|
19
|
+
while (pos < code.length && depth > 0) {
|
|
20
|
+
const skipped = skipStringOrComment(code, pos);
|
|
21
|
+
if (skipped > pos) {
|
|
22
|
+
pos = skipped;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (code[pos] === "(") depth++;
|
|
26
|
+
else if (code[pos] === ")") depth--;
|
|
27
|
+
pos++;
|
|
28
|
+
}
|
|
29
|
+
return depth === 0 ? pos : -1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Scan a bundled chunk for handler exports of a given type and extract
|
|
34
|
+
* their names + $$id values. Optionally detects passthrough flag.
|
|
35
|
+
* @internal Exported for testing only.
|
|
36
|
+
*/
|
|
37
|
+
export function extractHandlerExportsFromChunk(
|
|
38
|
+
chunkCode: string,
|
|
39
|
+
handlerModules: Map<string, string[]>,
|
|
40
|
+
fnName: string,
|
|
41
|
+
detectPassthrough: boolean,
|
|
42
|
+
): Array<{ name: string; handlerId: string; passthrough: boolean }> {
|
|
43
|
+
const handlers: Array<{
|
|
44
|
+
name: string;
|
|
45
|
+
handlerId: string;
|
|
46
|
+
passthrough: boolean;
|
|
47
|
+
}> = [];
|
|
48
|
+
|
|
49
|
+
for (const [, handlerNames] of handlerModules) {
|
|
50
|
+
for (const name of handlerNames) {
|
|
51
|
+
const eName = escapeRegExp(name);
|
|
52
|
+
const idPattern = new RegExp(
|
|
53
|
+
`(?<![a-zA-Z0-9_])${eName}\\.\\$\\$id\\s*=\\s*"([^"]+)"`,
|
|
54
|
+
);
|
|
55
|
+
const match = chunkCode.match(idPattern);
|
|
56
|
+
if (!match) continue;
|
|
57
|
+
|
|
58
|
+
let isPassthrough = false;
|
|
59
|
+
if (detectPassthrough) {
|
|
60
|
+
const eFnName = escapeRegExp(fnName);
|
|
61
|
+
const callStartRe = new RegExp(
|
|
62
|
+
`const\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
63
|
+
);
|
|
64
|
+
const callStart = callStartRe.exec(chunkCode);
|
|
65
|
+
if (callStart) {
|
|
66
|
+
const afterOpen = callStart.index + callStart[0].length;
|
|
67
|
+
const closePos = findMatchingParenInBundle(chunkCode, afterOpen);
|
|
68
|
+
if (closePos !== -1) {
|
|
69
|
+
const callBody = chunkCode.slice(callStart.index, closePos);
|
|
70
|
+
isPassthrough = /passthrough\s*:\s*(!0|true)/.test(callBody);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
handlers.push({ name, handlerId: match[1], passthrough: isPassthrough });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return handlers;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Evict handler code from a bundled chunk, replacing full handler call
|
|
83
|
+
* expressions with lightweight stub objects. Returns the modified code
|
|
84
|
+
* and bytes saved, or null if no changes were made.
|
|
85
|
+
* @internal Exported for testing only.
|
|
86
|
+
*/
|
|
87
|
+
export function evictHandlerCode(
|
|
88
|
+
code: string,
|
|
89
|
+
exports: Array<{ name: string; handlerId: string; passthrough?: boolean }>,
|
|
90
|
+
fnName: string,
|
|
91
|
+
brand: string,
|
|
92
|
+
): { code: string; savedBytes: number } | null {
|
|
93
|
+
const originalSize = Buffer.byteLength(code);
|
|
94
|
+
let modified = code;
|
|
95
|
+
|
|
96
|
+
const eFnName = escapeRegExp(fnName);
|
|
97
|
+
for (const { name, handlerId, passthrough } of exports) {
|
|
98
|
+
if (passthrough) continue;
|
|
99
|
+
|
|
100
|
+
const eName = escapeRegExp(name);
|
|
101
|
+
const callStartRe = new RegExp(
|
|
102
|
+
`const\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`,
|
|
103
|
+
);
|
|
104
|
+
const startMatch = callStartRe.exec(modified);
|
|
105
|
+
if (!startMatch) continue;
|
|
106
|
+
|
|
107
|
+
const afterOpen = startMatch.index + startMatch[0].length;
|
|
108
|
+
const closePos = findMatchingParenInBundle(modified, afterOpen);
|
|
109
|
+
if (closePos === -1) continue;
|
|
110
|
+
|
|
111
|
+
// Skip trailing whitespace and optional semicolon
|
|
112
|
+
let rangeEnd = closePos;
|
|
113
|
+
while (rangeEnd < modified.length && /\s/.test(modified[rangeEnd]))
|
|
114
|
+
rangeEnd++;
|
|
115
|
+
if (modified[rangeEnd] === ";") rangeEnd++;
|
|
116
|
+
|
|
117
|
+
// Validate: matched range must contain the expected handlerId
|
|
118
|
+
const matched = modified.slice(startMatch.index, rangeEnd);
|
|
119
|
+
if (!matched.includes(handlerId)) continue;
|
|
120
|
+
|
|
121
|
+
const stub = `const ${name} = { __brand: "${brand}", $$id: "${handlerId}" };`;
|
|
122
|
+
modified =
|
|
123
|
+
modified.slice(0, startMatch.index) + stub + modified.slice(rangeEnd);
|
|
124
|
+
|
|
125
|
+
// Remove the now-redundant $$id assignment line.
|
|
126
|
+
modified = modified.replace(
|
|
127
|
+
new RegExp(`\\n${eName}\\.\\$\\$id\\s*=\\s*"[^"]+";`),
|
|
128
|
+
"",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (modified === code) return null;
|
|
133
|
+
return {
|
|
134
|
+
code: modified,
|
|
135
|
+
savedBytes: originalSize - Buffer.byteLength(modified),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten prefix tree leaf nodes into precomputed route entries.
|
|
3
|
+
* Leaf nodes have no children (no nested includes), so their routes can be
|
|
4
|
+
* used directly by evaluateLazyEntry() without running the handler.
|
|
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
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Wrap a value as `JSON.parse('...')` instead of a JS object literal.
|
|
63
|
+
* V8's JSON parser is significantly faster than its full JS parser for large
|
|
64
|
+
* objects, so this improves startup time for big route manifests.
|
|
65
|
+
*/
|
|
66
|
+
export function jsonParseExpression(value: unknown): string {
|
|
67
|
+
const json = JSON.stringify(value);
|
|
68
|
+
const escaped = json.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
69
|
+
return `JSON.parse('${escaped}')`;
|
|
70
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
|
-
import packageJson from "
|
|
10
|
+
import packageJson from "../../../package.json" with { type: "json" };
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The canonical name used in virtual entries (without scope)
|
|
@@ -46,21 +46,26 @@ export function isWorkspaceDevelopment(): boolean {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Subpaths
|
|
50
|
-
*
|
|
49
|
+
* Subpaths derived from package.json exports that use TypeScript source.
|
|
50
|
+
* These must be excluded from Vite's dependency optimization (they ship
|
|
51
|
+
* as .ts/.tsx, not compiled JS) and aliased when installed from npm.
|
|
52
|
+
*
|
|
53
|
+
* Derived automatically from the exports field to prevent drift.
|
|
51
54
|
*/
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
const SOURCE_EXPORT_SUBPATHS = Object.keys(packageJson.exports)
|
|
56
|
+
.filter((key) => {
|
|
57
|
+
const entry = (
|
|
58
|
+
packageJson.exports as Record<string, Record<string, string>>
|
|
59
|
+
)[key];
|
|
60
|
+
// Include if any non-types condition points to TypeScript source
|
|
61
|
+
return Object.entries(entry).some(
|
|
62
|
+
([condition, path]) =>
|
|
63
|
+
condition !== "types" &&
|
|
64
|
+
typeof path === "string" &&
|
|
65
|
+
/\.tsx?$/.test(path),
|
|
66
|
+
);
|
|
67
|
+
})
|
|
68
|
+
.map((key) => key.replace(/^\./, ""));
|
|
64
69
|
|
|
65
70
|
/**
|
|
66
71
|
* Generate the list of modules to exclude from Vite's dependency optimization.
|
|
@@ -72,7 +77,7 @@ export function getExcludeDeps(): string[] {
|
|
|
72
77
|
const packageName = getPublishedPackageName();
|
|
73
78
|
const excludes: string[] = [];
|
|
74
79
|
|
|
75
|
-
for (const subpath of
|
|
80
|
+
for (const subpath of SOURCE_EXPORT_SUBPATHS) {
|
|
76
81
|
// Add scoped package paths
|
|
77
82
|
excludes.push(`${packageName}${subpath}`);
|
|
78
83
|
// Add virtual/aliased paths (before alias resolution)
|
|
@@ -85,20 +90,11 @@ export function getExcludeDeps(): string[] {
|
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
/**
|
|
88
|
-
* Subpaths that need aliasing
|
|
93
|
+
* Subpaths that need aliasing — same as SOURCE_EXPORT_SUBPATHS.
|
|
94
|
+
* When installed from npm, virtual entries may use a different package name
|
|
95
|
+
* than the published one; aliases bridge them.
|
|
89
96
|
*/
|
|
90
|
-
const ALIAS_SUBPATHS =
|
|
91
|
-
"/internal/deps/browser",
|
|
92
|
-
"/internal/deps/ssr",
|
|
93
|
-
"/internal/deps/rsc",
|
|
94
|
-
"/internal/deps/html-stream-client",
|
|
95
|
-
"/internal/deps/html-stream-server",
|
|
96
|
-
"/browser",
|
|
97
|
-
"/client",
|
|
98
|
-
"/server",
|
|
99
|
-
"/rsc",
|
|
100
|
-
"/ssr",
|
|
101
|
-
] as const;
|
|
97
|
+
const ALIAS_SUBPATHS = SOURCE_EXPORT_SUBPATHS;
|
|
102
98
|
|
|
103
99
|
/**
|
|
104
100
|
* Generate aliases to map virtual package paths to the actual published package.
|