Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-readers-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/cloudflare": patch
---

add tests for remote patterns
11 changes: 11 additions & 0 deletions examples/playground14/e2e/cloudflare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,15 @@ test.describe("playground/cloudflare", () => {
const res = await page.request.get("/api/env");
await expect(res.json()).resolves.toEqual(expect.objectContaining({ PROCESS_ENV_VAR: "process.env" }));
});

test("fetch an image allowed by remotePatterns", async ({ page }) => {
const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248818");
expect(res.status()).toBe(200);
expect(res.headers()).toMatchObject({ "content-type": "image/jpeg" });
});

test("404 when fetching an image disallowed by remotePatterns", async ({ page }) => {
const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248817");
expect(res.status()).toBe(400);
});
});
9 changes: 9 additions & 0 deletions examples/playground14/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ const nextConfig = {
serverSourceMaps: true,
instrumentationHook: true,
},
images: {
remotePatterns: [
{
protocol: "https",
hostname: "avatars.githubusercontent.com",
pathname: "/u/248818",
},
],
},
};

export default nextConfig;
7 changes: 5 additions & 2 deletions packages/cloudflare/src/cli/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { compileCache } from "@opennextjs/aws/build/compileCache.js";
import { createCacheAssets, createStaticAssets } from "@opennextjs/aws/build/createAssets.js";
import { createMiddleware } from "@opennextjs/aws/build/createMiddleware.js";
import * as buildHelper from "@opennextjs/aws/build/helper.js";
import { BuildOptions } from "@opennextjs/aws/build/helper.js";
import { printHeader } from "@opennextjs/aws/build/utils.js";
import logger from "@opennextjs/aws/logger.js";

Expand All @@ -12,6 +11,7 @@ import type { ProjectOptions } from "../project-options.js";
import { bundleServer } from "./bundle-server.js";
import { compileCacheAssetsManifestSqlFile } from "./open-next/compile-cache-assets-manifest.js";
import { compileEnvFiles } from "./open-next/compile-env-files.js";
import { compileImages } from "./open-next/compile-images.js";
import { compileInit } from "./open-next/compile-init.js";
import { compileDurableObjects } from "./open-next/compileDurableObjects.js";
import { createServerBundle } from "./open-next/createServerBundle.js";
Expand All @@ -28,7 +28,7 @@ import { getVersion } from "./utils/version.js";
* @param projectOpts The options for the project
*/
export async function build(
options: BuildOptions,
options: buildHelper.BuildOptions,
config: OpenNextConfig,
projectOpts: ProjectOptions
): Promise<void> {
Expand Down Expand Up @@ -67,6 +67,9 @@ export async function build(
// Compile workerd init
compileInit(options);

// Compile image helpers
compileImages(options);

// Compile middleware
await createMiddleware(options, { forceOnlyBuildOnce: true });

Expand Down
34 changes: 34 additions & 0 deletions packages/cloudflare/src/cli/build/open-next/compile-images.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
import { build } from "esbuild";

/**
* Compiles the initialization code for the workerd runtime
*/
export async function compileImages(options: BuildOptions) {
const currentDir = path.join(path.dirname(fileURLToPath(import.meta.url)));
const templatesDir = path.join(currentDir, "../../templates");
const imagesPath = path.join(templatesDir, "images.js");

const imagesManifestPath = path.join(options.appBuildOutputPath, ".next/images-manifest.json");
const imagesManifest = fs.existsSync(imagesManifestPath)
? JSON.parse(fs.readFileSync(imagesManifestPath, { encoding: "utf-8" }))
: {};

await build({
entryPoints: [imagesPath],
outdir: path.join(options.outputDir, "cloudflare"),
bundle: false,
minify: false,
format: "esm",
target: "esnext",
platform: "node",
define: {
__IMAGES_REMOTE_PATTERNS__: JSON.stringify(imagesManifest?.images?.remotePatterns ?? []),
__IMAGES_LOCAL_PATTERNS__: JSON.stringify(imagesManifest?.images?.localPatterns ?? []),
},
});
}
25 changes: 0 additions & 25 deletions packages/cloudflare/src/cli/build/open-next/compile-init.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import path from "node:path";
import { fileURLToPath } from "node:url";

import { loadConfig } from "@opennextjs/aws/adapters/config/util.js";
import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
import { build } from "esbuild";
import pm from "picomatch";

/**
* Compiles the initialization code for the workerd runtime
Expand All @@ -18,27 +16,6 @@ export async function compileInit(options: BuildOptions) {
const nextConfig = loadConfig(path.join(options.appBuildOutputPath, ".next"));
const basePath = nextConfig.basePath ?? "";

// https://github.com/vercel/next.js/blob/d76f0b13/packages/next/src/build/index.ts#L573
const nextRemotePatterns = nextConfig.images?.remotePatterns ?? [];

const remotePatterns = nextRemotePatterns.map((p) => ({
protocol: p.protocol,
hostname: p.hostname ? pm.makeRe(p.hostname).source : undefined,
port: p.port,
pathname: pm.makeRe(p.pathname ?? "**", { dot: true }).source,
// search is canary only as of June 2025
search: (p as any).search,
}));

// Local patterns are only in canary as of June 2025
const nextLocalPatterns = (nextConfig.images as any)?.localPatterns ?? [];

// https://github.com/vercel/next.js/blob/d76f0b13/packages/next/src/build/index.ts#L573
const localPatterns = nextLocalPatterns.map((p: any) => ({
pathname: pm.makeRe(p.pathname ?? "**", { dot: true }).source,
search: p.search,
}));

await build({
entryPoints: [initPath],
outdir: path.join(options.outputDir, "cloudflare"),
Expand All @@ -50,8 +27,6 @@ export async function compileInit(options: BuildOptions) {
define: {
__BUILD_TIMESTAMP_MS__: JSON.stringify(Date.now()),
__NEXT_BASE_PATH__: JSON.stringify(basePath),
__IMAGES_REMOTE_PATTERNS__: JSON.stringify(remotePatterns),
__IMAGES_LOCAL_PATTERNS__: JSON.stringify(localPatterns),
},
});
}
Loading