Skip to content

Commit 7b9f56b

Browse files
authored
Fix trailing slash (#262)
* fix trailing slash * fix rewrite simple form
1 parent eb2cfbd commit 7b9f56b

File tree

8 files changed

+71
-15
lines changed

8 files changed

+71
-15
lines changed

examples/pages-router/next.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const nextConfig = {
55
reactStrictMode: true,
66
output: "standalone",
77
outputFileTracing: "../sst",
8+
rewrites: () => [{ source: "/rewrite", destination: "/" }],
9+
trailingSlash: true,
810
};
911

1012
module.exports = nextConfig;

packages/open-next/src/adapters/config/util.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ export function loadRoutesManifest(nextDir: string) {
4545
};
4646

4747
return {
48-
rewrites: {
49-
beforeFiles: routesManifest.rewrites.beforeFiles ?? [],
50-
afterFiles: routesManifest.rewrites.afterFiles ?? [],
51-
fallback: routesManifest.rewrites.fallback ?? [],
52-
},
48+
rewrites: Array.isArray(routesManifest.rewrites)
49+
? { beforeFiles: [], afterFiles: routesManifest.rewrites, fallback: [] }
50+
: {
51+
beforeFiles: routesManifest.rewrites.beforeFiles ?? [],
52+
afterFiles: routesManifest.rewrites.afterFiles ?? [],
53+
fallback: routesManifest.rewrites.fallback ?? [],
54+
},
5355
redirects: routesManifest.redirects ?? [],
5456
routes: {
5557
static: routesManifest.staticRoutes ?? [],

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { compile, Match, match, PathFunction } from "path-to-regexp";
22

3+
import { NextConfig } from "../config";
34
import { InternalEvent, InternalResult } from "../event-mapper";
45
import { debug } from "../logger";
56
import {
@@ -170,7 +171,40 @@ export function handleRedirects(
170171
event: InternalEvent,
171172
redirects: RedirectDefinition[],
172173
): InternalResult | undefined {
173-
const { internalEvent, __rewrite } = handleRewrites(event, redirects);
174+
if (
175+
NextConfig.trailingSlash &&
176+
!event.rawPath.endsWith("/") &&
177+
!event.headers["x-nextjs-data"]
178+
) {
179+
return {
180+
type: event.type,
181+
statusCode: 308,
182+
headers: {
183+
Location: event.url + "/",
184+
},
185+
body: "",
186+
isBase64Encoded: false,
187+
};
188+
// eslint-disable-next-line sonarjs/elseif-without-else
189+
} else if (
190+
!NextConfig.trailingSlash &&
191+
event.rawPath.endsWith("/") &&
192+
event.rawPath !== "/"
193+
) {
194+
return {
195+
type: event.type,
196+
statusCode: 308,
197+
headers: {
198+
Location: event.url.replace(/\/$/, ""),
199+
},
200+
body: "",
201+
isBase64Encoded: false,
202+
};
203+
}
204+
const { internalEvent, __rewrite } = handleRewrites(
205+
event,
206+
redirects.filter((r) => !r.internal),
207+
);
174208
if (__rewrite && !__rewrite.internal) {
175209
return {
176210
type: event.type,

packages/open-next/src/adapters/types/next-types.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,13 @@ export interface RoutesManifest {
101101
dynamicRoutes: RouteDefinition[];
102102
staticRoutes: RouteDefinition[];
103103
dataRoutes: DataRouteDefinition[];
104-
rewrites: {
105-
beforeFiles: RewriteDefinition[];
106-
afterFiles: RewriteDefinition[];
107-
fallback: RewriteDefinition[];
108-
};
104+
rewrites:
105+
| {
106+
beforeFiles: RewriteDefinition[];
107+
afterFiles: RewriteDefinition[];
108+
fallback: RewriteDefinition[];
109+
}
110+
| RewriteDefinition[];
109111
redirects: RedirectDefinition[];
110112
headers?: Header[];
111113
}

packages/tests-e2e/tests/pagesRouter/isr.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { expect, test } from "@playwright/test";
44
test("Incremental Static Regeneration", async ({ page }) => {
55
test.setTimeout(45000);
66
await page.goto("/");
7-
await page.locator("[href='/isr']").click();
8-
await page.waitForURL("/isr");
7+
await page.locator("[href='/isr/']").click();
8+
await page.waitForURL("/isr/");
99
// Load the page a couple times to regenerate ISR
1010

1111
let el = page.getByText("Time:");
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test("Single Rewrite", async ({ page }) => {
4+
await page.goto("/rewrite");
5+
6+
let el = page.getByText("Nextjs Pages Router");
7+
await expect(el).toBeVisible();
8+
});

packages/tests-e2e/tests/pagesRouter/ssr.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { expect, test } from "@playwright/test";
33

44
test("Server Side Render", async ({ page }) => {
55
await page.goto("/");
6-
await page.locator('[href="/ssr"]').click();
6+
await page.locator('[href="/ssr/"]').click();
77

8-
await page.waitForURL("/ssr");
8+
await page.waitForURL("/ssr/");
99
let el = page.getByText("Time:");
1010
await expect(el).toBeVisible();
1111
let time = await el.textContent();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test("trailingSlash redirect", async ({ page }) => {
4+
const response = await page.goto("/ssr");
5+
6+
expect(response?.request().redirectedFrom()?.url()).toMatch(/\/ssr$/);
7+
expect(response?.request().url()).toMatch(/\/ssr\/$/);
8+
});

0 commit comments

Comments
 (0)