Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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(404);
});
});
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