Skip to content

Commit 1874372

Browse files
committed
Add typegen support for routes outside appDirectory
1 parent 368e0cb commit 1874372

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

integration/typegen-test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from "node:fs/promises";
22

3+
import { expect } from "@playwright/test";
34
import tsx from "dedent";
45
import * as Path from "pathe";
56

@@ -336,6 +337,57 @@ test.describe("typegen", () => {
336337
await $("pnpm typecheck");
337338
});
338339

340+
test("routes outside app dir", async ({ cwd, edit, $ }) => {
341+
// Create the subdirectories
342+
await fs.mkdir(Path.join(cwd, "app/router"), { recursive: true });
343+
await fs.mkdir(Path.join(cwd, "app/pages"), { recursive: true });
344+
345+
await edit({
346+
"react-router.config.ts": tsx`
347+
export default {
348+
appDirectory: "app/router",
349+
}
350+
`,
351+
"app/router/routes.ts": tsx`
352+
import { type RouteConfig, route } from "@react-router/dev/routes";
353+
354+
export default [
355+
route("products/:id", "../pages/product.tsx")
356+
] satisfies RouteConfig;
357+
`,
358+
"app/router/root.tsx": tsx`
359+
import { Outlet } from "react-router";
360+
361+
export default function Root() {
362+
return <Outlet />;
363+
}
364+
`,
365+
"app/pages/product.tsx": tsx`
366+
import type { Expect, Equal } from "../expect-type"
367+
import type { Route } from "./+types/product"
368+
369+
export function loader({ params }: Route.LoaderArgs) {
370+
type Test = Expect<Equal<typeof params, { id: string }>>
371+
return { planet: "world" }
372+
}
373+
374+
export default function Component({ loaderData }: Route.ComponentProps) {
375+
type Test = Expect<Equal<typeof loaderData.planet, string>>
376+
return <h1>Hello, {loaderData.planet}!</h1>
377+
}
378+
`,
379+
});
380+
await $("pnpm typecheck");
381+
382+
// Verify that the types file was generated in the correct location
383+
const annotationPath = Path.join(
384+
cwd,
385+
".react-router/types/app/pages/+types/product.ts",
386+
);
387+
const annotation = await fs.readFile(annotationPath, "utf8");
388+
expect(annotation).toContain("export namespace Route");
389+
});
390+
339391
test("matches", async ({ edit, $ }) => {
340392
await edit({
341393
"app/routes.ts": tsx`

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export function generateRoutes(ctx: Context): Array<VirtualFile> {
116116

117117
// **/+types/*.ts
118118
const allAnnotations: Array<VirtualFile> = Array.from(fileToRoutes.entries())
119-
.filter(([file]) => isInAppDirectory(ctx, file))
119+
.filter(([file]) => isInRootDirectory(ctx, file))
120120
.map(([file, routeIds]) =>
121121
getRouteAnnotations({ ctx, file, routeIds, lineages }),
122122
);
@@ -193,9 +193,9 @@ function routeFilesType({
193193
);
194194
}
195195

196-
function isInAppDirectory(ctx: Context, routeFile: string): boolean {
196+
function isInRootDirectory(ctx: Context, routeFile: string): boolean {
197197
const path = Path.resolve(ctx.config.appDirectory, routeFile);
198-
return path.startsWith(ctx.config.appDirectory);
198+
return path.startsWith(ctx.rootDirectory);
199199
}
200200

201201
function getRouteAnnotations({

0 commit comments

Comments
 (0)