Skip to content

Commit 4ec9265

Browse files
authored
fix matcher not working with i18n (#518)
* fix middleware matcher not working with i18n * fix headers with i18n remove unnecessary function * add e2e test * Create fifty-clocks-report.md
1 parent 44adea2 commit 4ec9265

File tree

6 files changed

+63
-27
lines changed

6 files changed

+63
-27
lines changed

.changeset/fifty-clocks-report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"open-next": patch
3+
---
4+
5+
fix middleware and headers matcher not working properly with i18n

examples/pages-router/next.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ const nextConfig = {
1212
eslint: {
1313
ignoreDuringBuilds: true,
1414
},
15+
headers: () => [
16+
{
17+
source: "/",
18+
headers: [
19+
{
20+
key: "x-custom-header",
21+
value: "my custom header value",
22+
},
23+
],
24+
},
25+
],
1526
rewrites: () => [
1627
{ source: "/rewrite", destination: "/", locale: false },
1728
{
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
3+
export function middleware(request: NextRequest) {
4+
return NextResponse.next({
5+
headers: {
6+
"x-from-middleware": "true",
7+
},
8+
});
9+
}
10+
11+
export const config = {
12+
matcher: ["/"],
13+
};

packages/open-next/src/core/routing/matcher.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,24 @@ export function addNextConfigHeaders(
144144

145145
const requestHeaders: Record<string, string> = {};
146146

147-
for (const { headers, has, missing, regex, source } of configHeaders) {
147+
const localizedRawPath = localizePath(event);
148+
149+
for (const {
150+
headers,
151+
has,
152+
missing,
153+
regex,
154+
source,
155+
locale,
156+
} of configHeaders) {
157+
const path = locale === false ? rawPath : localizedRawPath;
148158
if (
149-
new RegExp(regex).test(rawPath) &&
159+
new RegExp(regex).test(path) &&
150160
checkHas(matcher, has) &&
151161
checkHas(matcher, missing, true)
152162
) {
153163
const fromSource = match(source);
154-
const _match = fromSource(rawPath);
164+
const _match = fromSource(path);
155165
headers.forEach((h) => {
156166
try {
157167
const key = convertMatch(_match, compile(h.key), h.key);

packages/open-next/src/core/routing/middleware.ts

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { MiddlewareManifest, NextConfig } from "config/index.js";
44
import { InternalEvent, InternalResult } from "types/open-next.js";
55
import { emptyReadableStream } from "utils/stream.js";
66

7+
import { localizePath } from "./i18n/index.js";
78
//NOTE: we should try to avoid importing stuff from next as much as possible
89
// every release of next could break this
910
// const { run } = require("next/dist/server/web/sandbox");
@@ -32,33 +33,12 @@ type MiddlewareOutputEvent = InternalEvent & {
3233
// and res.body prior to processing the next-server.
3334
// @returns undefined | res.end()
3435

35-
// NOTE: We need to normalize the locale path before passing it to the middleware
36-
// See https://github.com/vercel/next.js/blob/39589ff35003ba73f92b7f7b349b3fdd3458819f/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L15
37-
function normalizeLocalePath(pathname: string) {
38-
// first item will be empty string from splitting at first char
39-
const pathnameParts = pathname.split("/");
40-
const locales = NextConfig.i18n?.locales;
41-
42-
(locales || []).some((locale) => {
43-
if (
44-
pathnameParts[1] &&
45-
pathnameParts[1].toLowerCase() === locale.toLowerCase()
46-
) {
47-
pathnameParts.splice(1, 1);
48-
pathname = pathnameParts.join("/") || "/";
49-
return true;
50-
}
51-
return false;
52-
});
53-
54-
return locales && !pathname.endsWith("/") ? `${pathname}/` : pathname;
55-
}
5636
// if res.end() is return, the parent needs to return and not process next server
5737
export async function handleMiddleware(
5838
internalEvent: InternalEvent,
5939
): Promise<MiddlewareOutputEvent | InternalResult> {
60-
const { rawPath, query } = internalEvent;
61-
const normalizedPath = normalizeLocalePath(rawPath);
40+
const { query } = internalEvent;
41+
const normalizedPath = localizePath(internalEvent);
6242
// We only need the normalizedPath to check if the middleware should run
6343
const hasMatch = middleMatch.some((r) => r.test(normalizedPath));
6444
if (!hasMatch) return internalEvent;
@@ -68,7 +48,7 @@ export async function handleMiddleware(
6848
const host = internalEvent.headers.host
6949
? `https://${internalEvent.headers.host}`
7050
: "http://localhost:3000";
71-
const initialUrl = new URL(rawPath, host);
51+
const initialUrl = new URL(normalizedPath, host);
7252
initialUrl.search = convertToQueryString(query);
7353
const url = initialUrl.toString();
7454
// console.log("url", url, normalizedPath);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test("Next config headers with i18n", async ({ page }) => {
4+
const responsePromise = page.waitForResponse((response) => {
5+
return response.status() === 200;
6+
});
7+
await page.goto("/");
8+
9+
const response = await responsePromise;
10+
// Response header should be set
11+
const headers = response.headers();
12+
// Headers from next.config.js should be set
13+
expect(headers["x-custom-header"]).toEqual("my custom header value");
14+
15+
// Headers from middleware should be set
16+
expect(headers["x-from-middleware"]).toEqual("true");
17+
});

0 commit comments

Comments
 (0)