Skip to content

Commit 03b2b53

Browse files
committed
Add support for custom entry file paths.
1 parent 237304d commit 03b2b53

File tree

3 files changed

+272
-31
lines changed

3 files changed

+272
-31
lines changed

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
- bobziroll
5252
- bravo-kernel
5353
- Brendonovich
54+
- brettburley
5455
- briankb
5556
- BrianT1414
5657
- brockross

integration/file-path-config-test.ts

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { expect } from "@playwright/test";
2+
import type { Files } from "./helpers/vite.js";
3+
import { test, viteConfig, build, createProject } from "./helpers/vite.js";
4+
5+
const js = String.raw;
6+
7+
const simpleFiles: Files = async ({ port }) => ({
8+
"vite.config.ts": await viteConfig.basic({ port }),
9+
"react-router.config.ts": js`
10+
export default {
11+
rootRouteFile: "custom/root.tsx",
12+
routesFile: "custom/app-routes.ts",
13+
clientEntryFile: "custom/entry.client.tsx",
14+
serverEntryFile: "custom/entry.server.tsx",
15+
};
16+
`,
17+
"app/custom/root.tsx": js`
18+
import { Links, Meta, Outlet, Scripts } from "react-router";
19+
20+
export default function Root() {
21+
return (
22+
<html lang="en">
23+
<head>
24+
<Meta />
25+
<Links />
26+
</head>
27+
<body>
28+
<div id="content">
29+
<h1>Custom Root</h1>
30+
<Outlet />
31+
</div>
32+
<Scripts />
33+
</body>
34+
</html>
35+
);
36+
}
37+
`,
38+
"app/custom/app-routes.ts": js`
39+
import { type RouteConfig, index } from "@react-router/dev/routes";
40+
41+
export default [
42+
index("index.tsx"),
43+
] satisfies RouteConfig;
44+
`,
45+
"app/index.tsx": js`
46+
export default function IndexRoute() {
47+
return <div id="hydrated" onClick={() => {}}>Custom IndexRoute</div>
48+
}
49+
`,
50+
"app/custom/entry.client.tsx": js`
51+
import { HydratedRouter } from "react-router/dom";
52+
import { startTransition, StrictMode } from "react";
53+
import { hydrateRoot } from "react-dom/client";
54+
55+
window.__customClientEntryExecuted = true;
56+
57+
startTransition(() => {
58+
hydrateRoot(
59+
document,
60+
<StrictMode>
61+
<HydratedRouter discover={"none"} />
62+
</StrictMode>
63+
);
64+
});
65+
`,
66+
"app/custom/entry.server.tsx": js`
67+
import * as React from "react";
68+
import { ServerRouter } from "react-router";
69+
import { renderToString } from "react-dom/server";
70+
71+
export default function handleRequest(
72+
request,
73+
responseStatusCode,
74+
responseHeaders,
75+
remixContext
76+
) {
77+
let markup = renderToString(
78+
<ServerRouter context={remixContext} url={request.url} />
79+
);
80+
responseHeaders.set("Content-Type", "text/html");
81+
responseHeaders.set("X-Custom-Server-Entry", "true");
82+
return new Response('<!DOCTYPE html>' + markup, {
83+
headers: responseHeaders,
84+
status: responseStatusCode,
85+
});
86+
}
87+
`,
88+
});
89+
90+
test.describe("File path configuration", () => {
91+
test("uses custom file paths", async ({ page, dev, request }) => {
92+
let { port } = await dev(simpleFiles);
93+
const response = await page.goto(`http://localhost:${port}/`);
94+
95+
// Verify custom root.tsx and app-routes.ts is being used.
96+
await expect(page.locator("h1")).toHaveText("Custom Root");
97+
await expect(page.locator("#content div")).toHaveText("Custom IndexRoute");
98+
99+
// Verify client entry is being used.
100+
expect(
101+
await page.evaluate(() => (window as any).__customClientEntryExecuted)
102+
).toBe(true);
103+
104+
// Verify server entry is used by checking for the custom header.
105+
expect(response?.headers()["x-custom-server-entry"]).toBe("true");
106+
});
107+
108+
test("fails build when custom rootRouteFile doesn't exist", async () => {
109+
let cwd = await createProject({
110+
"react-router.config.ts": js`
111+
export default {
112+
rootRouteFile: "custom/nonexistent-root.tsx"
113+
};
114+
`,
115+
});
116+
let buildResult = build({ cwd });
117+
expect(buildResult.status).toBe(1);
118+
expect(buildResult.stderr.toString()).toContain(
119+
'Could not find "root" entry file at'
120+
);
121+
expect(buildResult.stderr.toString()).toContain("nonexistent-root.tsx");
122+
});
123+
124+
test("fails build when custom routesFile doesn't exist", async () => {
125+
let cwd = await createProject({
126+
"app/root.tsx": js`
127+
export default function Root() {
128+
return <div>Root</div>;
129+
}
130+
`,
131+
"react-router.config.ts": js`
132+
export default {
133+
routesFile: "custom/nonexistent-routes.ts"
134+
};
135+
`,
136+
});
137+
let buildResult = build({ cwd });
138+
expect(buildResult.status).toBe(1);
139+
expect(buildResult.stderr.toString()).toContain(
140+
'Could not find "routes" entry file at'
141+
);
142+
expect(buildResult.stderr.toString()).toContain("nonexistent-routes.ts");
143+
});
144+
145+
test("fails build when custom clientEntryFile doesn't exist", async () => {
146+
let cwd = await createProject({
147+
"app/root.tsx": js`
148+
export default function Root() {
149+
return <div>Root</div>;
150+
}
151+
`,
152+
"app/routes.ts": js`
153+
export default [];
154+
`,
155+
"react-router.config.ts": js`
156+
export default {
157+
clientEntryFile: "custom/nonexistent-entry.client.tsx"
158+
};
159+
`,
160+
});
161+
let buildResult = build({ cwd });
162+
expect(buildResult.status).toBe(1);
163+
expect(buildResult.stderr.toString()).toContain(
164+
'Could not find "entry.client" entry file at'
165+
);
166+
expect(buildResult.stderr.toString()).toContain("nonexistent-entry.client.tsx");
167+
});
168+
169+
test("fails build when custom serverEntryFile doesn't exist", async () => {
170+
let cwd = await createProject({
171+
"app/root.tsx": js`
172+
export default function Root() {
173+
return <div>Root</div>;
174+
}
175+
`,
176+
"app/routes.ts": js`
177+
export default [];
178+
`,
179+
"react-router.config.ts": js`
180+
export default {
181+
serverEntryFile: "custom/nonexistent-entry.server.tsx"
182+
};
183+
`,
184+
});
185+
let buildResult = build({ cwd });
186+
expect(buildResult.status).toBe(1);
187+
expect(buildResult.stderr.toString()).toContain(
188+
'Could not find "entry.server" entry file at'
189+
);
190+
expect(buildResult.stderr.toString()).toContain("nonexistent-entry.server.tsx");
191+
});
192+
});

0 commit comments

Comments
 (0)