Skip to content

Commit

Permalink
support custom props of layouts with parallel routes, closes #2
Browse files Browse the repository at this point in the history
Signed-off-by: Vu Van Dung <[email protected]>
  • Loading branch information
joulev committed Dec 15, 2023
1 parent c338306 commit d6e63b7
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 13 deletions.
3 changes: 3 additions & 0 deletions apps/demo/app/parallel-route/@test/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <div>Hello world</div>;
}
12 changes: 12 additions & 0 deletions apps/demo/app/parallel-route/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div>
{props.test} {props.children}
</div>
);
}
3 changes: 3 additions & 0 deletions apps/demo/app/parallel-route/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <div>Hello world</div>;
}
10 changes: 5 additions & 5 deletions packages/nextjs-route-types/src/generate-files.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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);
}),
);
Expand All @@ -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);
}
29 changes: 21 additions & 8 deletions packages/nextjs-route-types/src/get-file-content.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { DirectoryTree, GetFileContent } from "./types";

enum PathSegmentType {
OptionalCatchAll = "OptionalCatchAll",
CatchAll = "CatchAll",
Expand Down Expand Up @@ -27,30 +29,41 @@ 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";
type EmptyObject = Record<string, never>;
export type SearchParams = Record<string, string | string[] | undefined>;
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 = {
Expand All @@ -65,4 +78,4 @@ export type RouteHandlerContext = {
type HandlerReturn = Response | Promise<Response>;
export type RouteHandler = (request: NextRequest, context: RouteHandlerContext) => HandlerReturn;
`.trimStart();
}
};
2 changes: 2 additions & 0 deletions packages/nextjs-route-types/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export interface DirectoryTreeItem {
}

export type DirectoryTree = DirectoryTreeItem[];

export type GetFileContent = (dirNames: string[], children: DirectoryTree) => string;

0 comments on commit d6e63b7

Please sign in to comment.