Fix image format for Safari and old browsers (#18646)
Older versions of Safari (and other browsers) do not support webp format so we were incorrectly falling back to png. This PR fixes #18509 so that we fallback to the original image format if a modern format like webp was not explicitly provided in the Accept header. Tests were added to ensure that the Accept header for Safari, Firefox, and Chrome work properly.
This commit is contained in:
parent
5c97b97755
commit
377aa4b650
2 changed files with 33 additions and 15 deletions
|
@ -18,8 +18,8 @@ const PNG = 'image/png'
|
|||
const JPEG = 'image/jpeg'
|
||||
const GIF = 'image/gif'
|
||||
const SVG = 'image/svg+xml'
|
||||
const MIME_TYPES = [/* AVIF, */ WEBP, PNG, JPEG]
|
||||
const CACHE_VERSION = 1
|
||||
const MODERN_TYPES = [/* AVIF, */ WEBP]
|
||||
const ANIMATABLE_TYPES = [WEBP, PNG, GIF]
|
||||
const VECTOR_TYPES = [SVG]
|
||||
|
||||
|
@ -49,7 +49,7 @@ export async function imageOptimizer(
|
|||
|
||||
const { headers } = req
|
||||
const { url, w, q } = parsedUrl.query
|
||||
const mimeType = mediaType(headers.accept, MIME_TYPES) || ''
|
||||
const mimeType = getSupportedMimeType(MODERN_TYPES, headers.accept)
|
||||
let href: string
|
||||
|
||||
if (!url) {
|
||||
|
@ -294,6 +294,11 @@ export async function imageOptimizer(
|
|||
return { finished: true }
|
||||
}
|
||||
|
||||
function getSupportedMimeType(options: string[], accept = ''): string {
|
||||
const mimeType = mediaType(accept, options)
|
||||
return accept.includes(mimeType) ? mimeType : ''
|
||||
}
|
||||
|
||||
function getHash(items: (string | number | undefined)[]) {
|
||||
const hash = createHash('sha256')
|
||||
for (let item of items) {
|
||||
|
|
|
@ -88,6 +88,26 @@ function runTests({ w, isDev, domains }) {
|
|||
expect(actual).toMatch(expected)
|
||||
})
|
||||
|
||||
it('should maintain jpg format for old Safari', async () => {
|
||||
const accept =
|
||||
'image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5'
|
||||
const query = { w, q: 90, url: '/test.jpg' }
|
||||
const opts = { headers: { accept } }
|
||||
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('Content-Type')).toContain('image/jpeg')
|
||||
})
|
||||
|
||||
it('should maintain png format for old Safari', async () => {
|
||||
const accept =
|
||||
'image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5'
|
||||
const query = { w, q: 75, url: '/test.png' }
|
||||
const opts = { headers: { accept } }
|
||||
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('Content-Type')).toContain('image/png')
|
||||
})
|
||||
|
||||
it('should fail when url is missing', async () => {
|
||||
const query = { w, q: 100 }
|
||||
const res = await fetchViaHTTP(appPort, '/_next/image', query, {})
|
||||
|
@ -173,24 +193,15 @@ function runTests({ w, isDev, domains }) {
|
|||
)
|
||||
})
|
||||
|
||||
it('should resize relative url and webp accept header', async () => {
|
||||
it('should resize relative url and webp Firefox accept header', async () => {
|
||||
const query = { url: '/test.png', w, q: 80 }
|
||||
const opts = { headers: { accept: 'image/webp' } }
|
||||
const opts = { headers: { accept: 'image/webp,*/*' } }
|
||||
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('Content-Type')).toBe('image/webp')
|
||||
await expectWidth(res, w)
|
||||
})
|
||||
|
||||
it('should resize relative url and jpeg accept header', async () => {
|
||||
const query = { url: '/test.png', w, q: 80 }
|
||||
const opts = { headers: { accept: 'image/jpeg' } }
|
||||
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('Content-Type')).toBe('image/jpeg')
|
||||
await expectWidth(res, w)
|
||||
})
|
||||
|
||||
it('should resize relative url and png accept header', async () => {
|
||||
const query = { url: '/test.png', w, q: 80 }
|
||||
const opts = { headers: { accept: 'image/png' } }
|
||||
|
@ -227,9 +238,11 @@ function runTests({ w, isDev, domains }) {
|
|||
await expectWidth(res, w)
|
||||
})
|
||||
|
||||
it('should resize relative url and wildcard accept header as webp', async () => {
|
||||
it('should resize relative url and Chrome accept header as webp', async () => {
|
||||
const query = { url: '/test.png', w, q: 80 }
|
||||
const opts = { headers: { accept: 'image/*' } }
|
||||
const opts = {
|
||||
headers: { accept: 'image/avif,image/webp,image/apng,image/*,*/*;q=0.8' },
|
||||
}
|
||||
const res = await fetchViaHTTP(appPort, '/_next/image', query, opts)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.headers.get('Content-Type')).toBe('image/webp')
|
||||
|
|
Loading…
Reference in a new issue