Skip to content

Commit 93ddf39

Browse files
committed
improved typegen
1 parent 369b100 commit 93ddf39

File tree

10 files changed

+676
-678
lines changed

10 files changed

+676
-678
lines changed

packages/react-router-dev/typegen/generate.ts

Lines changed: 216 additions & 177 deletions
Large diffs are not rendered by default.

packages/react-router-dev/typegen/index.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {
99
type VirtualFile,
1010
typesDirectory,
1111
generateFuture,
12-
generateRouteModuleAnnotations,
13-
generatePages,
1412
generateRoutes,
1513
generateServerBuild,
1614
} from "./generate";
@@ -36,10 +34,8 @@ export async function run(rootDirectory: string, { mode }: { mode: string }) {
3634
await fs.rm(typesDirectory(ctx), { recursive: true, force: true });
3735
await write(
3836
generateFuture(ctx),
39-
generatePages(ctx),
40-
generateRoutes(ctx),
4137
generateServerBuild(ctx),
42-
...generateRouteModuleAnnotations(ctx)
38+
...generateRoutes(ctx)
4339
);
4440
}
4541

@@ -55,10 +51,8 @@ export async function watch(
5551
await fs.rm(typesDirectory(ctx), { recursive: true, force: true });
5652
await write(
5753
generateFuture(ctx),
58-
generatePages(ctx),
59-
generateRoutes(ctx),
6054
generateServerBuild(ctx),
61-
...generateRouteModuleAnnotations(ctx)
55+
...generateRoutes(ctx)
6256
);
6357
logger?.info(green("generated types"), { timestamp: true, clear: true });
6458

@@ -80,11 +74,7 @@ export async function watch(
8074

8175
if (routeConfigChanged) {
8276
await clearRouteModuleAnnotations(ctx);
83-
await write(
84-
generatePages(ctx),
85-
generateRoutes(ctx),
86-
...generateRouteModuleAnnotations(ctx)
87-
);
77+
await write(...generateRoutes(ctx));
8878
logger?.info(green("regenerated types"), {
8979
timestamp: true,
9080
clear: true,

packages/react-router-dev/typegen/route.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@ export function lineage(
1515
}
1616

1717
export function fullpath(lineage: RouteManifestEntry[]) {
18-
if (lineage.length === 1 && lineage[0].id === "root") return "/";
18+
const route = lineage.at(-1);
19+
20+
// root
21+
if (lineage.length === 1 && route?.id === "root") return "/";
22+
23+
// layout
24+
const isLayout = route && route.index !== true && route.path === undefined;
25+
if (isLayout) return undefined;
26+
1927
return (
2028
"/" +
2129
lineage

packages/react-router/lib/href.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,16 @@
1-
import type { Register } from "./types/register";
1+
import type { Pages } from "./types/register";
22
import type { Equal } from "./types/utils";
33

4-
type AnyParams = Record<string, string | undefined>;
5-
type AnyPages = Record<
6-
string,
7-
{
8-
params: AnyParams;
9-
}
10-
>;
11-
type Pages = Register extends {
12-
pages: infer RegisteredPages extends AnyPages;
13-
}
14-
? RegisteredPages
15-
: AnyPages;
16-
174
type Args = { [K in keyof Pages]: ToArgs<Pages[K]["params"]> };
185

196
// prettier-ignore
20-
type ToArgs<T extends AnyParams> =
7+
type ToArgs<Params extends Record<string, string | undefined>> =
218
// path without params -> no `params` arg
22-
Equal<T, {}> extends true ? [] :
9+
Equal<Params, {}> extends true ? [] :
2310
// path with only optional params -> optional `params` arg
24-
Partial<T> extends T ? [T] | [] :
11+
Partial<Params> extends Params ? [Params] | [] :
2512
// otherwise, require `params` arg
26-
[T];
13+
[Params];
2714

2815
/**
2916
Returns a resolved URL path for the specified route.
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
export type { Params } from "./params";
2-
export type {
3-
RouteModuleAnnotations,
4-
CreateLoaderData,
5-
CreateActionData,
6-
} from "./route-module";
1+
export type { GetAnnotations } from "./route-module-annotations";
2+
3+
import type { Params } from "./params";
4+
import type { RouteFiles } from "./register";
5+
import type { GetLoaderData, GetActionData } from "./route-data";
6+
import type { RouteModule } from "./route-module.ts";
7+
8+
export type GetInfo<T extends { file: keyof RouteFiles; module: RouteModule }> =
9+
{
10+
params: Params<T["file"]>;
11+
loaderData: GetLoaderData<T["module"]>;
12+
actionData: GetActionData<T["module"]>;
13+
};
Lines changed: 3 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,6 @@
1-
import type { Register } from "./register";
1+
import type { Pages, RouteFiles } from "./register";
22
import type { Normalize } from "./utils";
33

4-
type AnyRoutes = Record<
5-
string,
6-
{
7-
parentId?: string;
8-
path?: string;
9-
index?: boolean;
10-
file: string;
11-
params: Record<string, string | undefined>;
12-
}
4+
export type Params<RouteFile extends keyof RouteFiles> = Normalize<
5+
Pages[RouteFiles[RouteFile]["page"]]["params"]
136
>;
14-
type RoutesPre = Register extends {
15-
routesPre: infer RegisteredRoutes extends AnyRoutes;
16-
}
17-
? RegisteredRoutes
18-
: AnyRoutes;
19-
20-
type RouteId = keyof RoutesPre;
21-
22-
// prettier-ignore
23-
type GetParents<Id extends RouteId> =
24-
RoutesPre[Id] extends { parentId: infer P extends RouteId } ?
25-
[...GetParents<P>, P] :
26-
[];
27-
28-
// prettier-ignore
29-
type _GetChildren<Id extends RouteId> = {
30-
[K in RouteId]: RoutesPre[K] extends { parentId : Id } ?
31-
RoutesPre[K] extends { index: true } ? [K] :
32-
RoutesPre[K] extends { path: undefined } ? [K, ...GetChildren<K>] :
33-
[K] | [K, ...GetChildren<K>]
34-
:
35-
[]
36-
}[RouteId]
37-
38-
type GetChildren<Id extends RouteId> = _GetChildren<Id> extends []
39-
? []
40-
: Exclude<_GetChildren<Id>, []>;
41-
42-
type GetBranch<Id extends RouteId> = [
43-
...GetParents<Id>,
44-
Id,
45-
...(RoutesPre[Id] extends { path: undefined }
46-
? GetChildren<Id>
47-
: _GetChildren<Id>)
48-
];
49-
50-
type Branches = {
51-
[Id in RouteId]: GetBranch<Id>;
52-
};
53-
54-
type PartialParams = {
55-
[Id in RouteId]: RoutesPre[Id]["params"];
56-
};
57-
type BranchParams<Branch extends Array<RouteId>> = Branch extends [
58-
infer Id extends RouteId,
59-
...infer Ids extends Array<RouteId>
60-
]
61-
? PartialParams[Id] & BranchParams<Ids>
62-
: {};
63-
export type Params = {
64-
[Id in RouteId]: Normalize<BranchParams<Branches[Id]>>;
65-
};

packages/react-router/lib/types/register.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,23 @@
55
* For more on declaration merging and module augmentation, see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation .
66
*/
77
export interface Register {
8-
// params
8+
// pages
9+
// routeFiles
910
}
11+
12+
// pages
13+
type AnyParams = Record<string, string | undefined>;
14+
type AnyPages = Record<string, { params: AnyParams }>;
15+
export type Pages = Register extends {
16+
pages: infer Registered extends AnyPages;
17+
}
18+
? Registered
19+
: AnyPages;
20+
21+
// route files
22+
type AnyRouteFiles = Record<string, { id: string; page: string }>;
23+
export type RouteFiles = Register extends {
24+
routeFiles: infer Registered extends AnyRouteFiles;
25+
}
26+
? Registered
27+
: AnyRouteFiles;

packages/react-router/lib/types/route-data.ts

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
} from "../dom/ssr/routeModules";
55
import type { DataWithResponseInit } from "../router/utils";
66
import type { Serializable } from "../server-runtime/single-fetch";
7+
import type { RouteModule } from "./route-module";
78
import type { unstable_SerializesTo } from "./serializes-to";
89
import type { Equal, Expect, Func, IsAny, Pretty } from "./utils";
910

@@ -67,7 +68,50 @@ export type SerializeFrom<T> = T extends (...args: infer Args) => unknown
6768
: ServerDataFrom<T>
6869
: T;
6970

70-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
71+
type IsDefined<T> = Equal<T, undefined> extends true ? false : true;
72+
73+
// prettier-ignore
74+
type IsHydrate<ClientLoader> =
75+
ClientLoader extends { hydrate: true } ? true :
76+
ClientLoader extends { hydrate: false } ? false :
77+
false
78+
79+
export type GetLoaderData<T extends RouteModule> = _DataLoaderData<
80+
ServerDataFrom<T["loader"]>,
81+
ClientDataFrom<T["clientLoader"]>,
82+
IsHydrate<T["clientLoader"]>,
83+
T extends { HydrateFallback: Func } ? true : false
84+
>;
85+
86+
// prettier-ignore
87+
type _DataLoaderData<
88+
ServerLoaderData,
89+
ClientLoaderData,
90+
ClientLoaderHydrate extends boolean,
91+
HasHydrateFallback
92+
> =
93+
[HasHydrateFallback, ClientLoaderHydrate] extends [true, true] ?
94+
IsDefined<ClientLoaderData> extends true ? ClientLoaderData :
95+
undefined
96+
:
97+
[IsDefined<ClientLoaderData>, IsDefined<ServerLoaderData>] extends [true, true] ? ServerLoaderData | ClientLoaderData :
98+
IsDefined<ClientLoaderData> extends true ? ClientLoaderData :
99+
IsDefined<ServerLoaderData> extends true ? ServerLoaderData :
100+
undefined
101+
102+
export type GetActionData<T extends RouteModule> = _DataActionData<
103+
ServerDataFrom<T["action"]>,
104+
ClientDataFrom<T["clientAction"]>
105+
>;
106+
107+
// prettier-ignore
108+
type _DataActionData<ServerActionData, ClientActionData> = Awaited<
109+
[IsDefined<ServerActionData>, IsDefined<ClientActionData>] extends [true, true] ? ServerActionData | ClientActionData :
110+
IsDefined<ClientActionData> extends true ? ClientActionData :
111+
IsDefined<ServerActionData> extends true ? ServerActionData :
112+
undefined
113+
>
114+
71115
type __tests = [
72116
// ServerDataFrom
73117
Expect<Equal<ServerDataFrom<any>, undefined>>,
@@ -130,5 +174,98 @@ type __tests = [
130174
| { data: string; b: Date; c: () => boolean }
131175
>
132176
>,
133-
Expect<Equal<ClientDataFrom<() => { a: string } | Response>, { a: string }>>
177+
Expect<Equal<ClientDataFrom<() => { a: string } | Response>, { a: string }>>,
178+
179+
// GetLoaderData
180+
Expect<Equal<GetLoaderData<{}>, undefined>>,
181+
Expect<
182+
Equal<
183+
GetLoaderData<{
184+
loader: () => { a: string; b: Date; c: () => boolean };
185+
}>,
186+
{ a: string; b: Date; c: undefined }
187+
>
188+
>,
189+
Expect<
190+
Equal<
191+
GetLoaderData<{
192+
clientLoader: () => { a: string; b: Date; c: () => boolean };
193+
}>,
194+
{ a: string; b: Date; c: () => boolean }
195+
>
196+
>,
197+
Expect<
198+
Equal<
199+
GetLoaderData<{
200+
loader: () => { a: string; b: Date; c: () => boolean };
201+
clientLoader: () => { d: string; e: Date; f: () => boolean };
202+
}>,
203+
| { a: string; b: Date; c: undefined }
204+
| { d: string; e: Date; f: () => boolean }
205+
>
206+
>,
207+
Expect<
208+
Equal<
209+
GetLoaderData<{
210+
loader: () => { a: string; b: Date; c: () => boolean };
211+
clientLoader: () => { d: string; e: Date; f: () => boolean };
212+
HydrateFallback: () => unknown;
213+
}>,
214+
| { a: string; b: Date; c: undefined }
215+
| { d: string; e: Date; f: () => boolean }
216+
>
217+
>,
218+
Expect<
219+
Equal<
220+
GetLoaderData<{
221+
loader: () => { a: string; b: Date; c: () => boolean };
222+
clientLoader: (() => { d: string; e: Date; f: () => boolean }) & {
223+
hydrate: true;
224+
};
225+
}>,
226+
| { a: string; b: Date; c: undefined }
227+
| { d: string; e: Date; f: () => boolean }
228+
>
229+
>,
230+
Expect<
231+
Equal<
232+
GetLoaderData<{
233+
loader: () => { a: string; b: Date; c: () => boolean };
234+
clientLoader: (() => { d: string; e: Date; f: () => boolean }) & {
235+
hydrate: true;
236+
};
237+
HydrateFallback: () => unknown;
238+
}>,
239+
{ d: string; e: Date; f: () => boolean }
240+
>
241+
>,
242+
243+
// ActionData
244+
Expect<Equal<GetActionData<{}>, undefined>>,
245+
Expect<
246+
Equal<
247+
GetActionData<{
248+
action: () => { a: string; b: Date; c: () => boolean };
249+
}>,
250+
{ a: string; b: Date; c: undefined }
251+
>
252+
>,
253+
Expect<
254+
Equal<
255+
GetActionData<{
256+
clientAction: () => { a: string; b: Date; c: () => boolean };
257+
}>,
258+
{ a: string; b: Date; c: () => boolean }
259+
>
260+
>,
261+
Expect<
262+
Equal<
263+
GetActionData<{
264+
action: () => { a: string; b: Date; c: () => boolean };
265+
clientAction: () => { d: string; e: Date; f: () => boolean };
266+
}>,
267+
| { a: string; b: Date; c: undefined }
268+
| { d: string; e: Date; f: () => boolean }
269+
>
270+
>
134271
];

0 commit comments

Comments
 (0)