From 0355e5f63f87db489f36db8d814958cb4c2b828b Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 10 Mar 2022 06:02:13 -0800 Subject: [PATCH] Fix `next/image` when src is webp but browser doesnt support it (#35190) * Fix `next/image` when src is webp but browser doesnt support it * Exclude old sharp since it doesnt support AVIF --- packages/next/server/image-optimizer.ts | 7 +++- .../image-optimizer/app/public/test.avif | Bin 0 -> 1043 bytes .../image-optimizer/app/public/test.webp | Bin 0 -> 1018 bytes test/integration/image-optimizer/test/util.js | 38 ++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/integration/image-optimizer/app/public/test.avif create mode 100644 test/integration/image-optimizer/app/public/test.webp diff --git a/packages/next/server/image-optimizer.ts b/packages/next/server/image-optimizer.ts index 976bd8c29fd6..9adec2dd8a1d 100644 --- a/packages/next/server/image-optimizer.ts +++ b/packages/next/server/image-optimizer.ts @@ -419,7 +419,12 @@ export async function imageOptimizer( if (mimeType) { contentType = mimeType - } else if (upstreamType?.startsWith('image/') && getExtension(upstreamType)) { + } else if ( + upstreamType?.startsWith('image/') && + getExtension(upstreamType) && + upstreamType !== WEBP && + upstreamType !== AVIF + ) { contentType = upstreamType } else { contentType = JPEG diff --git a/test/integration/image-optimizer/app/public/test.avif b/test/integration/image-optimizer/app/public/test.avif new file mode 100644 index 0000000000000000000000000000000000000000..e2c8170a6833ebff1ae4e11b9abff1bee6f4bdeb GIT binary patch literal 1043 zcmZQzU{FXasVqn=%S>Yc0uY^>nP!-qnV9D5Xy^nK`jnemk_eIm0*#E6oFWL5fuSHX zxdg@r(K(q(Fk|=%GD~v7a*RMyE;A=T8N_p8U|HGYPp_`;RN zQI{$oYx^~h{C(Tl1>lMeCPK)4A9urjDmcF}Sym>Oq zuQvx$8Y^FDRZDZOWigVCZeiEkrgxdQRO`S}W#70Swno%#aR?0O_-fq@A z-td)488LdsvuqsOrJH7$mpA;W{kt{rw6ka2>P6SsmbvX-Aywq>sQL4q8{^?~mjnJg z20obKDzi<|bg6crjj7nL&JLcE+fO_VBRQ8@g@t_nB@%R!>CSEYYUf!`7hLns*tTd> zSWy-Gb7sq_oii%;?o^*IcRoF4R$A*kG5^U`OFsSW3(c9ZQt8M zob`Q+N!|6H`07RK4oh-e^;{*{7S<|t-8djNcUhnIfm{C!4jj3$VX>{8g{4pX*>^6T zT(>g5aGCZtO|AQ=c0|p&tk~r!&%xKXR-Whno43-~uSGn0M(wpNqM?_#CeJPGxvD(z z{6Zl!X%nq=i^}`Lg)}a;PG*i{bJ7WFX4p~rhiM^mP+0QgIn%Gt(YT`Pp7ZwbcCmX8 zWVc7J-??N}_n$*M6TdNLr07%x{9L-mp21sQc-6yijyltyR=BP#OPsFth@+or&m-O` ai*%+vXFpb>?I3lmL0LhJC*)VwLI(iE8=mC= literal 0 HcmV?d00001 diff --git a/test/integration/image-optimizer/app/public/test.webp b/test/integration/image-optimizer/app/public/test.webp new file mode 100644 index 0000000000000000000000000000000000000000..4b306cb0898cc93e83dd6f72566e13cbd1339eaa GIT binary patch literal 1018 zcmWIYbaVT}%)k)t>J$(bV4?5~$lhSgFqctl0^gPIDuB8qHv48H-EEbk|FBuIt(=eGzI!$@2M@ z#TTkpe-pQ_(pI`{*bt+CqK4g>WuD^-8`F(nA0CC+|Bv@@pq)xt!UWH|e=J2I&PYA^ zWlqk)0|9oTHfu`3u4+!vT_4xYrvJXmxxu=z`*eD{~o~@4h9AWfq9;% zqR$+A*q{}?M)}6!)9;$Um~P0+FB3LdlX7i)VN-;n!!5QNkGLvHafX9Y89~=x2}Fh~ z6{=Xw=Xf8owQpHQQhfe*DWBsR$IZ_)v2DNh4;YT~5_h_!EDZl17V~0$^#W^&%R;M9 z1TEjSC+p$CeWvFQFka)8vCV&;F*&+u0)t$j-NbM5|Eshts#iT?RJqI9Y@6$P?sJE; z(L-rLjkkxn89m~?Lr5i{_oxx zSN0*UK=A6}>)z2FvXA5!`;<=WJryi<{b@Bvy@XxL&U~+rMl7KV^LSQ#f56oEGG)cK z=rpw;j*oejQy>@ zO`}}>1>=?-D{WT%kUkpAsF(X!&bNaVHSjask0+H4Wp zPLZ)O`L|h_E7aw-7d((IxEm7R?EL(E9LSfq9oZdPx)1Qg`#-(@lK0TJ8-elG*X7c( z`yam$?FnSP{2}Dw-$B|+seK+ P+udJbdSnVPy+8l}7RKRp literal 0 HcmV?d00001 diff --git a/test/integration/image-optimizer/test/util.js b/test/integration/image-optimizer/test/util.js index 51c57a75552d..ee83f9e93b18 100644 --- a/test/integration/image-optimizer/test/util.js +++ b/test/integration/image-optimizer/test/util.js @@ -313,6 +313,44 @@ export function runTests(ctx) { ) }) + it('should downlevel webp format to jpeg 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: ctx.w, q: 74, url: '/test.webp' } + const opts = { headers: { accept } } + const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) + expect(res.status).toBe(200) + expect(res.headers.get('Content-Type')).toContain('image/jpeg') + expect(res.headers.get('Cache-Control')).toBe( + `public, max-age=0, must-revalidate` + ) + expect(res.headers.get('Vary')).toBe('Accept') + expect(res.headers.get('etag')).toBeTruthy() + expect(res.headers.get('Content-Disposition')).toBe( + `inline; filename="test.jpeg"` + ) + }) + + if (!ctx.isOutdatedSharp) { + it('should downlevel avif format to jpeg 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: ctx.w, q: 74, url: '/test.avif' } + const opts = { headers: { accept } } + const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) + expect(res.status).toBe(200) + expect(res.headers.get('Content-Type')).toContain('image/jpeg') + expect(res.headers.get('Cache-Control')).toBe( + `public, max-age=0, must-revalidate` + ) + expect(res.headers.get('Vary')).toBe('Accept') + expect(res.headers.get('etag')).toBeTruthy() + expect(res.headers.get('Content-Disposition')).toBe( + `inline; filename="test.jpeg"` + ) + }) + } + it('should fail when url is missing', async () => { const query = { w: ctx.w, q: 100 } const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, {})