diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index c845aebd54..bdaa21664f 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -65,6 +65,7 @@ export interface FunctionManifest { cache?: 'manual' } > + layers?: Array<{ name: `https://${string}/mod.ts`; flag: string }> import_map?: string } @@ -345,6 +346,7 @@ export const writeEdgeFunctions = async ({ }) => { const manifest: FunctionManifest = { functions: [], + layers: [], version: 1, } @@ -473,7 +475,12 @@ export const writeEdgeFunctions = async ({ manifest.functions.push({ function: 'ipx', name: 'next/image handler', - path: '/_next/image*', + path: nextConfig.images.path || '/_next/image', + }) + + manifest.layers.push({ + name: 'https://ipx-edge-function-layer.netlify.app/mod.ts', + flag: 'ipx-edge-function-layer-url', }) } else { console.log( diff --git a/packages/runtime/src/templates/edge/imageconfig.json b/packages/runtime/src/templates/edge/imageconfig.json new file mode 100644 index 0000000000..05af77d046 --- /dev/null +++ b/packages/runtime/src/templates/edge/imageconfig.json @@ -0,0 +1,3 @@ +{ + "formats": ["webp"] +} diff --git a/packages/runtime/src/templates/edge/ipx.ts b/packages/runtime/src/templates/edge/ipx.ts index 7c3f5c010e..bca8d5d8d5 100644 --- a/packages/runtime/src/templates/edge/ipx.ts +++ b/packages/runtime/src/templates/edge/ipx.ts @@ -1,73 +1,5 @@ -import { Accepts } from 'https://deno.land/x/accepts@2.1.1/mod.ts' -import type { Context } from 'https://edge.netlify.com' -// Available at build time -import imageconfig from './imageconfig.json' assert { type: 'json' } - -const defaultFormat = 'webp' - -interface ImageConfig extends Record { - formats?: string[] -} - -// Checks if a URL param is numeric -const isNumeric = (value: string | null) => Number(value).toString() === value - -/** - * Implement content negotiation for images - */ - -// deno-lint-ignore require-await -const handler = async (req: Request, context: Context) => { - const { searchParams } = new URL(req.url) - const accept = new Accepts(req.headers) - const { formats = [defaultFormat] } = imageconfig as ImageConfig - if (formats.length === 0) { - formats.push(defaultFormat) - } - let type = accept.types(formats) || defaultFormat - if (Array.isArray(type)) { - type = type[0] - } - - const source = searchParams.get('url') - const width = searchParams.get('w') - const quality = searchParams.get('q') ?? '75' +import { getHandler } from 'https://ipx-edge-function-layer.netlify.app/mod.ts' - const errors: Array = [] - - if (!source) { - errors.push('Missing "url" parameter') - } else if (!source.startsWith('http') && !source.startsWith('/')) { - errors.push('The "url" parameter must be a valid URL or path') - } - - if (!width) { - errors.push('Missing "w" parameter') - } else if (!isNumeric(width)) { - errors.push('Invalid "w" parameter') - } - - if (!isNumeric(quality)) { - errors.push('Invalid "q" parameter') - } - - if (!source || errors.length > 0) { - return new Response(`Invalid request: \n${errors.join('\n')}`, { - status: 400, - }) - } - - const modifiers = [`w_${width}`, `q_${quality}`] - - if (type) { - if (type.includes('/')) { - // If this is a mimetype, strip "image/" - type = type.split('/')[1] - } - modifiers.push(`f_${type}`) - } - const target = `/_ipx/${modifiers.join(',')}/${encodeURIComponent(source)}` - return context.rewrite(target) -} +import imageconfig from './imageconfig.json' assert { type: 'json' } -export default handler +export default getHandler({ formats: imageconfig?.formats })