@evolonix/react-router-next 0.2.0

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/index.js ADDED
@@ -0,0 +1,482 @@
1
+ // src/runtime/app-router.tsx
2
+ import { createBrowserRouter, RouterProvider } from "react-router";
3
+ import { modules, appDir } from "virtual:react-router-next/app-tree";
4
+
5
+ // src/runtime/route-components.tsx
6
+ import { Outlet, useNavigation, useParams as useParams2 } from "react-router";
7
+
8
+ // src/runtime/use-route-params.ts
9
+ import { useParams } from "react-router";
10
+ function parseRouteParams(route, rrParams) {
11
+ const out = {};
12
+ for (const seg of route.split("/")) {
13
+ if (seg.startsWith("@") || seg.startsWith("(")) continue;
14
+ if (seg.startsWith("[[...") && seg.endsWith("]]")) {
15
+ const splat = rrParams["*"];
16
+ out[seg.slice(5, -2)] = splat === void 0 ? void 0 : splat.split("/").filter(Boolean);
17
+ } else if (seg.startsWith("[...") && seg.endsWith("]")) {
18
+ out[seg.slice(4, -1)] = (rrParams["*"] ?? "").split("/").filter(Boolean);
19
+ } else if (seg.startsWith("[[") && seg.endsWith("]]")) {
20
+ const name = seg.slice(2, -2);
21
+ out[name] = rrParams[name];
22
+ } else if (seg.startsWith("[") && seg.endsWith("]")) {
23
+ const name = seg.slice(1, -1);
24
+ out[name] = rrParams[name];
25
+ }
26
+ }
27
+ return out;
28
+ }
29
+ function useRouteParams(route) {
30
+ return parseRouteParams(route, useParams());
31
+ }
32
+
33
+ // src/runtime/route-components.tsx
34
+ import { jsx } from "react/jsx-runtime";
35
+ function LoadingBoundary({
36
+ Loading
37
+ }) {
38
+ const nav = useNavigation();
39
+ return nav.state === "loading" ? /* @__PURE__ */ jsx(Loading, {}) : /* @__PURE__ */ jsx(Outlet, {});
40
+ }
41
+ function ComponentWithParams({
42
+ Component,
43
+ route
44
+ }) {
45
+ const rrParams = useParams2();
46
+ const params = parseRouteParams(route, rrParams);
47
+ return /* @__PURE__ */ jsx(Component, { params });
48
+ }
49
+
50
+ // src/runtime/parallel-routes.tsx
51
+ import {
52
+ Outlet as Outlet2,
53
+ useLocation,
54
+ useNavigationType,
55
+ useParams as useParams3,
56
+ useRoutes
57
+ } from "react-router";
58
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
59
+ function SlotElement({ slot }) {
60
+ const matched = useRoutes(slot.routes);
61
+ return matched ?? slot.defaultElement;
62
+ }
63
+ function ParallelLayout({
64
+ Component,
65
+ slots,
66
+ route
67
+ }) {
68
+ const rrParams = useParams3();
69
+ const slotProps = { children: /* @__PURE__ */ jsx2(Outlet2, {}) };
70
+ for (const [name, slot] of Object.entries(slots)) {
71
+ slotProps[name] = /* @__PURE__ */ jsx2(SlotElement, { slot }, name);
72
+ }
73
+ const params = route.includes("[") ? parseRouteParams(route, rrParams) : void 0;
74
+ return /* @__PURE__ */ jsx2(Component, { ...slotProps, params });
75
+ }
76
+ function TemplateRemount({
77
+ Template
78
+ }) {
79
+ const { pathname } = useLocation();
80
+ return /* @__PURE__ */ jsx2(Template, { children: /* @__PURE__ */ jsx2(Outlet2, {}) }, pathname);
81
+ }
82
+ function InterceptedRoute({
83
+ Interceptor,
84
+ Target
85
+ }) {
86
+ const navType = useNavigationType();
87
+ return /* @__PURE__ */ jsx2(Fragment, { children: navType === "POP" ? Target : Interceptor });
88
+ }
89
+
90
+ // src/runtime/app-routes.tsx
91
+ import { jsx as jsx3 } from "react/jsx-runtime";
92
+ var FILE_KINDS = /* @__PURE__ */ new Set([
93
+ "page",
94
+ "layout",
95
+ "loader",
96
+ "loading",
97
+ "error",
98
+ "default",
99
+ "template",
100
+ "not-found"
101
+ ]);
102
+ function emptyNode() {
103
+ return { files: {}, children: /* @__PURE__ */ new Map(), slots: /* @__PURE__ */ new Map() };
104
+ }
105
+ function isPrivateSegment(seg) {
106
+ return seg.startsWith("_");
107
+ }
108
+ function isSlotSegment(seg) {
109
+ return seg.startsWith("@") && seg.length > 1;
110
+ }
111
+ function parseInterceptPrefix(seg) {
112
+ if (seg.startsWith("(...)")) return { depth: "root", rest: seg.slice(5) };
113
+ if (seg.startsWith("(..)(..)")) return { depth: 3, rest: seg.slice(8) };
114
+ if (seg.startsWith("(..)")) return { depth: 2, rest: seg.slice(4) };
115
+ if (seg.startsWith("(.)")) return { depth: 1, rest: seg.slice(3) };
116
+ return null;
117
+ }
118
+ function isRouteGroupSegment(seg) {
119
+ return seg.startsWith("(") && seg.endsWith(")") && parseInterceptPrefix(seg) === null;
120
+ }
121
+ function routeKeySegmentsOf(parts) {
122
+ return parts.filter((s) => !isSlotSegment(s) && !isPrivateSegment(s));
123
+ }
124
+ function resolveInterceptTargetKey(fsPrefix, intercept, postIntercept) {
125
+ let resolved;
126
+ if (intercept.depth === "root") {
127
+ resolved = [];
128
+ } else {
129
+ const popCount = intercept.depth - 1;
130
+ resolved = fsPrefix.slice(0, Math.max(0, fsPrefix.length - popCount));
131
+ }
132
+ const prefixSegs = routeKeySegmentsOf(resolved);
133
+ const tail = [];
134
+ if (intercept.rest) tail.push(intercept.rest);
135
+ tail.push(...postIntercept);
136
+ return [...prefixSegs, ...routeKeySegmentsOf(tail)].join("/");
137
+ }
138
+ function buildTree(modules2, appDir2) {
139
+ const root = emptyNode();
140
+ const intercepts = [];
141
+ const interceptNodes = /* @__PURE__ */ new Map();
142
+ const prefix = appDir2.endsWith("/") ? appDir2 : `${appDir2}/`;
143
+ for (const path in modules2) {
144
+ const fromAppDir = path.startsWith(prefix) ? path.slice(prefix.length) : path;
145
+ const rel = fromAppDir.replace(/\.[jt]sx?$/, "");
146
+ const parts = rel.split("/");
147
+ const last = parts.pop();
148
+ if (!last || !FILE_KINDS.has(last)) continue;
149
+ if (parts.some(isPrivateSegment)) continue;
150
+ let interceptIdx = -1;
151
+ let interceptParse = null;
152
+ for (let i = 0; i < parts.length; i++) {
153
+ const p = parseInterceptPrefix(parts[i]);
154
+ if (p) {
155
+ interceptIdx = i;
156
+ interceptParse = p;
157
+ break;
158
+ }
159
+ }
160
+ if (interceptParse === null) {
161
+ let node2 = root;
162
+ for (const seg of parts) {
163
+ if (isSlotSegment(seg)) {
164
+ const name = seg.slice(1);
165
+ let child = node2.slots.get(name);
166
+ if (!child) {
167
+ child = emptyNode();
168
+ node2.slots.set(name, child);
169
+ }
170
+ node2 = child;
171
+ } else {
172
+ let child = node2.children.get(seg);
173
+ if (!child) {
174
+ child = emptyNode();
175
+ node2.children.set(seg, child);
176
+ }
177
+ node2 = child;
178
+ }
179
+ }
180
+ node2.files[last] = modules2[path];
181
+ continue;
182
+ }
183
+ const fsPrefix = parts.slice(0, interceptIdx);
184
+ const interceptSeg = parts[interceptIdx];
185
+ const postIntercept = parts.slice(interceptIdx + 1);
186
+ const targetKey = resolveInterceptTargetKey(
187
+ fsPrefix,
188
+ interceptParse,
189
+ postIntercept
190
+ );
191
+ const folderKey = [...fsPrefix, interceptSeg].join("/");
192
+ let node = interceptNodes.get(folderKey);
193
+ if (!node) {
194
+ node = emptyNode();
195
+ interceptNodes.set(folderKey, node);
196
+ intercepts.push({ targetKey, node });
197
+ }
198
+ let cur = node;
199
+ for (const seg of postIntercept) {
200
+ if (isSlotSegment(seg)) {
201
+ const name = seg.slice(1);
202
+ let child = cur.slots.get(name);
203
+ if (!child) {
204
+ child = emptyNode();
205
+ cur.slots.set(name, child);
206
+ }
207
+ cur = child;
208
+ } else {
209
+ let child = cur.children.get(seg);
210
+ if (!child) {
211
+ child = emptyNode();
212
+ cur.children.set(seg, child);
213
+ }
214
+ cur = child;
215
+ }
216
+ }
217
+ cur.files[last] = modules2[path];
218
+ }
219
+ return { root, intercepts };
220
+ }
221
+ function segmentToSpecs(segment) {
222
+ if (isRouteGroupSegment(segment)) return [{}];
223
+ if (segment.startsWith("[[...") && segment.endsWith("]]"))
224
+ return [{ index: true }, { path: "*" }];
225
+ if (segment.startsWith("[[") && segment.endsWith("]]"))
226
+ return [{ path: `:${segment.slice(2, -2)}?` }];
227
+ if (segment.startsWith("[...") && segment.endsWith("]"))
228
+ return [{ path: "*" }];
229
+ if (segment.startsWith("[") && segment.endsWith("]"))
230
+ return [{ path: `:${segment.slice(1, -1)}` }];
231
+ return [{ path: segment }];
232
+ }
233
+ function routeHasParams(route) {
234
+ return route.includes("[");
235
+ }
236
+ function renderComponent(Component, route) {
237
+ return routeHasParams(route) ? /* @__PURE__ */ jsx3(ComponentWithParams, { Component, route }) : /* @__PURE__ */ jsx3(Component, {});
238
+ }
239
+ function lowerSlotToConfig(slotNode, slotPath) {
240
+ const Default = slotNode.files.default?.default;
241
+ const defaultElement = Default ? renderComponent(Default, slotPath) : null;
242
+ const stripped = {
243
+ files: { ...slotNode.files, default: void 0 },
244
+ children: slotNode.children,
245
+ slots: slotNode.slots
246
+ };
247
+ const routes = nodeToRoute(stripped, null, slotPath, void 0);
248
+ return { routes, defaultElement };
249
+ }
250
+ function lowerInterceptor(node, targetKey) {
251
+ if (node.files.loader) {
252
+ console.warn(
253
+ `[react-router-next] Loader on intercepting route "${targetKey}" is ignored. Interceptor loaders are not supported in V1.`
254
+ );
255
+ }
256
+ if (node.files.layout) {
257
+ console.warn(
258
+ `[react-router-next] Layout on intercepting route "${targetKey}" is ignored. Interceptor layouts are not supported in V1.`
259
+ );
260
+ }
261
+ const Page = node.files.page?.default;
262
+ if (!Page) {
263
+ throw new Error(
264
+ `[react-router-next] Intercepting route at "${targetKey}" is missing page.tsx.`
265
+ );
266
+ }
267
+ return renderComponent(Page, targetKey);
268
+ }
269
+ function nodeToRoute(node, segment, path, NotFound) {
270
+ const Layout = node.files.layout?.default;
271
+ const Page = node.files.page?.default;
272
+ const Loading = node.files.loading?.default;
273
+ const ErrorEl = node.files.error?.default;
274
+ const Template = node.files.template?.default;
275
+ const loader = node.files.loader?.loader ?? node.files.loader?.default;
276
+ const childRoutes = [];
277
+ for (const [childSegment, childNode] of node.children) {
278
+ const childPath = path === "" ? childSegment : `${path}/${childSegment}`;
279
+ childRoutes.push(
280
+ ...nodeToRoute(childNode, childSegment, childPath, NotFound)
281
+ );
282
+ }
283
+ const pageEl = Page ? renderComponent(Page, path) : null;
284
+ const pageLeaf = pageEl ? { index: true, element: pageEl, loader } : null;
285
+ const inner = [];
286
+ if (pageLeaf) inner.push(pageLeaf);
287
+ inner.push(...childRoutes);
288
+ if (NotFound && inner.some((c) => c.path === "*") && !inner.some((c) => c.index)) {
289
+ inner.unshift({ index: true, element: /* @__PURE__ */ jsx3(NotFound, {}) });
290
+ }
291
+ let slotConfigs = null;
292
+ if (node.slots.size > 0) {
293
+ if (!Layout) {
294
+ console.warn(
295
+ `[react-router-next] Parallel-route slots at "${path || "/"}" require a layout.tsx to render. Slots ignored.`
296
+ );
297
+ } else {
298
+ slotConfigs = {};
299
+ for (const [slotName, slotNode] of node.slots) {
300
+ if (slotNode.files.loader) {
301
+ console.warn(
302
+ `[react-router-next] Loader inside @${slotName} at "${path || "/"}" is ignored. Slot loaders are not supported in V1.`
303
+ );
304
+ }
305
+ slotConfigs[slotName] = lowerSlotToConfig(slotNode, path);
306
+ }
307
+ }
308
+ }
309
+ const specs = segment === null ? [{}] : segmentToSpecs(segment);
310
+ const buildLayoutElement = () => {
311
+ if (!Layout) return null;
312
+ if (slotConfigs) {
313
+ return /* @__PURE__ */ jsx3(
314
+ ParallelLayout,
315
+ {
316
+ Component: Layout,
317
+ slots: slotConfigs,
318
+ route: path
319
+ }
320
+ );
321
+ }
322
+ return renderComponent(Layout, path);
323
+ };
324
+ return specs.map((spec) => {
325
+ const route = { ...spec };
326
+ if (ErrorEl) route.errorElement = /* @__PURE__ */ jsx3(ErrorEl, {});
327
+ const layoutElement = buildLayoutElement();
328
+ if (layoutElement) {
329
+ route.element = layoutElement;
330
+ const wrappedInner = Template ? [
331
+ {
332
+ element: /* @__PURE__ */ jsx3(TemplateRemount, { Template }),
333
+ children: inner
334
+ }
335
+ ] : inner;
336
+ route.children = Loading ? [
337
+ {
338
+ element: /* @__PURE__ */ jsx3(LoadingBoundary, { Loading }),
339
+ children: wrappedInner
340
+ }
341
+ ] : wrappedInner;
342
+ } else if (Template) {
343
+ route.element = /* @__PURE__ */ jsx3(TemplateRemount, { Template });
344
+ route.children = Loading ? [{ element: /* @__PURE__ */ jsx3(LoadingBoundary, { Loading }), children: inner }] : inner;
345
+ } else if (Loading) {
346
+ route.element = /* @__PURE__ */ jsx3(LoadingBoundary, { Loading });
347
+ route.children = inner;
348
+ } else if (pageEl && childRoutes.length === 0) {
349
+ route.element = pageEl;
350
+ if (loader) route.loader = loader;
351
+ } else if (inner.length > 0) {
352
+ route.children = inner;
353
+ }
354
+ return route;
355
+ });
356
+ }
357
+ function routeKeyToRrSegments(routeKey) {
358
+ if (routeKey === "") return [];
359
+ const out = [];
360
+ for (const s of routeKey.split("/")) {
361
+ if (s === "") continue;
362
+ if (isRouteGroupSegment(s)) continue;
363
+ if (s.startsWith("[[...") && s.endsWith("]]")) {
364
+ out.push("*");
365
+ } else if (s.startsWith("[...") && s.endsWith("]")) {
366
+ out.push("*");
367
+ } else if (s.startsWith("[[") && s.endsWith("]]")) {
368
+ out.push(`:${s.slice(2, -2)}?`);
369
+ } else if (s.startsWith("[") && s.endsWith("]")) {
370
+ out.push(`:${s.slice(1, -1)}`);
371
+ } else {
372
+ out.push(s);
373
+ }
374
+ }
375
+ return out;
376
+ }
377
+ function findRouteByPath(routes, targetSegments) {
378
+ if (targetSegments.length === 0) {
379
+ for (const r of routes) {
380
+ if (r.index) return r;
381
+ }
382
+ return null;
383
+ }
384
+ const [head, ...rest] = targetSegments;
385
+ for (const r of routes) {
386
+ if (r.index) continue;
387
+ if (r.path === void 0) {
388
+ if (r.children) {
389
+ const found = findRouteByPath(r.children, targetSegments);
390
+ if (found) return found;
391
+ }
392
+ continue;
393
+ }
394
+ if (r.path === head) {
395
+ if (rest.length === 0) {
396
+ if (r.children) {
397
+ for (const c of r.children) {
398
+ if (c.index) return c;
399
+ }
400
+ }
401
+ return r;
402
+ }
403
+ if (r.children) {
404
+ const found = findRouteByPath(r.children, rest);
405
+ if (found) return found;
406
+ }
407
+ }
408
+ }
409
+ return null;
410
+ }
411
+ function applyIntercept(rootRoute, intercept) {
412
+ const segs = routeKeyToRrSegments(intercept.targetKey);
413
+ const target = findRouteByPath([rootRoute], segs);
414
+ if (!target) {
415
+ throw new Error(
416
+ `[react-router-next] Intercepting route targets "${intercept.targetKey}", but no matching route exists. Add a page.tsx at that path or remove the interceptor.`
417
+ );
418
+ }
419
+ if (target.element === void 0) {
420
+ throw new Error(
421
+ `[react-router-next] Intercepting route target "${intercept.targetKey}" has no element to wrap.`
422
+ );
423
+ }
424
+ const interceptorEl = lowerInterceptor(intercept.node, intercept.targetKey);
425
+ target.element = /* @__PURE__ */ jsx3(InterceptedRoute, { Interceptor: interceptorEl, Target: target.element });
426
+ }
427
+ function buildRoutesFromModules(modules2, appDir2) {
428
+ const tree = buildTree(modules2, appDir2);
429
+ const NotFound = tree.root.files["not-found"]?.default;
430
+ const [root] = nodeToRoute(tree.root, null, "", NotFound);
431
+ for (const intercept of tree.intercepts) {
432
+ applyIntercept(root, intercept);
433
+ }
434
+ if (NotFound) {
435
+ (root.children ??= []).push({ path: "*", element: /* @__PURE__ */ jsx3(NotFound, {}) });
436
+ }
437
+ return [root];
438
+ }
439
+
440
+ // src/runtime/app-router.tsx
441
+ import { jsx as jsx4 } from "react/jsx-runtime";
442
+ var router = createBrowserRouter(
443
+ buildRoutesFromModules(modules, appDir)
444
+ );
445
+ function AppRouter() {
446
+ return /* @__PURE__ */ jsx4(RouterProvider, { router });
447
+ }
448
+
449
+ // src/runtime/generate-url.ts
450
+ function generateUrl(route, params) {
451
+ const p = params;
452
+ const out = [];
453
+ for (const seg of route.split("/")) {
454
+ if (seg === "") continue;
455
+ if (seg.startsWith("@")) continue;
456
+ if (seg.startsWith("(") && seg.endsWith(")")) continue;
457
+ if (seg.startsWith("[[...") && seg.endsWith("]]")) {
458
+ const v = p[seg.slice(5, -2)];
459
+ if (Array.isArray(v) && v.length > 0) out.push(...v);
460
+ } else if (seg.startsWith("[...") && seg.endsWith("]")) {
461
+ const v = p[seg.slice(4, -1)];
462
+ if (Array.isArray(v)) out.push(...v);
463
+ } else if (seg.startsWith("[[") && seg.endsWith("]]")) {
464
+ const v = p[seg.slice(2, -2)];
465
+ if (typeof v === "string" && v.length > 0) out.push(v);
466
+ } else if (seg.startsWith("[") && seg.endsWith("]")) {
467
+ const v = p[seg.slice(1, -1)];
468
+ if (typeof v === "string") out.push(v);
469
+ } else {
470
+ out.push(seg);
471
+ }
472
+ }
473
+ return "/" + out.join("/");
474
+ }
475
+ export {
476
+ AppRouter,
477
+ buildRoutesFromModules,
478
+ generateUrl,
479
+ parseRouteParams,
480
+ useRouteParams
481
+ };
482
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/app-router.tsx","../src/runtime/route-components.tsx","../src/runtime/use-route-params.ts","../src/runtime/parallel-routes.tsx","../src/runtime/app-routes.tsx","../src/runtime/generate-url.ts"],"sourcesContent":["import type { JSX } from \"react\";\nimport { createBrowserRouter, RouterProvider } from \"react-router\";\n// @ts-expect-error virtual module is provided by the routeTypegen Vite plugin at build/dev time.\nimport { modules, appDir } from \"virtual:react-router-next/app-tree\";\nimport { buildRoutesFromModules, type RouteModuleMap } from \"./app-routes\";\n\nconst router = createBrowserRouter(\n buildRoutesFromModules(modules as RouteModuleMap, appDir as string),\n);\n\nexport default function AppRouter(): JSX.Element {\n return <RouterProvider router={router} />;\n}\n","import type { ComponentType, ReactElement } from \"react\";\nimport { Outlet, useNavigation, useParams } from \"react-router\";\nimport { parseRouteParams } from \"./use-route-params\";\n\ntype RouteParamsRecord = Record<string, string | string[] | undefined>;\n\nexport function LoadingBoundary({\n Loading,\n}: {\n Loading: ComponentType;\n}): ReactElement {\n const nav = useNavigation();\n return nav.state === \"loading\" ? <Loading /> : <Outlet />;\n}\n\nexport function ComponentWithParams({\n Component,\n route,\n}: {\n Component: ComponentType<{ params?: RouteParamsRecord }>;\n route: string;\n}): ReactElement {\n const rrParams = useParams();\n const params = parseRouteParams(route, rrParams);\n return <Component params={params} />;\n}\n","import { useParams } from \"react-router\";\n\ntype ParseSegment<S extends string> = S extends `[[...${infer Name}]]`\n ? { [K in Name]?: string[] }\n : S extends `[...${infer Name}]`\n ? { [K in Name]: string[] }\n : S extends `[[${infer Name}]]`\n ? { [K in Name]?: string }\n : S extends `[${infer Name}]`\n ? { [K in Name]: string }\n : S extends `(${string})`\n ? Record<never, never>\n : S extends `@${string}`\n ? Record<never, never>\n : Record<never, never>;\n\ntype ParseRoute<S extends string> = S extends `${infer Head}/${infer Tail}`\n ? ParseSegment<Head> & ParseRoute<Tail>\n : ParseSegment<S>;\n\nexport type RouteParams<S extends string> = string extends S\n ? Record<string, string | string[] | undefined>\n : { [K in keyof ParseRoute<S>]: ParseRoute<S>[K] };\n\nexport type RouteProps<S extends string> = {\n params: RouteParams<S>;\n};\n\nexport function parseRouteParams<S extends string>(\n route: S,\n rrParams: Readonly<Record<string, string | undefined>>,\n): RouteParams<S> {\n const out: Record<string, string | string[] | undefined> = {};\n for (const seg of route.split(\"/\")) {\n if (seg.startsWith(\"@\") || seg.startsWith(\"(\")) continue;\n if (seg.startsWith(\"[[...\") && seg.endsWith(\"]]\")) {\n const splat = rrParams[\"*\"];\n out[seg.slice(5, -2)] =\n splat === undefined ? undefined : splat.split(\"/\").filter(Boolean);\n } else if (seg.startsWith(\"[...\") && seg.endsWith(\"]\")) {\n out[seg.slice(4, -1)] = (rrParams[\"*\"] ?? \"\").split(\"/\").filter(Boolean);\n } else if (seg.startsWith(\"[[\") && seg.endsWith(\"]]\")) {\n const name = seg.slice(2, -2);\n out[name] = rrParams[name];\n } else if (seg.startsWith(\"[\") && seg.endsWith(\"]\")) {\n const name = seg.slice(1, -1);\n out[name] = rrParams[name]!;\n }\n }\n return out as RouteParams<S>;\n}\n\nexport function useRouteParams<S extends string>(route: S): RouteParams<S> {\n return parseRouteParams(route, useParams());\n}\n","import type { ComponentType, ReactElement, ReactNode } from \"react\";\nimport {\n Outlet,\n useLocation,\n useNavigationType,\n useParams,\n useRoutes,\n type RouteObject,\n} from \"react-router\";\n\nimport { parseRouteParams } from \"./use-route-params\";\n\nexport type SlotConfig = {\n routes: RouteObject[];\n defaultElement: ReactNode | null;\n};\n\ntype LayoutWithSlots = ComponentType<{\n params?: Record<string, string | string[] | undefined>;\n children?: ReactNode;\n [slot: string]: unknown;\n}>;\n\nfunction SlotElement({ slot }: { slot: SlotConfig }): ReactNode {\n const matched = useRoutes(slot.routes);\n return matched ?? slot.defaultElement;\n}\n\n/**\n * Wraps a layout that owns one or more `@slot` parallel routes. Each slot is\n * rendered into a `SlotElement` whose content is driven by `useRoutes(slot.routes)`\n * — i.e. the slot's own subtree is matched against the current URL independently\n * of the main outlet. The matched element (or the slot's `default.tsx`) is\n * passed to the user's layout as a named prop.\n */\nexport function ParallelLayout({\n Component,\n slots,\n route,\n}: {\n Component: LayoutWithSlots;\n slots: Record<string, SlotConfig>;\n route: string;\n}): ReactElement {\n const rrParams = useParams();\n const slotProps: Record<string, ReactNode> = { children: <Outlet /> };\n for (const [name, slot] of Object.entries(slots)) {\n slotProps[name] = <SlotElement key={name} slot={slot} />;\n }\n const params = route.includes(\"[\")\n ? parseRouteParams(route, rrParams)\n : undefined;\n return <Component {...slotProps} params={params} />;\n}\n\n/**\n * Re-mounts its template on every URL change by keying it on `location.pathname`.\n * Mirrors Next.js `template.tsx` semantics: like a layout, but state is not\n * preserved across navigations.\n */\nexport function TemplateRemount({\n Template,\n}: {\n Template: ComponentType<{ children?: ReactNode }>;\n}): ReactElement {\n const { pathname } = useLocation();\n return (\n <Template key={pathname}>\n <Outlet />\n </Template>\n );\n}\n\n/**\n * Renders the interceptor element on PUSH/REPLACE (soft) navigation, and the\n * original target element on POP (back/forward) and initial loads. Mirrors\n * Next.js intercepting-route semantics: a deep link or refresh shows the full\n * page; an in-app click shows the interceptor (e.g. modal).\n */\nexport function InterceptedRoute({\n Interceptor,\n Target,\n}: {\n Interceptor: ReactNode;\n Target: ReactNode;\n}): ReactElement {\n const navType = useNavigationType();\n return <>{navType === \"POP\" ? Target : Interceptor}</>;\n}\n","import type { ComponentType, ReactElement, ReactNode } from \"react\";\nimport type { LoaderFunction, RouteObject } from \"react-router\";\nimport { ComponentWithParams, LoadingBoundary } from \"./route-components\";\nimport {\n InterceptedRoute,\n ParallelLayout,\n TemplateRemount,\n type SlotConfig,\n} from \"./parallel-routes\";\n\ntype LayoutWithSlots = ComponentType<{\n params?: RouteParamsRecord;\n children?: ReactNode;\n [slot: string]: unknown;\n}>;\n\ntype RouteParamsRecord = Record<string, string | string[] | undefined>;\n\nexport type RouteModule = {\n default?: ComponentType<{\n params?: RouteParamsRecord;\n children?: ReactNode;\n [slot: string]: unknown;\n }>;\n loader?: LoaderFunction;\n};\n\nexport type RouteModuleMap = Record<string, RouteModule>;\n\ntype FileKind =\n | \"page\"\n | \"layout\"\n | \"loader\"\n | \"loading\"\n | \"error\"\n | \"default\"\n | \"template\"\n | \"not-found\";\n\ntype Node = {\n files: Partial<Record<FileKind, RouteModule>>;\n children: Map<string, Node>;\n slots: Map<string, Node>;\n};\n\ntype Intercept = {\n /** Resolved target URL pattern in routeKey form (e.g. \"photos/[id]\"). */\n targetKey: string;\n node: Node;\n};\n\ntype Tree = {\n root: Node;\n intercepts: Intercept[];\n};\n\nconst FILE_KINDS = new Set<FileKind>([\n \"page\",\n \"layout\",\n \"loader\",\n \"loading\",\n \"error\",\n \"default\",\n \"template\",\n \"not-found\",\n]);\n\nfunction emptyNode(): Node {\n return { files: {}, children: new Map(), slots: new Map() };\n}\n\nfunction isPrivateSegment(seg: string): boolean {\n return seg.startsWith(\"_\");\n}\n\nfunction isSlotSegment(seg: string): boolean {\n return seg.startsWith(\"@\") && seg.length > 1;\n}\n\ntype InterceptDepth = 1 | 2 | 3 | \"root\";\n\nfunction parseInterceptPrefix(\n seg: string,\n): { depth: InterceptDepth; rest: string } | null {\n if (seg.startsWith(\"(...)\")) return { depth: \"root\", rest: seg.slice(5) };\n if (seg.startsWith(\"(..)(..)\")) return { depth: 3, rest: seg.slice(8) };\n if (seg.startsWith(\"(..)\")) return { depth: 2, rest: seg.slice(4) };\n if (seg.startsWith(\"(.)\")) return { depth: 1, rest: seg.slice(3) };\n return null;\n}\n\nfunction isRouteGroupSegment(seg: string): boolean {\n return (\n seg.startsWith(\"(\") &&\n seg.endsWith(\")\") &&\n parseInterceptPrefix(seg) === null\n );\n}\n\nfunction routeKeySegmentsOf(parts: readonly string[]): string[] {\n return parts.filter((s) => !isSlotSegment(s) && !isPrivateSegment(s));\n}\n\nfunction resolveInterceptTargetKey(\n fsPrefix: readonly string[],\n intercept: { depth: InterceptDepth; rest: string },\n postIntercept: readonly string[],\n): string {\n let resolved: readonly string[];\n if (intercept.depth === \"root\") {\n resolved = [];\n } else {\n const popCount = intercept.depth - 1;\n resolved = fsPrefix.slice(0, Math.max(0, fsPrefix.length - popCount));\n }\n const prefixSegs = routeKeySegmentsOf(resolved);\n const tail: string[] = [];\n if (intercept.rest) tail.push(intercept.rest);\n tail.push(...postIntercept);\n return [...prefixSegs, ...routeKeySegmentsOf(tail)].join(\"/\");\n}\n\nfunction buildTree(modules: RouteModuleMap, appDir: string): Tree {\n const root = emptyNode();\n const intercepts: Intercept[] = [];\n /** Re-use the same Node across multiple files in the same interceptor folder. */\n const interceptNodes = new Map<string, Node>();\n const prefix = appDir.endsWith(\"/\") ? appDir : `${appDir}/`;\n\n for (const path in modules) {\n const fromAppDir = path.startsWith(prefix)\n ? path.slice(prefix.length)\n : path;\n const rel = fromAppDir.replace(/\\.[jt]sx?$/, \"\");\n const parts = rel.split(\"/\");\n const last = parts.pop();\n if (!last || !FILE_KINDS.has(last as FileKind)) continue;\n if (parts.some(isPrivateSegment)) continue;\n\n let interceptIdx = -1;\n let interceptParse: { depth: InterceptDepth; rest: string } | null = null;\n for (let i = 0; i < parts.length; i++) {\n const p = parseInterceptPrefix(parts[i]);\n if (p) {\n interceptIdx = i;\n interceptParse = p;\n break;\n }\n }\n\n if (interceptParse === null) {\n let node = root;\n for (const seg of parts) {\n if (isSlotSegment(seg)) {\n const name = seg.slice(1);\n let child = node.slots.get(name);\n if (!child) {\n child = emptyNode();\n node.slots.set(name, child);\n }\n node = child;\n } else {\n let child = node.children.get(seg);\n if (!child) {\n child = emptyNode();\n node.children.set(seg, child);\n }\n node = child;\n }\n }\n node.files[last as FileKind] = modules[path];\n continue;\n }\n\n const fsPrefix = parts.slice(0, interceptIdx);\n const interceptSeg = parts[interceptIdx];\n const postIntercept = parts.slice(interceptIdx + 1);\n const targetKey = resolveInterceptTargetKey(\n fsPrefix,\n interceptParse,\n postIntercept,\n );\n const folderKey = [...fsPrefix, interceptSeg].join(\"/\");\n let node = interceptNodes.get(folderKey);\n if (!node) {\n node = emptyNode();\n interceptNodes.set(folderKey, node);\n intercepts.push({ targetKey, node });\n }\n let cur = node;\n for (const seg of postIntercept) {\n if (isSlotSegment(seg)) {\n const name = seg.slice(1);\n let child = cur.slots.get(name);\n if (!child) {\n child = emptyNode();\n cur.slots.set(name, child);\n }\n cur = child;\n } else {\n let child = cur.children.get(seg);\n if (!child) {\n child = emptyNode();\n cur.children.set(seg, child);\n }\n cur = child;\n }\n }\n cur.files[last as FileKind] = modules[path];\n }\n\n return { root, intercepts };\n}\n\ntype RouteSpec = { path?: string; index?: true };\n\nfunction segmentToSpecs(segment: string): RouteSpec[] {\n if (isRouteGroupSegment(segment)) return [{}];\n if (segment.startsWith(\"[[...\") && segment.endsWith(\"]]\"))\n return [{ index: true }, { path: \"*\" }];\n if (segment.startsWith(\"[[\") && segment.endsWith(\"]]\"))\n return [{ path: `:${segment.slice(2, -2)}?` }];\n if (segment.startsWith(\"[...\") && segment.endsWith(\"]\"))\n return [{ path: \"*\" }];\n if (segment.startsWith(\"[\") && segment.endsWith(\"]\"))\n return [{ path: `:${segment.slice(1, -1)}` }];\n return [{ path: segment }];\n}\n\nfunction routeHasParams(route: string): boolean {\n return route.includes(\"[\");\n}\n\nfunction renderComponent(\n Component: ComponentType<{ params?: RouteParamsRecord }>,\n route: string,\n): ReactElement {\n return routeHasParams(route) ? (\n <ComponentWithParams Component={Component} route={route} />\n ) : (\n <Component />\n );\n}\n\nfunction lowerSlotToConfig(slotNode: Node, slotPath: string): SlotConfig {\n const Default = slotNode.files.default?.default;\n const defaultElement: ReactNode | null = Default\n ? renderComponent(Default, slotPath)\n : null;\n // Strip the default file before lowering — it's a sibling fallback, not a page.\n const stripped: Node = {\n files: { ...slotNode.files, default: undefined },\n children: slotNode.children,\n slots: slotNode.slots,\n };\n const routes = nodeToRoute(stripped, null, slotPath, undefined);\n return { routes, defaultElement };\n}\n\nfunction lowerInterceptor(node: Node, targetKey: string): ReactNode {\n if (node.files.loader) {\n console.warn(\n `[react-router-next] Loader on intercepting route \"${targetKey}\" is ignored. ` +\n `Interceptor loaders are not supported in V1.`,\n );\n }\n if (node.files.layout) {\n console.warn(\n `[react-router-next] Layout on intercepting route \"${targetKey}\" is ignored. ` +\n `Interceptor layouts are not supported in V1.`,\n );\n }\n const Page = node.files.page?.default;\n if (!Page) {\n throw new Error(\n `[react-router-next] Intercepting route at \"${targetKey}\" is missing page.tsx.`,\n );\n }\n return renderComponent(Page, targetKey);\n}\n\nfunction nodeToRoute(\n node: Node,\n segment: string | null,\n path: string,\n NotFound: ComponentType | undefined,\n): RouteObject[] {\n const Layout = node.files.layout?.default;\n const Page = node.files.page?.default;\n const Loading = node.files.loading?.default;\n const ErrorEl = node.files.error?.default;\n const Template = node.files.template?.default;\n const loader =\n node.files.loader?.loader ??\n (node.files.loader?.default as LoaderFunction | undefined);\n\n const childRoutes: RouteObject[] = [];\n for (const [childSegment, childNode] of node.children) {\n const childPath = path === \"\" ? childSegment : `${path}/${childSegment}`;\n childRoutes.push(\n ...nodeToRoute(childNode, childSegment, childPath, NotFound),\n );\n }\n\n const pageEl = Page ? renderComponent(Page, path) : null;\n\n const pageLeaf: RouteObject | null = pageEl\n ? { index: true, element: pageEl, loader }\n : null;\n\n const inner: RouteObject[] = [];\n if (pageLeaf) inner.push(pageLeaf);\n inner.push(...childRoutes);\n\n if (\n NotFound &&\n inner.some((c) => c.path === \"*\") &&\n !inner.some((c) => c.index)\n ) {\n inner.unshift({ index: true, element: <NotFound /> });\n }\n\n // Lower @slot subtrees once per layout that owns them.\n let slotConfigs: Record<string, SlotConfig> | null = null;\n if (node.slots.size > 0) {\n if (!Layout) {\n console.warn(\n `[react-router-next] Parallel-route slots at \"${path || \"/\"}\" require a layout.tsx to render. Slots ignored.`,\n );\n } else {\n slotConfigs = {};\n for (const [slotName, slotNode] of node.slots) {\n if (slotNode.files.loader) {\n console.warn(\n `[react-router-next] Loader inside @${slotName} at \"${path || \"/\"}\" is ignored. ` +\n `Slot loaders are not supported in V1.`,\n );\n }\n slotConfigs[slotName] = lowerSlotToConfig(slotNode, path);\n }\n }\n }\n\n const specs: RouteSpec[] = segment === null ? [{}] : segmentToSpecs(segment);\n\n const buildLayoutElement = (): ReactElement | null => {\n if (!Layout) return null;\n if (slotConfigs) {\n return (\n <ParallelLayout\n Component={Layout as LayoutWithSlots}\n slots={slotConfigs}\n route={path}\n />\n );\n }\n return renderComponent(Layout, path);\n };\n\n return specs.map((spec) => {\n const route: RouteObject = { ...spec };\n if (ErrorEl) route.errorElement = <ErrorEl />;\n\n const layoutElement = buildLayoutElement();\n\n if (layoutElement) {\n route.element = layoutElement;\n const wrappedInner: RouteObject[] = Template\n ? [\n {\n element: <TemplateRemount Template={Template} />,\n children: inner,\n },\n ]\n : inner;\n route.children = Loading\n ? [\n {\n element: <LoadingBoundary Loading={Loading} />,\n children: wrappedInner,\n },\n ]\n : wrappedInner;\n } else if (Template) {\n route.element = <TemplateRemount Template={Template} />;\n route.children = Loading\n ? [{ element: <LoadingBoundary Loading={Loading} />, children: inner }]\n : inner;\n } else if (Loading) {\n route.element = <LoadingBoundary Loading={Loading} />;\n route.children = inner;\n } else if (pageEl && childRoutes.length === 0) {\n route.element = pageEl;\n if (loader) route.loader = loader;\n } else if (inner.length > 0) {\n route.children = inner;\n }\n return route;\n });\n}\n\n/** Convert a routeKey like \"photos/[id]\" to RR-style URL pattern segments. */\nfunction routeKeyToRrSegments(routeKey: string): string[] {\n if (routeKey === \"\") return [];\n const out: string[] = [];\n for (const s of routeKey.split(\"/\")) {\n if (s === \"\") continue;\n if (isRouteGroupSegment(s)) continue;\n if (s.startsWith(\"[[...\") && s.endsWith(\"]]\")) {\n out.push(\"*\");\n } else if (s.startsWith(\"[...\") && s.endsWith(\"]\")) {\n out.push(\"*\");\n } else if (s.startsWith(\"[[\") && s.endsWith(\"]]\")) {\n out.push(`:${s.slice(2, -2)}?`);\n } else if (s.startsWith(\"[\") && s.endsWith(\"]\")) {\n out.push(`:${s.slice(1, -1)}`);\n } else {\n out.push(s);\n }\n }\n return out;\n}\n\n/**\n * Walk a lowered route tree to find the route whose URL pattern matches\n * `targetSegments`. Descends transparently through pathless route-group\n * routes (where `path === undefined`).\n */\nfunction findRouteByPath(\n routes: readonly RouteObject[],\n targetSegments: readonly string[],\n): RouteObject | null {\n if (targetSegments.length === 0) {\n for (const r of routes) {\n if (r.index) return r;\n }\n return null;\n }\n const [head, ...rest] = targetSegments;\n for (const r of routes) {\n if (r.index) continue;\n if (r.path === undefined) {\n if (r.children) {\n const found = findRouteByPath(r.children, targetSegments);\n if (found) return found;\n }\n continue;\n }\n if (r.path === head) {\n if (rest.length === 0) {\n if (r.children) {\n for (const c of r.children) {\n if (c.index) return c;\n }\n }\n return r;\n }\n if (r.children) {\n const found = findRouteByPath(r.children, rest);\n if (found) return found;\n }\n }\n }\n return null;\n}\n\nfunction applyIntercept(rootRoute: RouteObject, intercept: Intercept): void {\n const segs = routeKeyToRrSegments(intercept.targetKey);\n const target = findRouteByPath([rootRoute], segs);\n if (!target) {\n throw new Error(\n `[react-router-next] Intercepting route targets \"${intercept.targetKey}\", ` +\n `but no matching route exists. Add a page.tsx at that path or remove the interceptor.`,\n );\n }\n if (target.element === undefined) {\n throw new Error(\n `[react-router-next] Intercepting route target \"${intercept.targetKey}\" has no element to wrap.`,\n );\n }\n const interceptorEl = lowerInterceptor(intercept.node, intercept.targetKey);\n target.element = (\n <InterceptedRoute Interceptor={interceptorEl} Target={target.element} />\n );\n}\n\nexport function buildRoutesFromModules(\n modules: RouteModuleMap,\n appDir: string,\n): RouteObject[] {\n const tree = buildTree(modules, appDir);\n const NotFound = tree.root.files[\"not-found\"]?.default;\n const [root] = nodeToRoute(tree.root, null, \"\", NotFound);\n for (const intercept of tree.intercepts) {\n applyIntercept(root, intercept);\n }\n if (NotFound) {\n (root.children ??= []).push({ path: \"*\", element: <NotFound /> });\n }\n return [root];\n}\n","import type { RouteParams } from \"./use-route-params\";\n\nexport function generateUrl<S extends string>(\n route: S,\n params: RouteParams<S>,\n): string {\n const p = params as Record<string, string | string[] | undefined>;\n const out: string[] = [];\n for (const seg of route.split(\"/\")) {\n if (seg === \"\") continue;\n if (seg.startsWith(\"@\")) continue;\n if (seg.startsWith(\"(\") && seg.endsWith(\")\")) continue;\n if (seg.startsWith(\"[[...\") && seg.endsWith(\"]]\")) {\n const v = p[seg.slice(5, -2)];\n if (Array.isArray(v) && v.length > 0) out.push(...v);\n } else if (seg.startsWith(\"[...\") && seg.endsWith(\"]\")) {\n const v = p[seg.slice(4, -1)];\n if (Array.isArray(v)) out.push(...v);\n } else if (seg.startsWith(\"[[\") && seg.endsWith(\"]]\")) {\n const v = p[seg.slice(2, -2)];\n if (typeof v === \"string\" && v.length > 0) out.push(v);\n } else if (seg.startsWith(\"[\") && seg.endsWith(\"]\")) {\n const v = p[seg.slice(1, -1)];\n if (typeof v === \"string\") out.push(v);\n } else {\n out.push(seg);\n }\n }\n return \"/\" + out.join(\"/\");\n}\n"],"mappings":"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","names":["useParams","useParams","Outlet","useParams","jsx","useParams","Outlet","jsx","modules","appDir","node","jsx"]}
package/dist/vite.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ type RouteTypegenOptions = {
4
+ /** Source-of-truth directory containing `page.tsx`/`layout.tsx`. Defaults to `src/app`. */
5
+ appDir?: string;
6
+ /** Where the ambient `routes.d.ts` shim is written. Defaults to `<root>/node_modules/.react-router-next`. */
7
+ outDir?: string;
8
+ };
9
+ declare function routeTypegen(options?: RouteTypegenOptions): Plugin;
10
+
11
+ type GenerateOptions = {
12
+ /** Project root used to resolve relative paths. Defaults to `process.cwd()`. */
13
+ root?: string;
14
+ /** Source-of-truth directory containing `page.tsx`/`layout.tsx`. Defaults to `src/app`. */
15
+ appDir?: string;
16
+ /** Where the ambient `routes.d.ts` shim is written. Defaults to `<root>/node_modules/.react-router-next`. */
17
+ outDir?: string;
18
+ };
19
+ type GenerateResult = {
20
+ appDir: string;
21
+ outDir: string;
22
+ routeKeys: string[];
23
+ shimPath: string;
24
+ written: boolean;
25
+ };
26
+ declare function generateRouteTypes(opts?: GenerateOptions): GenerateResult;
27
+
28
+ export { type GenerateOptions, type GenerateResult, type RouteTypegenOptions, generateRouteTypes, routeTypegen };