Skip to content

Commit 04287d2

Browse files
committed
Merge branch 'dev' into markdalgleish/lazy-route-properties
2 parents 55e1d27 + 2e661fb commit 04287d2

File tree

10 files changed

+117
-17
lines changed

10 files changed

+117
-17
lines changed

.changeset/gentle-gifts-kneel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix types on `unstable_MiddlewareFunction` to avoid type errors when a middleware doesn't return a value

.changeset/honest-mangos-sneeze.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/express": patch
3+
---
4+
5+
Better validation of `x-forwarded-host` header to preent potential security issues.

.changeset/six-mugs-scream.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
When `future.unstable_viteEnvironmentApi` is enabled, ensure critical CSS in development works when using a custom Vite `base` has been configured

integration/helpers/vite.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const viteConfig = {
102102

103103
export const EXPRESS_SERVER = (args: {
104104
port: number;
105+
base?: string;
105106
loadContext?: Record<string, unknown>;
106107
}) =>
107108
String.raw`

integration/vite-css-test.ts

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,19 @@ const files = {
152152
`,
153153
};
154154

155-
const VITE_CONFIG = async (port: number) => dedent`
155+
const VITE_CONFIG = async ({
156+
port,
157+
base,
158+
}: {
159+
port: number;
160+
base?: string;
161+
}) => dedent`
156162
import { reactRouter } from "@react-router/dev/vite";
157163
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
158164
159165
export default {
160166
${await viteConfig.server({ port })}
167+
${base ? `base: "${base}",` : ""}
161168
plugins: [
162169
reactRouter(),
163170
vanillaExtractPlugin({
@@ -182,7 +189,7 @@ test.describe("Vite CSS", () => {
182189
"react-router.config.ts": reactRouterConfig({
183190
viteEnvironmentApi: templateName === "vite-6-template",
184191
}),
185-
"vite.config.ts": await VITE_CONFIG(port),
192+
"vite.config.ts": await VITE_CONFIG({ port }),
186193
...files,
187194
},
188195
templateName
@@ -207,6 +214,45 @@ test.describe("Vite CSS", () => {
207214
});
208215
});
209216

217+
test.describe("vite dev with custom base", async () => {
218+
let port: number;
219+
let cwd: string;
220+
let stop: () => void;
221+
let base = "/custom/base/";
222+
223+
test.beforeAll(async () => {
224+
port = await getPort();
225+
cwd = await createProject(
226+
{
227+
"react-router.config.ts": reactRouterConfig({
228+
viteEnvironmentApi: templateName === "vite-6-template",
229+
basename: base,
230+
}),
231+
"vite.config.ts": await VITE_CONFIG({ port, base }),
232+
...files,
233+
},
234+
templateName
235+
);
236+
stop = await dev({ cwd, port });
237+
});
238+
test.afterAll(() => stop());
239+
240+
test.describe(() => {
241+
test.use({ javaScriptEnabled: false });
242+
test("without JS", async ({ page }) => {
243+
await pageLoadWorkflow({ page, port, base });
244+
});
245+
});
246+
247+
test.describe(() => {
248+
test.use({ javaScriptEnabled: true });
249+
test("with JS", async ({ page }) => {
250+
await pageLoadWorkflow({ page, port, base });
251+
await hmrWorkflow({ page, port, cwd, base });
252+
});
253+
});
254+
});
255+
210256
test.describe("express", async () => {
211257
let port: number;
212258
let cwd: string;
@@ -215,7 +261,7 @@ test.describe("Vite CSS", () => {
215261
test.beforeAll(async () => {
216262
port = await getPort();
217263
cwd = await createProject({
218-
"vite.config.ts": await VITE_CONFIG(port),
264+
"vite.config.ts": await VITE_CONFIG({ port }),
219265
"server.mjs": EXPRESS_SERVER({ port }),
220266
...files,
221267
});
@@ -247,7 +293,7 @@ test.describe("Vite CSS", () => {
247293
test.beforeAll(async () => {
248294
port = await getPort();
249295
cwd = await createProject({
250-
"vite.config.ts": await VITE_CONFIG(port),
296+
"vite.config.ts": await VITE_CONFIG({ port }),
251297
...files,
252298
});
253299

@@ -290,11 +336,19 @@ test.describe("Vite CSS", () => {
290336
});
291337
});
292338

293-
async function pageLoadWorkflow({ page, port }: { page: Page; port: number }) {
339+
async function pageLoadWorkflow({
340+
page,
341+
port,
342+
base,
343+
}: {
344+
page: Page;
345+
port: number;
346+
base?: string;
347+
}) {
294348
let pageErrors: Error[] = [];
295349
page.on("pageerror", (error) => pageErrors.push(error));
296350

297-
await page.goto(`http://localhost:${port}/`, {
351+
await page.goto(`http://localhost:${port}${base ?? "/"}`, {
298352
waitUntil: "networkidle",
299353
});
300354

@@ -316,15 +370,17 @@ async function hmrWorkflow({
316370
page,
317371
cwd,
318372
port,
373+
base,
319374
}: {
320375
page: Page;
321376
cwd: string;
322377
port: number;
378+
base?: string;
323379
}) {
324380
let pageErrors: Error[] = [];
325381
page.on("pageerror", (error) => pageErrors.push(error));
326382

327-
await page.goto(`http://localhost:${port}/`, {
383+
await page.goto(`http://localhost:${port}${base ?? "/"}`, {
328384
waitUntil: "networkidle",
329385
});
330386

packages/react-router-dev/vite/plugin.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -765,9 +765,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
765765
export const unstable_getCriticalCss = ({ pathname }) => {
766766
return {
767767
rel: "stylesheet",
768-
href: "${
769-
viteUserConfig.base ?? "/"
770-
}@react-router/critical.css?pathname=" + pathname,
768+
href: "${ctx.publicPath}@react-router/critical.css?pathname=" + pathname,
771769
};
772770
}
773771
`
@@ -1566,7 +1564,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
15661564
if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi) {
15671565
viteDevServer.middlewares.use(async (req, res, next) => {
15681566
let [reqPathname, reqSearch] = (req.url ?? "").split("?");
1569-
if (reqPathname === "/@react-router/critical.css") {
1567+
if (reqPathname === `${ctx.publicPath}@react-router/critical.css`) {
15701568
let pathname = new URLSearchParams(reqSearch).get("pathname");
15711569
if (!pathname) {
15721570
return next("No pathname provided");

packages/react-router-express/__tests__/server-test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,28 @@ describe("express createRemixRequest", () => {
217217
);
218218
expect(remixRequest.headers.get("host")).toBe("localhost:3000");
219219
});
220+
221+
it("validates parsed port", async () => {
222+
let expressRequest = createRequest({
223+
url: "/foo/bar",
224+
method: "GET",
225+
protocol: "http",
226+
hostname: "localhost",
227+
headers: {
228+
"Cache-Control": "max-age=300, s-maxage=3600",
229+
Host: "localhost:3000",
230+
"x-forwarded-host": ":/spoofed",
231+
},
232+
});
233+
let expressResponse = createResponse();
234+
235+
let remixRequest = createRemixRequest(expressRequest, expressResponse);
236+
237+
expect(remixRequest.method).toBe("GET");
238+
expect(remixRequest.headers.get("cache-control")).toBe(
239+
"max-age=300, s-maxage=3600"
240+
);
241+
expect(remixRequest.headers.get("host")).toBe("localhost:3000");
242+
expect(remixRequest.url).toBe("http://localhost:3000/foo/bar");
243+
});
220244
});

packages/react-router-express/server.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,15 @@ export function createRemixRequest(
9898
): Request {
9999
// req.hostname doesn't include port information so grab that from
100100
// `X-Forwarded-Host` or `Host`
101-
let [, hostnamePort] = req.get("X-Forwarded-Host")?.split(":") ?? [];
102-
let [, hostPort] = req.get("host")?.split(":") ?? [];
103-
let port = hostnamePort || hostPort;
101+
let [, hostnamePortStr] = req.get("X-Forwarded-Host")?.split(":") ?? [];
102+
let [, hostPortStr] = req.get("host")?.split(":") ?? [];
103+
let hostnamePort = Number.parseInt(hostnamePortStr, 10);
104+
let hostPort = Number.parseInt(hostPortStr, 10);
105+
let port = Number.isSafeInteger(hostnamePort)
106+
? hostnamePort
107+
: Number.isSafeInteger(hostPort)
108+
? hostPort
109+
: "";
104110
// Use req.hostname here as it respects the "trust proxy" setting
105111
let resolvedHost = `${req.hostname}${port ? `:${port}` : ""}`;
106112
// Use `req.originalUrl` so Remix is aware of the full path

packages/react-router/lib/router/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export interface unstable_MiddlewareNextFunction<Result = unknown> {
220220
export type unstable_MiddlewareFunction<Result = unknown> = (
221221
args: DataFunctionArgs<unstable_RouterContextProvider>,
222222
next: unstable_MiddlewareNextFunction<Result>
223-
) => MaybePromise<Result | undefined>;
223+
) => MaybePromise<Result | void>;
224224

225225
/**
226226
* Arguments passed to loader functions

packages/react-router/lib/types/route-module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,12 @@ type ServerDataFunctionArgs<T extends RouteInfo> = {
190190
export type CreateServerMiddlewareFunction<T extends RouteInfo> = (
191191
args: ServerDataFunctionArgs<T>,
192192
next: unstable_MiddlewareNextFunction<Response>
193-
) => MaybePromise<Response | undefined>;
193+
) => MaybePromise<Response | void>;
194194

195195
export type CreateClientMiddlewareFunction<T extends RouteInfo> = (
196196
args: ClientDataFunctionArgs<T>,
197197
next: unstable_MiddlewareNextFunction<undefined>
198-
) => MaybePromise<undefined>;
198+
) => MaybePromise<void>;
199199

200200
export type CreateServerLoaderArgs<T extends RouteInfo> =
201201
ServerDataFunctionArgs<T>;

0 commit comments

Comments
 (0)