Skip to content
28 changes: 23 additions & 5 deletions examples/playground14/e2e/cloudflare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,53 @@ test.describe("playground/cloudflare", () => {

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

test("400 when fetching an image disallowed by remotePatterns", async ({ page }) => {
const res = await page.request.get("/_next/image?url=https://avatars.githubusercontent.com/u/248817");
const res = await page.request.get(
"/_next/image?url=https://avatars.githubusercontent.com/u/248817&w=256&q=75"
);
expect(res.status()).toBe(400);
});
});

test.describe("localPatterns", () => {
test("fetch an image allowed by localPatterns", async ({ page }) => {
const res = await page.request.get("/_next/image?url=/snipp/snipp.webp?iscute=yes");
const res = await page.request.get("/_next/image?url=/snipp/snipp.webp?iscute=yes&w=256&q=75");
expect(res.status()).toBe(200);
expect(res.headers()).toMatchObject({ "content-type": "image/webp" });
});

test("400 when fetching an image disallowed by localPatterns with wrong query parameter", async ({
page,
}) => {
const res = await page.request.get("/_next/image?url=/snipp/snipp?iscute=no");
const res = await page.request.get("/_next/image?url=/snipp/snipp?iscute=no&w=256&q=75");
expect(res.status()).toBe(400);
});

test("400 when fetching an image disallowed by localPatterns without query parameter", async ({
page,
}) => {
const res = await page.request.get("/_next/image?url=/snipp/snipp");
const res = await page.request.get("/_next/image?url=/snipp/snipp&w=256&q=75");
expect(res.status()).toBe(400);
});
});

test.describe("imageSizes", () => {
test("400 when fetching an image with unsupported width value", async ({ page }) => {
const res = await page.request.get("/_next/image?url=/snipp/snipp.webp?iscute=yes&w=100&q=75");
expect(res.status()).toBe(400);
});
});

test.describe("qualities", () => {
test("400 when fetching an image with unsupported quality value", async ({ page }) => {
const res = await page.request.get("/_next/image?url=/snipp/snipp.webp?iscute=yes&w=256&q=100");
expect(res.status()).toBe(400);
});
});
Expand Down
3 changes: 3 additions & 0 deletions examples/playground14/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
"hello": "Hello World from the cloudflare context!",
"PROCESS_ENV_VAR": "process.env",
"NEXT_INC_CACHE_KV_PREFIX": "custom_prefix"
},
"images": {
"binding": "IMAGES"
}
}
3 changes: 3 additions & 0 deletions examples/playground15/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ const nextConfig: NextConfig = {
},
deploymentId: getDeploymentId(),
trailingSlash: true,
images: {
formats: ["image/avif", "image/webp"],
},
};

export default nextConfig;
5 changes: 4 additions & 1 deletion examples/playground15/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,8 @@
"database_id": "db_id",
"database_name": "db_name"
}
]
],
"images": {
"binding": "IMAGES"
}
}
3 changes: 3 additions & 0 deletions packages/cloudflare/src/api/cloudflare-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ declare global {
// Asset binding
ASSETS?: Fetcher;

// Images binding
IMAGES?: ImagesBinding;

// Environment to use when loading Next `.env` files
// Default to "production"
NEXTJS_ENV?: string;
Expand Down
26 changes: 25 additions & 1 deletion packages/cloudflare/src/cli/build/open-next/compile-images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,53 @@ export async function compileImages(options: BuildOptions) {
: {};

const __IMAGES_REMOTE_PATTERNS__ = JSON.stringify(imagesManifest?.images?.remotePatterns ?? []);
const __IMAGES_LOCAL_PATTERNS_DEFINED__ = JSON.stringify(
Array.isArray(imagesManifest?.images?.localPatterns)
);
const __IMAGES_LOCAL_PATTERNS__ = JSON.stringify(imagesManifest?.images?.localPatterns ?? []);
const __IMAGES_DEVICE_SIZES__ = JSON.stringify(imagesManifest?.images?.deviceSizes ?? defaultDeviceSizes);
const __IMAGES_IMAGE_SIZES__ = JSON.stringify(imagesManifest?.images?.imageSizes ?? defaultImageSizes);
const __IMAGES_QUALITIES__ = JSON.stringify(imagesManifest?.images?.qualities ?? defaultQualities);
const __IMAGES_FORMATS__ = JSON.stringify(imagesManifest?.images?.formats ?? defaultFormats);
const __IMAGES_MINIMUM_CACHE_TTL__ = JSON.stringify(
imagesManifest?.images?.minimumCacheTTL ?? defaultMinimumCacheTTL
);
const __IMAGES_ALLOW_SVG__ = JSON.stringify(Boolean(imagesManifest?.images?.dangerouslyAllowSVG));
const __IMAGES_CONTENT_SECURITY_POLICY__ = JSON.stringify(
imagesManifest?.images?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;"
);
const __IMAGES_CONTENT_DISPOSITION__ = JSON.stringify(
imagesManifest?.images?.contentDispositionType ?? "attachment"
);
const __IMAGES_MAX_REDIRECTS__ = JSON.stringify(imagesManifest?.images?.maximumRedirects ?? 3);

await build({
entryPoints: [imagesPath],
outdir: path.join(options.outputDir, "cloudflare"),
bundle: false,
bundle: true,
minify: false,
format: "esm",
target: "esnext",
platform: "node",
define: {
__IMAGES_REMOTE_PATTERNS__,
__IMAGES_LOCAL_PATTERNS_DEFINED__,
__IMAGES_LOCAL_PATTERNS__,
__IMAGES_DEVICE_SIZES__,
__IMAGES_IMAGE_SIZES__,
__IMAGES_QUALITIES__,
__IMAGES_FORMATS__,
__IMAGES_MINIMUM_CACHE_TTL__,
__IMAGES_ALLOW_SVG__,
__IMAGES_CONTENT_SECURITY_POLICY__,
__IMAGES_CONTENT_DISPOSITION__,
__IMAGES_MAX_REDIRECTS__,
},
});
}

const defaultDeviceSizes = [640, 750, 828, 1080, 1200, 1920, 2048, 3840];
const defaultImageSizes = [32, 48, 64, 96, 128, 256, 384];
const defaultQualities = [75];
const defaultFormats = ["image/webp"];
const defaultMinimumCacheTTL = 14400;
Loading
Loading