Skip to content

Commit 23b67ce

Browse files
authored
fix: Respect trailing slash config for _next/image route in worker
* fix: Respect trailing slash config for _next/image route in worker * add e2e * fix e2e:dev
1 parent 3be30a4 commit 23b67ce

File tree

10 files changed

+41
-6
lines changed

10 files changed

+41
-6
lines changed

.changeset/evil-rabbits-relax.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
fix: Respect trailing slash config for \_next/image route in worker

examples/playground15/app/api/signal/revalidate/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { revalidatePath } from "next/cache";
33
export const dynamic = "force-dynamic";
44

55
export async function GET() {
6-
revalidatePath("/signal");
6+
revalidatePath("/signal/");
77

88
return new Response("ok");
99
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Image from "next/image";
2+
3+
import tomineImg from "../../public/tomine.webp";
4+
5+
export default function Page() {
6+
return (
7+
<div>
8+
<Image src={tomineImg} alt="Picture of Tomine" />
9+
</div>
10+
);
11+
}

examples/playground15/e2e/base.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ test.describe("playground/base", () => {
4646
});
4747

4848
test("returns correct information about the request from a route handler", async ({ page, baseURL }) => {
49-
const res = await page.request.get("/api/request");
49+
const res = await page.request.get("/api/request/");
5050
// Next.js can fall back to `localhost:3000` or `n` if it doesn't get the host - neither of these are expected.
51-
const expectedURL = `${baseURL}/api/request`;
51+
const expectedURL = `${baseURL}/api/request/`;
5252
await expect(res.json()).resolves.toEqual({ nextUrl: expectedURL, url: expectedURL });
5353
});
5454

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test.describe("next/image with trailing slash", () => {
4+
test("next/image with trailing slash", async ({ page }) => {
5+
await page.goto("/image");
6+
await expect(page.getByAltText("Picture of Tomine")).toBeVisible();
7+
// The trailing slash should only be there if trailingSlash is enabled in next.config.ts
8+
expect(await page.getAttribute("img", "src")).toMatch(/^\/_next\/image\//);
9+
});
10+
});
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import { initOpenNextCloudflareForDev, getDeploymentId } from "@opennextjs/cloudflare";
2+
import { NextConfig } from "next";
23

34
initOpenNextCloudflareForDev();
45

5-
/** @type {import('next').NextConfig} */
6-
const nextConfig = {
6+
const nextConfig: NextConfig = {
77
typescript: { ignoreBuildErrors: true },
88
eslint: { ignoreDuringBuilds: true },
99
experimental: {
1010
// Generate source map to validate the fix for opennextjs/opennextjs-cloudflare#341
1111
serverSourceMaps: true,
1212
},
1313
deploymentId: getDeploymentId(),
14+
trailingSlash: true,
1415
};
1516

1617
export default nextConfig;
8.43 KB
Loading

packages/cloudflare/src/cli/build/open-next/compile-init.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export async function compileInit(options: BuildOptions, wranglerConfig: Unstabl
1717
const nextConfig = loadConfig(path.join(options.appBuildOutputPath, ".next"));
1818
const basePath = nextConfig.basePath ?? "";
1919
const deploymentId = nextConfig.deploymentId ?? "";
20+
const trailingSlash = nextConfig.trailingSlash ?? false;
2021

2122
await build({
2223
entryPoints: [initPath],
@@ -31,6 +32,7 @@ export async function compileInit(options: BuildOptions, wranglerConfig: Unstabl
3132
__NEXT_BASE_PATH__: JSON.stringify(basePath),
3233
__ASSETS_RUN_WORKER_FIRST__: JSON.stringify(wranglerConfig.assets?.run_worker_first ?? false),
3334
__DEPLOYMENT_ID__: JSON.stringify(deploymentId),
35+
__TRAILING_SLASH__: JSON.stringify(trailingSlash),
3436
},
3537
});
3638
}

packages/cloudflare/src/cli/templates/init.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ function initRuntime() {
9797
__BUILD_TIMESTAMP_MS__,
9898
__NEXT_BASE_PATH__,
9999
__ASSETS_RUN_WORKER_FIRST__,
100+
__TRAILING_SLASH__,
100101
// The external middleware will use the convertTo function of the `edge` converter
101102
// by default it will try to fetch the request, but since we are running everything in the same worker
102103
// we need to use the request as is.
@@ -155,4 +156,6 @@ declare global {
155156
var __ASSETS_RUN_WORKER_FIRST__: boolean | string[] | undefined;
156157
// Deployment ID
157158
var __DEPLOYMENT_ID__: string;
159+
// Next trailingSlash config
160+
var __TRAILING_SLASH__: boolean;
158161
}

packages/cloudflare/src/cli/templates/worker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ export default {
3939
}
4040

4141
// Fallback for the Next default image loader.
42-
if (url.pathname === `${globalThis.__NEXT_BASE_PATH__}/_next/image`) {
42+
if (
43+
url.pathname ===
44+
`${globalThis.__NEXT_BASE_PATH__}/_next/image${globalThis.__TRAILING_SLASH__ ? "/" : ""}`
45+
) {
4346
const imageUrl = url.searchParams.get("url") ?? "";
4447
return await fetchImage(env.ASSETS, imageUrl, ctx);
4548
}

0 commit comments

Comments
 (0)