Skip to content

Commit 56c7828

Browse files
feat: use edge function layer for ipx (#1880)
Co-authored-by: Nick Taylor <[email protected]>
1 parent a630ab4 commit 56c7828

File tree

3 files changed

+14
-72
lines changed

3 files changed

+14
-72
lines changed

packages/runtime/src/helpers/edge.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface FunctionManifest {
6565
cache?: 'manual'
6666
}
6767
>
68+
layers?: Array<{ name: `https://${string}/mod.ts`; flag: string }>
6869
import_map?: string
6970
}
7071

@@ -345,6 +346,7 @@ export const writeEdgeFunctions = async ({
345346
}) => {
346347
const manifest: FunctionManifest = {
347348
functions: [],
349+
layers: [],
348350
version: 1,
349351
}
350352

@@ -473,7 +475,12 @@ export const writeEdgeFunctions = async ({
473475
manifest.functions.push({
474476
function: 'ipx',
475477
name: 'next/image handler',
476-
path: '/_next/image*',
478+
path: nextConfig.images.path || '/_next/image',
479+
})
480+
481+
manifest.layers.push({
482+
name: 'https://ipx-edge-function-layer.netlify.app/mod.ts',
483+
flag: 'ipx-edge-function-layer-url',
477484
})
478485
} else {
479486
console.log(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"formats": ["webp"]
3+
}
+3-71
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,5 @@
1-
import { Accepts } from 'https://deno.land/x/[email protected]/mod.ts'
2-
import type { Context } from 'https://edge.netlify.com'
3-
// Available at build time
4-
import imageconfig from './imageconfig.json' assert { type: 'json' }
5-
6-
const defaultFormat = 'webp'
7-
8-
interface ImageConfig extends Record<string, unknown> {
9-
formats?: string[]
10-
}
11-
12-
// Checks if a URL param is numeric
13-
const isNumeric = (value: string | null) => Number(value).toString() === value
14-
15-
/**
16-
* Implement content negotiation for images
17-
*/
18-
19-
// deno-lint-ignore require-await
20-
const handler = async (req: Request, context: Context) => {
21-
const { searchParams } = new URL(req.url)
22-
const accept = new Accepts(req.headers)
23-
const { formats = [defaultFormat] } = imageconfig as ImageConfig
24-
if (formats.length === 0) {
25-
formats.push(defaultFormat)
26-
}
27-
let type = accept.types(formats) || defaultFormat
28-
if (Array.isArray(type)) {
29-
type = type[0]
30-
}
31-
32-
const source = searchParams.get('url')
33-
const width = searchParams.get('w')
34-
const quality = searchParams.get('q') ?? '75'
1+
import { getHandler } from 'https://ipx-edge-function-layer.netlify.app/mod.ts'
352

36-
const errors: Array<string> = []
37-
38-
if (!source) {
39-
errors.push('Missing "url" parameter')
40-
} else if (!source.startsWith('http') && !source.startsWith('/')) {
41-
errors.push('The "url" parameter must be a valid URL or path')
42-
}
43-
44-
if (!width) {
45-
errors.push('Missing "w" parameter')
46-
} else if (!isNumeric(width)) {
47-
errors.push('Invalid "w" parameter')
48-
}
49-
50-
if (!isNumeric(quality)) {
51-
errors.push('Invalid "q" parameter')
52-
}
53-
54-
if (!source || errors.length > 0) {
55-
return new Response(`Invalid request: \n${errors.join('\n')}`, {
56-
status: 400,
57-
})
58-
}
59-
60-
const modifiers = [`w_${width}`, `q_${quality}`]
61-
62-
if (type) {
63-
if (type.includes('/')) {
64-
// If this is a mimetype, strip "image/"
65-
type = type.split('/')[1]
66-
}
67-
modifiers.push(`f_${type}`)
68-
}
69-
const target = `/_ipx/${modifiers.join(',')}/${encodeURIComponent(source)}`
70-
return context.rewrite(target)
71-
}
3+
import imageconfig from './imageconfig.json' assert { type: 'json' }
724

73-
export default handler
5+
export default getHandler({ formats: imageconfig?.formats })

0 commit comments

Comments
 (0)