feat: add experiment for sharpjs cpu flags (#71733)

This PR adds two experimental flags to configure sharpjs settings that
may improve cpu for image optimization.
This commit is contained in:
Steven
2024-10-23 20:30:00 -04:00
committed by GitHub
parent dbc94523d5
commit 0dce6bb473
4 changed files with 63 additions and 4 deletions
@@ -314,6 +314,9 @@ export const configSchema: zod.ZodType<NextConfig> = z.lazy(() =>
forceSwcTransforms: z.boolean().optional(),
fullySpecified: z.boolean().optional(),
gzipSize: z.boolean().optional(),
imgOptConcurrency: z.number().int().optional().nullable(),
imgOptTimeoutInSeconds: z.number().int().optional(),
imgOptMaxInputPixels: z.number().int().optional(),
internal_disableSyncDynamicAPIWarnings: z.boolean().optional(),
isrFlushToDisk: z.boolean().optional(),
largePageDataBytes: z.number().optional(),
@@ -286,6 +286,9 @@ export interface ExperimentalConfig {
extensionAlias?: Record<string, any>
allowedRevalidateHeaderKeys?: string[]
fetchCacheKeyPrefix?: string
imgOptConcurrency?: number | null
imgOptTimeoutInSeconds?: number
imgOptMaxInputPixels?: number
optimisticClientCache?: boolean
/**
* @deprecated use config.expireTime instead
@@ -1112,6 +1115,9 @@ export const defaultConfig: NextConfig = {
(os.cpus() || { length: 1 }).length) - 1
),
memoryBasedWorkersCount: false,
imgOptConcurrency: null,
imgOptTimeoutInSeconds: 7,
imgOptMaxInputPixels: 268_402_689, // https://sharp.pixelplumbing.com/api-constructor#:~:text=%5Boptions.limitInputPixels%5D
isrFlushToDisk: true,
workerThreads: false,
proxyTimeout: undefined,
+23 -4
View File
@@ -47,7 +47,7 @@ const BLUR_QUALITY = 70 // should match `next-image-loader`
let _sharp: typeof import('sharp')
function getSharp() {
function getSharp(concurrency: number | null | undefined) {
if (_sharp) {
return _sharp
}
@@ -59,7 +59,7 @@ function getSharp() {
// https://sharp.pixelplumbing.com/api-utility#concurrency
const divisor = process.env.NODE_ENV === 'development' ? 4 : 2
_sharp.concurrency(
Math.floor(Math.max(_sharp.concurrency() / divisor, 1))
concurrency ?? Math.floor(Math.max(_sharp.concurrency() / divisor, 1))
)
}
} catch (e: unknown) {
@@ -512,15 +512,27 @@ export async function optimizeImage({
quality,
width,
height,
concurrency,
limitInputPixels,
timeoutInSeconds,
}: {
buffer: Buffer
contentType: string
quality: number
width: number
height?: number
concurrency?: number | null
limitInputPixels?: number
timeoutInSeconds?: number
}): Promise<Buffer> {
const sharp = getSharp()
const transformer = sharp(buffer).timeout({ seconds: 7 }).rotate()
const sharp = getSharp(concurrency)
const transformer = sharp(buffer, {
limitInputPixels,
})
.timeout({
seconds: timeoutInSeconds ?? 7,
})
.rotate()
if (height) {
transformer.resize(width, height)
@@ -631,6 +643,10 @@ export async function imageOptimizer(
'href' | 'width' | 'quality' | 'mimeType'
>,
nextConfig: {
experimental: Pick<
NextConfigComplete['experimental'],
'imgOptConcurrency' | 'imgOptMaxInputPixels' | 'imgOptTimeoutInSeconds'
>
images: Pick<
NextConfigComplete['images'],
'dangerouslyAllowSVG' | 'minimumCacheTTL'
@@ -735,6 +751,9 @@ export async function imageOptimizer(
contentType,
quality,
width,
concurrency: nextConfig.experimental.imgOptConcurrency,
limitInputPixels: nextConfig.experimental.imgOptMaxInputPixels,
timeoutInSeconds: nextConfig.experimental.imgOptTimeoutInSeconds,
})
if (optimizedBuffer) {
if (isDev && width <= BLUR_IMG_SIZE && quality === BLUR_QUALITY) {
@@ -734,6 +734,37 @@ describe('Image Optimizer', () => {
})
})
describe('experimental.imgOptMaxInputPixels in next.config.js', () => {
let app
let appPort
beforeAll(async () => {
nextConfig.replace(
'{ /* replaceme */ }',
JSON.stringify({
experimental: {
imgOptMaxInputPixels: 100,
},
})
)
await cleanImagesDir({ imagesDir })
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
nextConfig.restore()
})
it('should fallback to source image when input exceeds imgOptMaxInputPixels', async () => {
const size = 256 // defaults defined in lib/image-config.ts
const query = { w: size, q: 75, url: '/test.jpg' }
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/jpeg')
})
})
describe('External rewrite support with for serving static content in images', () => {
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',