Skip to content

Commit 4db4082

Browse files
Add relative routes.ts helpers (#11966)
1 parent 3df18ca commit 4db4082

File tree

4 files changed

+119
-12
lines changed

4 files changed

+119
-12
lines changed

integration/routes-config-test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,28 @@ test.describe("routes config", () => {
224224
);
225225
}).toPass();
226226
});
227+
228+
test("supports absolute route file paths", async ({ page, dev }) => {
229+
let files: Files = async ({ port }) => ({
230+
"vite.config.js": await viteConfig.basic({ port }),
231+
"app/routes.ts": js`
232+
import path from "node:path";
233+
import { type RouteConfig } from "@react-router/dev/routes";
234+
235+
export const routes: RouteConfig = [
236+
{
237+
file: path.resolve(import.meta.dirname, "test-route.tsx"),
238+
index: true,
239+
},
240+
];
241+
`,
242+
"app/test-route.tsx": `
243+
export default () => <div data-test-route>Test route</div>
244+
`,
245+
});
246+
let { port } = await dev(files);
247+
248+
await page.goto(`http://localhost:${port}/`, { waitUntil: "networkidle" });
249+
await expect(page.locator("[data-test-route]")).toHaveText("Test route");
250+
});
227251
});

packages/react-router-dev/__tests__/routes-config-test.ts

+65-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { route, layout, index } from "../config/routes";
1+
import { route, layout, index, relative } from "../config/routes";
22

33
describe("routes config", () => {
44
describe("route helpers", () => {
@@ -196,5 +196,69 @@ describe("routes config", () => {
196196
`);
197197
});
198198
});
199+
200+
describe("relative", () => {
201+
it("supports relative routes", () => {
202+
let { route } = relative("/path/to/dirname");
203+
expect(
204+
route("parent", "nested/parent.tsx", [
205+
route("child", "nested/child.tsx", { id: "child" }),
206+
])
207+
).toMatchInlineSnapshot(`
208+
{
209+
"children": [
210+
{
211+
"children": undefined,
212+
"file": "/path/to/dirname/nested/child.tsx",
213+
"id": "child",
214+
"path": "child",
215+
},
216+
],
217+
"file": "/path/to/dirname/nested/parent.tsx",
218+
"path": "parent",
219+
}
220+
`);
221+
});
222+
223+
it("supports relative index routes", () => {
224+
let { index } = relative("/path/to/dirname");
225+
expect([
226+
index("nested/without-options.tsx"),
227+
index("nested/with-options.tsx", { id: "with-options" }),
228+
]).toMatchInlineSnapshot(`
229+
[
230+
{
231+
"file": "/path/to/dirname/nested/without-options.tsx",
232+
"index": true,
233+
},
234+
{
235+
"file": "/path/to/dirname/nested/with-options.tsx",
236+
"id": "with-options",
237+
"index": true,
238+
},
239+
]
240+
`);
241+
});
242+
243+
it("supports relative layout routes", () => {
244+
let { layout } = relative("/path/to/dirname");
245+
expect(
246+
layout("nested/parent.tsx", [
247+
layout("nested/child.tsx", { id: "child" }),
248+
])
249+
).toMatchInlineSnapshot(`
250+
{
251+
"children": [
252+
{
253+
"children": undefined,
254+
"file": "/path/to/dirname/nested/child.tsx",
255+
"id": "child",
256+
},
257+
],
258+
"file": "/path/to/dirname/nested/parent.tsx",
259+
}
260+
`);
261+
});
262+
});
199263
});
200264
});

packages/react-router-dev/config/routes.ts

+23-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as path from "node:path";
1+
import { resolve, win32 } from "node:path";
22
import pick from "lodash/pick";
33
import invariant from "../invariant";
44

@@ -92,11 +92,6 @@ export interface RouteConfigEntry {
9292

9393
type CreateRoutePath = string | null | undefined;
9494

95-
type RequireAtLeastOne<T> = {
96-
[K in keyof T]-?: Required<Pick<T, K>> &
97-
Partial<Pick<T, Exclude<keyof T, K>>>;
98-
}[keyof T];
99-
10095
const createConfigRouteOptionKeys = [
10196
"id",
10297
"index",
@@ -114,7 +109,7 @@ function createRoute(
114109
function createRoute(
115110
path: CreateRoutePath,
116111
file: string,
117-
options: RequireAtLeastOne<CreateRouteOptions>,
112+
options: CreateRouteOptions,
118113
children?: RouteConfigEntry[]
119114
): RouteConfigEntry;
120115
function createRoute(
@@ -148,7 +143,7 @@ type CreateIndexOptions = Pick<
148143
>;
149144
function createIndex(
150145
file: string,
151-
options?: RequireAtLeastOne<CreateIndexOptions>
146+
options?: CreateIndexOptions
152147
): RouteConfigEntry {
153148
return {
154149
file,
@@ -170,7 +165,7 @@ function createLayout(
170165
): RouteConfigEntry;
171166
function createLayout(
172167
file: string,
173-
options: RequireAtLeastOne<CreateLayoutOptions>,
168+
options: CreateLayoutOptions,
174169
children?: RouteConfigEntry[]
175170
): RouteConfigEntry;
176171
function createLayout(
@@ -196,6 +191,24 @@ function createLayout(
196191
export const route = createRoute;
197192
export const index = createIndex;
198193
export const layout = createLayout;
194+
type RouteHelpers = {
195+
route: typeof route;
196+
index: typeof index;
197+
layout: typeof layout;
198+
};
199+
export function relative(directory: string): RouteHelpers {
200+
return {
201+
route: (path, file, ...rest) => {
202+
return route(path, resolve(directory, file), ...(rest as any));
203+
},
204+
index: (file, ...rest) => {
205+
return index(resolve(directory, file), ...(rest as any));
206+
},
207+
layout: (file, ...rest) => {
208+
return layout(resolve(directory, file), ...(rest as any));
209+
},
210+
};
211+
}
199212

200213
export function configRoutesToRouteManifest(
201214
routes: RouteConfigEntry[],
@@ -240,7 +253,7 @@ function createRouteId(file: string) {
240253
}
241254

242255
function normalizeSlashes(file: string) {
243-
return file.split(path.win32.sep).join("/");
256+
return file.split(win32.sep).join("/");
244257
}
245258

246259
function stripFileExtension(file: string) {

packages/react-router-dev/routes.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
export type { RouteConfig, RouteConfigEntry } from "./config/routes";
22

3-
export { route, index, layout, getAppDirectory } from "./config/routes";
3+
export {
4+
route,
5+
index,
6+
layout,
7+
relative,
8+
getAppDirectory,
9+
} from "./config/routes";

0 commit comments

Comments
 (0)