Skip to content

Commit c4641e5

Browse files
styflegaojude
authored andcommitted
fix(next/image): use image-size pkg as additional format detector (#82538)
In a previous PR #82118 we added a fallback to `sharp().metadata()` when the magic number didn't detect the image format in order to improve detection. But `sharp` can be slow to read metadata, so we should avoid it if we can. So this PR uses the existing `image-size` package to detect the content type in JS and only then does it fallback to the native `sharp` code if necessary. No tests were added since this doesn't change the behavior, only performance.
1 parent b4923f0 commit c4641e5

File tree

4 files changed

+70
-5
lines changed

4 files changed

+70
-5
lines changed

packages/next/src/compiled/image-detector/detector.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/next/src/server/image-optimizer.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { IncomingMessage, ServerResponse } from 'http'
44
import { mediaType } from 'next/dist/compiled/@hapi/accept'
55
import contentDisposition from 'next/dist/compiled/content-disposition'
66
import imageSizeOf from 'next/dist/compiled/image-size'
7+
import { detector } from 'next/dist/compiled/image-detector/detector.js'
78
import isAnimated from 'next/dist/compiled/is-animated'
89
import { join } from 'path'
910
import nodeUrl, { type UrlWithParsedQuery } from 'url'
@@ -232,11 +233,21 @@ export async function detectContentType(
232233
return JP2
233234
}
234235

235-
const sharp = getSharp(null)
236-
const meta = await sharp(buffer)
237-
.metadata()
238-
.catch((_) => null)
239-
switch (meta?.format) {
236+
let format:
237+
| import('sharp').Metadata['format']
238+
| ReturnType<typeof detector>
239+
| undefined
240+
format = detector(buffer)
241+
242+
if (!format) {
243+
const sharp = getSharp(null)
244+
const meta = await sharp(buffer)
245+
.metadata()
246+
.catch((_) => null)
247+
format = meta?.format
248+
}
249+
250+
switch (format) {
240251
case 'avif':
241252
return AVIF
242253
case 'webp':
@@ -251,6 +262,7 @@ export async function detectContentType(
251262
case 'svg':
252263
return SVG
253264
case 'jxl':
265+
case 'jxl-stream':
254266
return JXL
255267
case 'jp2':
256268
return JP2
@@ -259,6 +271,12 @@ export async function detectContentType(
259271
return TIFF
260272
case 'pdf':
261273
return PDF
274+
case 'bmp':
275+
return BMP
276+
case 'ico':
277+
return ICO
278+
case 'icns':
279+
return ICNS
262280
case 'dcraw':
263281
case 'dz':
264282
case 'exr':
@@ -271,6 +289,13 @@ export async function detectContentType(
271289
case 'rad':
272290
case 'raw':
273291
case 'v':
292+
case 'cur':
293+
case 'dds':
294+
case 'j2c':
295+
case 'ktx':
296+
case 'pnm':
297+
case 'psd':
298+
case 'tga':
274299
case undefined:
275300
default:
276301
return null

packages/next/taskfile.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,17 @@ export async function ncc_image_size(task, opts) {
660660
.target('src/compiled/image-size')
661661
}
662662

663+
// eslint-disable-next-line camelcase
664+
externals['image-detector'] = 'next/dist/compiled/image-detector'
665+
export async function ncc_image_detector(task, opts) {
666+
// NOTE: remove this special compile step if the upstream PR lands
667+
// https://github.com/image-size/image-size/pull/451
668+
await task
669+
.source(relative(__dirname, require.resolve('image-size/dist/detector.js')))
670+
.ncc({ packageName: 'image-size', externals })
671+
.target('src/compiled/image-detector')
672+
}
673+
663674
// eslint-disable-next-line camelcase
664675
externals['@hapi/accept'] = 'next/dist/compiled/@hapi/accept'
665676
export async function ncc_hapi_accept(task, opts) {
@@ -2281,6 +2292,7 @@ export async function ncc(task, opts) {
22812292
'ncc_p_queue',
22822293
'ncc_raw_body',
22832294
'ncc_image_size',
2295+
'ncc_image_detector',
22842296
'ncc_hapi_accept',
22852297
'ncc_commander',
22862298
'ncc_node_anser',

packages/next/types/$$compiled.internal.d.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,33 @@ declare module 'next/dist/compiled/image-size' {
464464
export = m
465465
}
466466

467+
declare module 'next/dist/compiled/image-detector/detector.js' {
468+
export function detector(
469+
arr: Uint8Array
470+
):
471+
| 'bmp'
472+
| 'cur'
473+
| 'dds'
474+
| 'gif'
475+
| 'heif'
476+
| 'icns'
477+
| 'ico'
478+
| 'j2c'
479+
| 'jp2'
480+
| 'jpg'
481+
| 'jxl'
482+
| 'jxl-stream'
483+
| 'ktx'
484+
| 'png'
485+
| 'pnm'
486+
| 'psd'
487+
| 'svg'
488+
| 'tga'
489+
| 'tiff'
490+
| 'webp'
491+
| undefined
492+
}
493+
467494
declare module 'next/dist/compiled/@hapi/accept' {
468495
import m from '@hapi/accept'
469496
export = m

0 commit comments

Comments
 (0)