@rangojs/router 0.0.0-experimental.110 → 0.0.0-experimental.112
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/dist/bin/rango.js +41 -37
- package/dist/vite/index.js +144 -191
- package/package.json +17 -14
- package/skills/handler-use/SKILL.md +1 -1
- package/skills/rango/SKILL.md +20 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/event-controller.ts +42 -66
- package/src/browser/navigation-bridge.ts +4 -0
- package/src/browser/navigation-client.ts +12 -15
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +7 -21
- package/src/browser/partial-update.ts +8 -16
- package/src/browser/react/NavigationProvider.tsx +29 -40
- package/src/browser/react/use-params.ts +3 -4
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +16 -2
- package/src/browser/server-action-bridge.ts +23 -30
- package/src/browser/types.ts +2 -0
- package/src/build/generate-manifest.ts +29 -31
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/route-types/router-processing.ts +37 -9
- package/src/build/runtime-discovery.ts +9 -20
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +29 -0
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +1 -0
- package/src/response-utils.ts +9 -0
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +231 -259
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +19 -41
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/match-result.ts +32 -30
- package/src/router/middleware.ts +46 -78
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +4 -28
- package/src/rsc/handler.ts +20 -65
- package/src/rsc/helpers.ts +3 -2
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/response-route-handler.ts +32 -52
- package/src/rsc/rsc-rendering.ts +27 -53
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +13 -37
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/segment-system.tsx +5 -39
- package/src/server/context.ts +76 -35
- package/src/urls/include-helper.ts +10 -53
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +2 -19
- package/src/urls/response-types.ts +20 -19
- package/src/urls/type-extraction.ts +20 -115
- package/src/urls/urls-function.ts +1 -5
- package/src/vite/discovery/discover-routers.ts +10 -22
- package/src/vite/discovery/route-types-writer.ts +38 -82
- package/src/vite/plugins/cjs-to-esm.ts +3 -7
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-internal-ids.ts +34 -62
- package/src/vite/plugins/version-injector.ts +2 -12
- package/src/vite/router-discovery.ts +71 -26
- package/src/vite/utils/shared-utils.ts +13 -1
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -32,21 +32,32 @@ type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
|
|
|
32
32
|
* Symbol marking a route as a response route (non-RSC).
|
|
33
33
|
* Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
|
|
34
34
|
*/
|
|
35
|
-
export const RESPONSE_TYPE: unique symbol = Symbol.for(
|
|
36
|
-
"rangojs.responseType",
|
|
37
|
-
) as any;
|
|
35
|
+
export const RESPONSE_TYPE: unique symbol = Symbol.for("rangojs.responseType");
|
|
38
36
|
|
|
39
37
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* Shared shape of a response-route handler: a function returning TReturn (or a
|
|
39
|
+
* promise of it), plus an optional composable `use` thunk merged at mount time.
|
|
42
40
|
*/
|
|
43
|
-
|
|
41
|
+
type ResponseHandlerOf<
|
|
42
|
+
TReturn,
|
|
43
|
+
TParams = Record<string, string>,
|
|
44
|
+
TEnv = any,
|
|
45
|
+
> = ((
|
|
44
46
|
ctx: ResponseHandlerContext<TParams, TEnv>,
|
|
45
|
-
) =>
|
|
47
|
+
) => TReturn | Promise<TReturn>) & {
|
|
46
48
|
/** Composable default DSL items merged when the handler is mounted. */
|
|
47
49
|
use?: () => UseItems<ResponseRouteUseItem>;
|
|
48
50
|
};
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Handler that must return Response (not ReactNode).
|
|
54
|
+
* Used by path.image(), path.stream(), path.any() (binary/streaming data).
|
|
55
|
+
*/
|
|
56
|
+
export type ResponseHandler<
|
|
57
|
+
TParams = Record<string, string>,
|
|
58
|
+
TEnv = any,
|
|
59
|
+
> = ResponseHandlerOf<Response, TParams, TEnv>;
|
|
60
|
+
|
|
50
61
|
/**
|
|
51
62
|
* JSON-serializable value type for auto-wrap support.
|
|
52
63
|
*/
|
|
@@ -65,12 +76,7 @@ export type JsonValue =
|
|
|
65
76
|
export type JsonResponseHandler<
|
|
66
77
|
TParams = Record<string, string>,
|
|
67
78
|
TEnv = any,
|
|
68
|
-
> =
|
|
69
|
-
ctx: ResponseHandlerContext<TParams, TEnv>,
|
|
70
|
-
) => JsonValue | Response | Promise<JsonValue | Response>) & {
|
|
71
|
-
/** Composable default DSL items merged when the handler is mounted. */
|
|
72
|
-
use?: () => UseItems<ResponseRouteUseItem>;
|
|
73
|
-
};
|
|
79
|
+
> = ResponseHandlerOf<JsonValue | Response, TParams, TEnv>;
|
|
74
80
|
|
|
75
81
|
/**
|
|
76
82
|
* Handler for text-based response routes (text, html, xml).
|
|
@@ -79,12 +85,7 @@ export type JsonResponseHandler<
|
|
|
79
85
|
export type TextResponseHandler<
|
|
80
86
|
TParams = Record<string, string>,
|
|
81
87
|
TEnv = any,
|
|
82
|
-
> =
|
|
83
|
-
ctx: ResponseHandlerContext<TParams, TEnv>,
|
|
84
|
-
) => string | Response | Promise<string | Response>) & {
|
|
85
|
-
/** Composable default DSL items merged when the handler is mounted. */
|
|
86
|
-
use?: () => UseItems<ResponseRouteUseItem>;
|
|
87
|
-
};
|
|
88
|
+
> = ResponseHandlerOf<string | Response, TParams, TEnv>;
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* Lighter handler context for response routes.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ExtractParams } from "../types.js";
|
|
2
1
|
import type { JsonSerialize } from "../serialize.js";
|
|
3
2
|
import type {
|
|
4
3
|
TypedRouteItem,
|
|
@@ -7,11 +6,7 @@ import type {
|
|
|
7
6
|
TypedCacheItem,
|
|
8
7
|
TypedTransitionItem,
|
|
9
8
|
} from "../route-types.js";
|
|
10
|
-
import type {
|
|
11
|
-
LocalOnlyInclude,
|
|
12
|
-
UnnamedRoute,
|
|
13
|
-
UrlPatterns,
|
|
14
|
-
} from "./pattern-types.js";
|
|
9
|
+
import type { LocalOnlyInclude, UnnamedRoute } from "./pattern-types.js";
|
|
15
10
|
|
|
16
11
|
// ============================================================================
|
|
17
12
|
// Route Type Extraction Utilities
|
|
@@ -68,62 +63,6 @@ type PrefixPatterns<
|
|
|
68
63
|
: TRoutes[K];
|
|
69
64
|
};
|
|
70
65
|
|
|
71
|
-
/**
|
|
72
|
-
* Depth counter for limiting recursion (max 40 levels)
|
|
73
|
-
* Supports up to 40 sibling items at any level of a urls() call
|
|
74
|
-
* Note: Higher values hit TypeScript's internal recursion limits
|
|
75
|
-
*/
|
|
76
|
-
type Depth = [
|
|
77
|
-
never,
|
|
78
|
-
0,
|
|
79
|
-
1,
|
|
80
|
-
2,
|
|
81
|
-
3,
|
|
82
|
-
4,
|
|
83
|
-
5,
|
|
84
|
-
6,
|
|
85
|
-
7,
|
|
86
|
-
8,
|
|
87
|
-
9,
|
|
88
|
-
10,
|
|
89
|
-
11,
|
|
90
|
-
12,
|
|
91
|
-
13,
|
|
92
|
-
14,
|
|
93
|
-
15,
|
|
94
|
-
16,
|
|
95
|
-
17,
|
|
96
|
-
18,
|
|
97
|
-
19,
|
|
98
|
-
20,
|
|
99
|
-
21,
|
|
100
|
-
22,
|
|
101
|
-
23,
|
|
102
|
-
24,
|
|
103
|
-
25,
|
|
104
|
-
26,
|
|
105
|
-
27,
|
|
106
|
-
28,
|
|
107
|
-
29,
|
|
108
|
-
30,
|
|
109
|
-
31,
|
|
110
|
-
32,
|
|
111
|
-
33,
|
|
112
|
-
34,
|
|
113
|
-
35,
|
|
114
|
-
36,
|
|
115
|
-
37,
|
|
116
|
-
38,
|
|
117
|
-
39,
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Force TypeScript to eagerly evaluate a type.
|
|
122
|
-
* This helps with interface extension by creating a "concrete" object type.
|
|
123
|
-
*/
|
|
124
|
-
type Simplify<T> =
|
|
125
|
-
T extends Record<string, string> ? { [K in keyof T]: T[K] } : T;
|
|
126
|
-
|
|
127
66
|
/**
|
|
128
67
|
* Convert a union type to an intersection type.
|
|
129
68
|
* Used to combine route maps from multiple siblings without recursive tuple processing.
|
|
@@ -136,13 +75,11 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
|
136
75
|
|
|
137
76
|
/**
|
|
138
77
|
* Extract routes from a single item (path, include, layout, cache with children)
|
|
139
|
-
* D is the current depth level for nested layouts/caches
|
|
140
78
|
*/
|
|
141
|
-
type ExtractRoutesFromItem<T
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
T extends TypedRouteItem<infer TName, infer TPattern, any, infer TSearch>
|
|
79
|
+
type ExtractRoutesFromItem<T> =
|
|
80
|
+
// TypedRouteItem: extract name -> pattern (exclude unnamed routes)
|
|
81
|
+
// When search schema is non-empty, value becomes { path, search } object
|
|
82
|
+
T extends TypedRouteItem<infer TName, infer TPattern, any, infer TSearch>
|
|
146
83
|
? TName extends string
|
|
147
84
|
? TName extends UnnamedRoute
|
|
148
85
|
? {} // Exclude unnamed routes from type map
|
|
@@ -186,14 +123,10 @@ type ExtractRoutesFromItem<T, D extends number = 40> = [D] extends [never]
|
|
|
186
123
|
* Extract routes from an array of items using mapped types.
|
|
187
124
|
* Uses UnionToIntersection to combine routes without recursive tuple processing,
|
|
188
125
|
* removing the sibling limit that was caused by TypeScript recursion limits.
|
|
189
|
-
* D is passed to ExtractRoutesFromItem for nested depth tracking.
|
|
190
126
|
*/
|
|
191
|
-
type ExtractRoutesFromItems<
|
|
192
|
-
T extends readonly any[],
|
|
193
|
-
D extends number = 40,
|
|
194
|
-
> = T extends readonly any[]
|
|
127
|
+
type ExtractRoutesFromItems<T extends readonly any[]> = T extends readonly any[]
|
|
195
128
|
? UnionToIntersection<
|
|
196
|
-
{ [K in keyof T]: ExtractRoutesFromItem<T[K]
|
|
129
|
+
{ [K in keyof T]: ExtractRoutesFromItem<T[K]> }[number]
|
|
197
130
|
> extends infer R
|
|
198
131
|
? R extends Record<string, any>
|
|
199
132
|
? R
|
|
@@ -204,12 +137,8 @@ type ExtractRoutesFromItems<
|
|
|
204
137
|
/**
|
|
205
138
|
* Main utility: extract route map from urls() callback return type
|
|
206
139
|
* Uses mapped types for sibling processing (no sibling limit).
|
|
207
|
-
* Uses Simplify to force eager evaluation for interface extension compatibility.
|
|
208
140
|
*/
|
|
209
|
-
export type ExtractRoutes<T extends readonly any[]> = ExtractRoutesFromItems<
|
|
210
|
-
T,
|
|
211
|
-
40
|
|
212
|
-
>;
|
|
141
|
+
export type ExtractRoutes<T extends readonly any[]> = ExtractRoutesFromItems<T>;
|
|
213
142
|
|
|
214
143
|
// ============================================================================
|
|
215
144
|
// Response Type Extraction Utilities
|
|
@@ -237,9 +166,8 @@ type PrefixKeys<
|
|
|
237
166
|
* Extract response data types from a single item.
|
|
238
167
|
* Parallel to ExtractRoutesFromItem but extracts name -> TData mapping.
|
|
239
168
|
*/
|
|
240
|
-
type ExtractResponsesFromItem<T
|
|
241
|
-
|
|
242
|
-
: T extends TypedRouteItem<infer TName, any, infer TData>
|
|
169
|
+
type ExtractResponsesFromItem<T> =
|
|
170
|
+
T extends TypedRouteItem<infer TName, any, infer TData>
|
|
243
171
|
? TName extends string
|
|
244
172
|
? TName extends UnnamedRoute
|
|
245
173
|
? {}
|
|
@@ -273,46 +201,23 @@ type ExtractResponsesFromItem<T, D extends number = 40> = [D] extends [never]
|
|
|
273
201
|
* Extract responses from an array of items using mapped types.
|
|
274
202
|
* Parallel to ExtractRoutesFromItems.
|
|
275
203
|
*/
|
|
276
|
-
type ExtractResponsesFromItems<
|
|
277
|
-
T extends readonly any[]
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
? R
|
|
204
|
+
type ExtractResponsesFromItems<T extends readonly any[]> =
|
|
205
|
+
T extends readonly any[]
|
|
206
|
+
? UnionToIntersection<
|
|
207
|
+
{ [K in keyof T]: ExtractResponsesFromItem<T[K]> }[number]
|
|
208
|
+
> extends infer R
|
|
209
|
+
? R extends Record<string, unknown>
|
|
210
|
+
? R
|
|
211
|
+
: {}
|
|
285
212
|
: {}
|
|
286
|
-
: {}
|
|
287
|
-
: {};
|
|
213
|
+
: {};
|
|
288
214
|
|
|
289
215
|
/**
|
|
290
216
|
* Main utility: extract response data type map from urls() callback return type.
|
|
291
217
|
* Parallel to ExtractRoutes.
|
|
292
218
|
*/
|
|
293
219
|
export type ExtractResponses<T extends readonly any[]> =
|
|
294
|
-
ExtractResponsesFromItems<T
|
|
295
|
-
|
|
296
|
-
// ============================================================================
|
|
297
|
-
// Type Utilities for path()
|
|
298
|
-
// ============================================================================
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Extract route names from a UrlPatterns result
|
|
302
|
-
* Used for type-safe href() generation
|
|
303
|
-
*/
|
|
304
|
-
export type ExtractRouteNames<T extends UrlPatterns<any>> =
|
|
305
|
-
T extends UrlPatterns<infer _TEnv>
|
|
306
|
-
? string // For now, will be refined with full implementation
|
|
307
|
-
: never;
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Extract params for a specific route name
|
|
311
|
-
*/
|
|
312
|
-
export type ExtractPathParams<
|
|
313
|
-
T extends UrlPatterns<any>,
|
|
314
|
-
K extends string,
|
|
315
|
-
> = ExtractParams<string>; // Will be refined with pattern tracking
|
|
220
|
+
ExtractResponsesFromItems<T>;
|
|
316
221
|
|
|
317
222
|
// ============================================================================
|
|
318
223
|
// Response Envelope Types
|
|
@@ -3,7 +3,7 @@ import type { AllUseItems } from "../route-types.js";
|
|
|
3
3
|
import { getContext } from "../server/context";
|
|
4
4
|
import { invariant } from "../errors";
|
|
5
5
|
import { createRouteHelpers } from "../route-definition.js";
|
|
6
|
-
import type {
|
|
6
|
+
import type { UrlPatterns } from "./pattern-types.js";
|
|
7
7
|
import type { PathHelpers } from "./path-helper-types.js";
|
|
8
8
|
import type { ExtractRoutes, ExtractResponses } from "./type-extraction.js";
|
|
9
9
|
import { createPathHelper, attachPathResponseTags } from "./path-helper.js";
|
|
@@ -34,9 +34,6 @@ export function urls<
|
|
|
34
34
|
>(
|
|
35
35
|
builder: (helpers: PathHelpers<TEnv>) => TItems,
|
|
36
36
|
): UrlPatterns<TEnv, ExtractRoutes<TItems>, ExtractResponses<TItems>> {
|
|
37
|
-
// Collect path definitions during build
|
|
38
|
-
const definitions: PathDefinition[] = [];
|
|
39
|
-
|
|
40
37
|
// Create the handler function that will be called by the router
|
|
41
38
|
const handler = () => {
|
|
42
39
|
invariant(
|
|
@@ -82,7 +79,6 @@ export function urls<
|
|
|
82
79
|
// trailingSlash config is populated when handler() runs
|
|
83
80
|
// We expose it via a getter that reads from the context after handler execution
|
|
84
81
|
return {
|
|
85
|
-
definitions,
|
|
86
82
|
handler,
|
|
87
83
|
get trailingSlash() {
|
|
88
84
|
// Get the trailingSlash map from the current context
|
|
@@ -292,18 +292,16 @@ export async function discoverRouters(
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
// buildRouteTrie reads these via ?.has / ?.[] — empty is observationally
|
|
296
|
+
// identical to undefined, so no empty->undefined coercion is needed.
|
|
295
297
|
newMergedRouteTrie = buildRouteTrie(
|
|
296
298
|
newMergedRouteManifest,
|
|
297
299
|
mergedRouteAncestry,
|
|
298
300
|
routeToStaticPrefix,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
|
|
304
|
-
Object.keys(mergedResponseTypeRoutes).length > 0
|
|
305
|
-
? mergedResponseTypeRoutes
|
|
306
|
-
: undefined,
|
|
301
|
+
mergedRouteTrailingSlash,
|
|
302
|
+
prerenderRouteNames,
|
|
303
|
+
passthroughRouteNames,
|
|
304
|
+
mergedResponseTypeRoutes,
|
|
307
305
|
);
|
|
308
306
|
|
|
309
307
|
// Build per-router tries for multi-router isolation.
|
|
@@ -330,20 +328,10 @@ export async function discoverRouters(
|
|
|
330
328
|
manifest.routeManifest,
|
|
331
329
|
manifest._routeAncestry,
|
|
332
330
|
perRouterStaticPrefix,
|
|
333
|
-
manifest.routeTrailingSlash
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
perRouterPrerenderNames && perRouterPrerenderNames.size > 0
|
|
338
|
-
? perRouterPrerenderNames
|
|
339
|
-
: undefined,
|
|
340
|
-
perRouterPassthroughNames && perRouterPassthroughNames.size > 0
|
|
341
|
-
? perRouterPassthroughNames
|
|
342
|
-
: undefined,
|
|
343
|
-
manifest.responseTypeRoutes &&
|
|
344
|
-
Object.keys(manifest.responseTypeRoutes).length > 0
|
|
345
|
-
? manifest.responseTypeRoutes
|
|
346
|
-
: undefined,
|
|
331
|
+
manifest.routeTrailingSlash,
|
|
332
|
+
perRouterPrerenderNames,
|
|
333
|
+
perRouterPassthroughNames,
|
|
334
|
+
manifest.responseTypeRoutes,
|
|
347
335
|
);
|
|
348
336
|
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
349
337
|
}
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
* from discovered router manifests and static source parsing.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { dirname,
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
9
|
import { readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs";
|
|
10
10
|
import {
|
|
11
11
|
generateRouteTypesSource,
|
|
12
12
|
writeCombinedRouteTypes,
|
|
13
13
|
findRouterFiles,
|
|
14
14
|
buildCombinedRouteMapForRouterFile,
|
|
15
|
+
genFileTsPath,
|
|
16
|
+
resolveSearchSchemas,
|
|
15
17
|
} from "../../build/generate-route-types.js";
|
|
16
18
|
import type { DiscoveryState } from "./state.js";
|
|
17
19
|
import { markSelfGenWrite } from "./self-gen-tracking.js";
|
|
@@ -35,6 +37,22 @@ function filterUserNamedRoutes(
|
|
|
35
37
|
return filtered;
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
// Write a gen file only when content changed, marking the write as
|
|
41
|
+
// self-generated BEFORE writeFileSync so the watcher distinguishes it from a
|
|
42
|
+
// manual edit (the HMR self-gen-loop guard).
|
|
43
|
+
function writeGenFileIfChanged(
|
|
44
|
+
state: DiscoveryState,
|
|
45
|
+
outPath: string,
|
|
46
|
+
source: string,
|
|
47
|
+
opts?: { log?: boolean },
|
|
48
|
+
): void {
|
|
49
|
+
const existing = existsSync(outPath) ? readFileSync(outPath, "utf-8") : null;
|
|
50
|
+
if (existing === source) return;
|
|
51
|
+
markSelfGenWrite(state, outPath, source);
|
|
52
|
+
writeFileSync(outPath, source);
|
|
53
|
+
if (opts?.log) console.log(`[rango] Generated route types -> ${outPath}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
/**
|
|
39
57
|
* Write combined route types for all router files.
|
|
40
58
|
* Only writes when content has changed to avoid triggering HMR loops.
|
|
@@ -48,45 +66,16 @@ export function writeCombinedRouteTypesWithTracking(
|
|
|
48
66
|
findRouterFiles(state.projectRoot, state.scanFilter);
|
|
49
67
|
state.cachedRouterFiles = routerFiles;
|
|
50
68
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
preContent.set(outPath, readFileSync(outPath, "utf-8"));
|
|
62
|
-
} catch {
|
|
63
|
-
// File doesn't exist yet — any write is a real change.
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
writeCombinedRouteTypes(state.projectRoot, routerFiles, opts);
|
|
68
|
-
|
|
69
|
-
// Mark only files that were actually written so the watcher can
|
|
70
|
-
// distinguish self-triggered change events from manual edits.
|
|
71
|
-
// Marking unchanged files creates stale entries that interfere with
|
|
72
|
-
// multi-server setups (e.g. shared webServer + isolated HMR server).
|
|
73
|
-
for (const routerFilePath of routerFiles) {
|
|
74
|
-
const routerDir = dirname(routerFilePath);
|
|
75
|
-
const routerBasename = basename(routerFilePath).replace(
|
|
76
|
-
/\.(tsx?|jsx?)$/,
|
|
77
|
-
"",
|
|
78
|
-
);
|
|
79
|
-
const outPath = join(routerDir, `${routerBasename}.named-routes.gen.ts`);
|
|
80
|
-
if (!existsSync(outPath)) continue;
|
|
81
|
-
try {
|
|
82
|
-
const content = readFileSync(outPath, "utf-8");
|
|
83
|
-
if (content !== preContent.get(outPath)) {
|
|
84
|
-
markSelfGenWrite(state, outPath, content);
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
// Ignore transient fs errors while files are being rewritten.
|
|
88
|
-
}
|
|
89
|
-
}
|
|
69
|
+
// Mark each gen file as self-generated BEFORE it is written, via the onWrite
|
|
70
|
+
// callback fired at every writeFileSync site, so the watcher distinguishes
|
|
71
|
+
// self-triggered change events from manual edits. The callback fires only
|
|
72
|
+
// for files actually written, so unchanged files are never marked (stale
|
|
73
|
+
// entries interfere with multi-server setups such as a shared webServer plus
|
|
74
|
+
// an isolated HMR server).
|
|
75
|
+
writeCombinedRouteTypes(state.projectRoot, routerFiles, {
|
|
76
|
+
...opts,
|
|
77
|
+
onWrite: (outPath, content) => markSelfGenWrite(state, outPath, content),
|
|
78
|
+
});
|
|
90
79
|
}
|
|
91
80
|
|
|
92
81
|
/**
|
|
@@ -128,34 +117,16 @@ export function writeRouteTypesFiles(state: DiscoveryState): void {
|
|
|
128
117
|
);
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
const
|
|
132
|
-
const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
133
|
-
const outPath = join(routerDir, `${routerBasename}.named-routes.gen.ts`);
|
|
120
|
+
const outPath = genFileTsPath(sourceFile);
|
|
134
121
|
|
|
135
122
|
// Filter out auto-generated route names (e.g. "$path____debug_reverse-test")
|
|
136
123
|
// to match the static parser's output and prevent HMR oscillation.
|
|
137
124
|
const userRoutes = filterUserNamedRoutes(routeManifest);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
(!effectiveSearchSchemas ||
|
|
144
|
-
Object.keys(effectiveSearchSchemas).length === 0) &&
|
|
145
|
-
sourceFile
|
|
146
|
-
) {
|
|
147
|
-
const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
|
|
148
|
-
if (Object.keys(staticParsed.searchSchemas).length > 0) {
|
|
149
|
-
const filtered: Record<string, Record<string, string>> = {};
|
|
150
|
-
for (const name of Object.keys(userRoutes)) {
|
|
151
|
-
const schema = staticParsed.searchSchemas[name];
|
|
152
|
-
if (schema) filtered[name] = schema;
|
|
153
|
-
}
|
|
154
|
-
if (Object.keys(filtered).length > 0) {
|
|
155
|
-
effectiveSearchSchemas = filtered;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
125
|
+
const effectiveSearchSchemas = resolveSearchSchemas(
|
|
126
|
+
Object.keys(userRoutes),
|
|
127
|
+
routeSearchSchemas,
|
|
128
|
+
sourceFile,
|
|
129
|
+
);
|
|
159
130
|
|
|
160
131
|
const source = generateRouteTypesSource(
|
|
161
132
|
userRoutes,
|
|
@@ -163,14 +134,7 @@ export function writeRouteTypesFiles(state: DiscoveryState): void {
|
|
|
163
134
|
? effectiveSearchSchemas
|
|
164
135
|
: undefined,
|
|
165
136
|
);
|
|
166
|
-
|
|
167
|
-
? readFileSync(outPath, "utf-8")
|
|
168
|
-
: null;
|
|
169
|
-
if (existing !== source) {
|
|
170
|
-
markSelfGenWrite(state, outPath, source);
|
|
171
|
-
writeFileSync(outPath, source);
|
|
172
|
-
console.log(`[rango] Generated route types -> ${outPath}`);
|
|
173
|
-
}
|
|
137
|
+
writeGenFileIfChanged(state, outPath, source, { log: true });
|
|
174
138
|
}
|
|
175
139
|
}
|
|
176
140
|
|
|
@@ -236,22 +200,14 @@ export function supplementGenFilesWithRuntimeRoutes(
|
|
|
236
200
|
}
|
|
237
201
|
}
|
|
238
202
|
|
|
239
|
-
const
|
|
240
|
-
const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
241
|
-
const outPath = join(routerDir, `${routerBasename}.named-routes.gen.ts`);
|
|
203
|
+
const outPath = genFileTsPath(sourceFile);
|
|
242
204
|
const source = generateRouteTypesSource(
|
|
243
205
|
mergedRoutes,
|
|
244
206
|
Object.keys(mergedSearchSchemas).length > 0
|
|
245
207
|
? mergedSearchSchemas
|
|
246
208
|
: undefined,
|
|
247
209
|
);
|
|
248
|
-
|
|
249
|
-
? readFileSync(outPath, "utf-8")
|
|
250
|
-
: null;
|
|
251
|
-
if (existing !== source) {
|
|
252
|
-
markSelfGenWrite(state, outPath, source);
|
|
253
|
-
writeFileSync(outPath, source);
|
|
254
|
-
}
|
|
210
|
+
writeGenFileIfChanged(state, outPath, source);
|
|
255
211
|
}
|
|
256
212
|
// No manual manifest update needed: the virtual module imports the gen
|
|
257
213
|
// file, so Vite's HMR automatically re-evaluates it with fresh data.
|
|
@@ -12,13 +12,10 @@ export function createCjsToEsmPlugin(): Plugin {
|
|
|
12
12
|
name: "@rangojs/router:cjs-to-esm",
|
|
13
13
|
enforce: "pre",
|
|
14
14
|
transform(code, id) {
|
|
15
|
-
const cleanId = id.split("?")[0];
|
|
15
|
+
const cleanId = id.split("?")[0].replaceAll("\\", "/");
|
|
16
16
|
|
|
17
17
|
// Transform the client.browser.js entry point to re-export from CJS
|
|
18
|
-
if (
|
|
19
|
-
cleanId.includes("vendor/react-server-dom/client.browser.js") ||
|
|
20
|
-
cleanId.includes("vendor\\react-server-dom\\client.browser.js")
|
|
21
|
-
) {
|
|
18
|
+
if (cleanId.includes("vendor/react-server-dom/client.browser.js")) {
|
|
22
19
|
const isProd = process.env.NODE_ENV === "production";
|
|
23
20
|
const cjsFile = isProd
|
|
24
21
|
? "./cjs/react-server-dom-webpack-client.browser.production.js"
|
|
@@ -33,8 +30,7 @@ export function createCjsToEsmPlugin(): Plugin {
|
|
|
33
30
|
|
|
34
31
|
// Transform the actual CJS files to ESM
|
|
35
32
|
if (
|
|
36
|
-
|
|
37
|
-
cleanId.includes("vendor\\react-server-dom\\cjs\\")) &&
|
|
33
|
+
cleanId.includes("vendor/react-server-dom/cjs/") &&
|
|
38
34
|
cleanId.includes("client.browser")
|
|
39
35
|
) {
|
|
40
36
|
let transformed = code;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
|
-
import {
|
|
2
|
+
import { makeStubId } from "../expose-id-utils.js";
|
|
3
3
|
import type { HandlerTransformConfig, CreateExportBinding } from "./types.js";
|
|
4
4
|
import { isExportOnlyFile } from "./export-analysis.js";
|
|
5
5
|
|
|
@@ -28,9 +28,7 @@ export function transformHandles(
|
|
|
28
28
|
binding.callCloseParenPos,
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
-
const handleId = isBuild
|
|
32
|
-
? hashId(filePath, exportName)
|
|
33
|
-
: `${filePath}#${exportName}`;
|
|
31
|
+
const handleId = makeStubId(filePath, exportName, isBuild);
|
|
34
32
|
|
|
35
33
|
let paramInjection: string;
|
|
36
34
|
if (!args.hasArgs) {
|
|
@@ -58,9 +56,7 @@ export function transformLocationState(
|
|
|
58
56
|
for (const binding of bindings) {
|
|
59
57
|
const exportName = binding.exportNames[0];
|
|
60
58
|
|
|
61
|
-
const stateKey = isBuild
|
|
62
|
-
? hashId(filePath, exportName)
|
|
63
|
-
: `${filePath}#${exportName}`;
|
|
59
|
+
const stateKey = makeStubId(filePath, exportName, isBuild);
|
|
64
60
|
|
|
65
61
|
// Key is injected as a property assignment (not as a function argument).
|
|
66
62
|
// This allows createLocationState to accept options like { flash: true }
|
|
@@ -88,7 +84,7 @@ export function generateWholeFileStubs(
|
|
|
88
84
|
|
|
89
85
|
const exportNames = bindings.flatMap((b) => b.exportNames);
|
|
90
86
|
const stubs = exportNames.map((name) => {
|
|
91
|
-
const handlerId =
|
|
87
|
+
const handlerId = makeStubId(filePath, name, isBuild);
|
|
92
88
|
return `export const ${name} = { __brand: "${cfg.brand}", $$id: "${handlerId}" };`;
|
|
93
89
|
});
|
|
94
90
|
|
|
@@ -96,53 +92,8 @@ export function generateWholeFileStubs(
|
|
|
96
92
|
}
|
|
97
93
|
|
|
98
94
|
/**
|
|
99
|
-
* Replace handler call expressions with lightweight stub objects
|
|
100
|
-
*
|
|
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
|
-
* Replace handler call expressions with lightweight stub objects on an
|
|
143
|
-
* existing MagicString. Unlike generateExprStubs (which creates its own
|
|
144
|
-
* MagicString and returns the full result), this integrates into the
|
|
145
|
-
* unified transform pipeline so all transforms share one sourcemap.
|
|
95
|
+
* Replace handler call expressions with lightweight stub objects on the shared
|
|
96
|
+
* unified-pipeline MagicString so all transforms share one sourcemap.
|
|
146
97
|
*/
|
|
147
98
|
export function stubHandlerExprs(
|
|
148
99
|
cfg: HandlerTransformConfig,
|
|
@@ -154,9 +105,7 @@ export function stubHandlerExprs(
|
|
|
154
105
|
let hasChanges = false;
|
|
155
106
|
for (const binding of bindings) {
|
|
156
107
|
const exportName = binding.exportNames[0];
|
|
157
|
-
const handlerId = isBuild
|
|
158
|
-
? hashId(filePath, exportName)
|
|
159
|
-
: `${filePath}#${exportName}`;
|
|
108
|
+
const handlerId = makeStubId(filePath, exportName, isBuild);
|
|
160
109
|
|
|
161
110
|
s.overwrite(
|
|
162
111
|
binding.callExprStart,
|
|
@@ -182,9 +131,7 @@ export function transformHandlerIds(
|
|
|
182
131
|
for (const binding of bindings) {
|
|
183
132
|
const exportName = binding.exportNames[0];
|
|
184
133
|
|
|
185
|
-
const handlerId = isBuild
|
|
186
|
-
? hashId(filePath, exportName)
|
|
187
|
-
: `${filePath}#${exportName}`;
|
|
134
|
+
const handlerId = makeStubId(filePath, exportName, isBuild);
|
|
188
135
|
|
|
189
136
|
// Injection strategy matches the runtime overload signatures:
|
|
190
137
|
// 0 args -> inject undefined, "id"
|