diff --git a/apps/demo/app/parallel-route/@test/page.tsx b/apps/demo/app/parallel-route/@test/page.tsx
new file mode 100644
index 0000000..2f2522e
--- /dev/null
+++ b/apps/demo/app/parallel-route/@test/page.tsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return
Hello world
;
+}
diff --git a/apps/demo/app/parallel-route/layout.tsx b/apps/demo/app/parallel-route/layout.tsx
new file mode 100644
index 0000000..9054001
--- /dev/null
+++ b/apps/demo/app/parallel-route/layout.tsx
@@ -0,0 +1,12 @@
+import { expectType } from "ts-expect";
+
+import type { LayoutProps } from "./$types";
+
+export default function Layout(props: LayoutProps) {
+ expectType<{ test: React.ReactNode; children: React.ReactNode }>(props);
+ return (
+
+ {props.test} {props.children}
+
+ );
+}
diff --git a/apps/demo/app/parallel-route/page.tsx b/apps/demo/app/parallel-route/page.tsx
new file mode 100644
index 0000000..2f2522e
--- /dev/null
+++ b/apps/demo/app/parallel-route/page.tsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return Hello world
;
+}
diff --git a/packages/nextjs-route-types/src/generate-files.ts b/packages/nextjs-route-types/src/generate-files.ts
index 11b663c..879e6ee 100644
--- a/packages/nextjs-route-types/src/generate-files.ts
+++ b/packages/nextjs-route-types/src/generate-files.ts
@@ -1,7 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
-import type { DirectoryTree } from "./types";
+import type { DirectoryTree, GetFileContent } from "./types";
import { removeCwdFromPath } from "./utils";
const FOLDER_NAME = ".next-types";
@@ -12,14 +12,14 @@ async function generateFilesRecursive(
rootAppDir: string,
tree: DirectoryTree,
pathSoFar: string[],
- getFileContent: (dirNames: string[]) => string,
+ getFileContent: GetFileContent,
) {
await Promise.all(
tree.map(async item => {
const newPath = [...pathSoFar, item.name];
const fullPath = path.join(rootAppDir, ...newPath);
await fs.mkdir(fullPath, { recursive: true });
- await fs.writeFile(path.join(fullPath, FILE_NAME), getFileContent(newPath));
+ await fs.writeFile(path.join(fullPath, FILE_NAME), getFileContent(newPath, item.children));
await generateFilesRecursive(rootAppDir, item.children, newPath, getFileContent);
}),
);
@@ -28,10 +28,10 @@ async function generateFilesRecursive(
export async function generateFiles(
appDir: string,
tree: DirectoryTree,
- getFileContent: (dirNames: string[]) => string,
+ getFileContent: GetFileContent,
) {
const rootAppDir = path.join(root, removeCwdFromPath(appDir));
await fs.mkdir(rootAppDir, { recursive: true });
- await fs.writeFile(path.join(rootAppDir, FILE_NAME), getFileContent([]));
+ await fs.writeFile(path.join(rootAppDir, FILE_NAME), getFileContent([], tree));
await generateFilesRecursive(rootAppDir, tree, [], getFileContent);
}
diff --git a/packages/nextjs-route-types/src/get-file-content.ts b/packages/nextjs-route-types/src/get-file-content.ts
index 6aa7a3b..3e7827a 100644
--- a/packages/nextjs-route-types/src/get-file-content.ts
+++ b/packages/nextjs-route-types/src/get-file-content.ts
@@ -1,3 +1,5 @@
+import type { DirectoryTree, GetFileContent } from "./types";
+
enum PathSegmentType {
OptionalCatchAll = "OptionalCatchAll",
CatchAll = "CatchAll",
@@ -27,12 +29,26 @@ function getTsTypeFromPathSegmentType(type: PathSegmentType) {
}
}
-export function getFileContent(path: string[]) {
+function getParallelRoutesFromChildren(children: DirectoryTree) {
+ const parallelRoutes = children
+ .filter(child => child.name.startsWith("@"))
+ .map(child => child.name.slice(1));
+ if (!parallelRoutes.includes("children")) parallelRoutes.push("children");
+ return parallelRoutes;
+}
+
+export const getFileContent: GetFileContent = (path, children) => {
const params = getDynamicParamsFromPath(path);
- const tsInterfaceContent = params
+ const paramsTsInterfaceContent = params
.map(([type, name]) => ` "${name}": ${getTsTypeFromPathSegmentType(type)}`)
.join(";\n")
.trim();
+ const parallelRoutes = getParallelRoutesFromChildren(children);
+ const layoutPropsTsInterfaceContent = parallelRoutes
+ .map(route => ` "${route}": ReactNode`)
+ .concat(" params: Params")
+ .join(";\n")
+ .trim();
return `
import type { NextRequest } from "next/server";
import type { ReactNode } from "react";
@@ -40,17 +56,14 @@ import type { ReactNode } from "react";
type EmptyObject = Record;
export type SearchParams = Record;
-export type Params = ${params.length ? `{\n ${tsInterfaceContent};\n}` : "EmptyObject"};
+export type Params = ${params.length ? `{\n ${paramsTsInterfaceContent};\n}` : "EmptyObject"};
export type DefaultProps = any; // Need help: I have never used default.tsx and its documentation is still WIP
export type ErrorProps = {
error: Error & { digest?: string };
reset: () => void;
};
-export type LayoutProps = {
- children: ReactNode;
- params: Params;
-};
+export type LayoutProps = {\n ${layoutPropsTsInterfaceContent};\n};
export type LoadingProps = EmptyObject;
export type NotFoundProps = EmptyObject;
export type PageProps = {
@@ -65,4 +78,4 @@ export type RouteHandlerContext = {
type HandlerReturn = Response | Promise;
export type RouteHandler = (request: NextRequest, context: RouteHandlerContext) => HandlerReturn;
`.trimStart();
-}
+};
diff --git a/packages/nextjs-route-types/src/types.ts b/packages/nextjs-route-types/src/types.ts
index 92c5d0b..283315f 100644
--- a/packages/nextjs-route-types/src/types.ts
+++ b/packages/nextjs-route-types/src/types.ts
@@ -4,3 +4,5 @@ export interface DirectoryTreeItem {
}
export type DirectoryTree = DirectoryTreeItem[];
+
+export type GetFileContent = (dirNames: string[], children: DirectoryTree) => string;