rsnext/packages/next/build/webpack/loaders/next-image-loader.js
Steven 73bffd6fd7
Fix next/future/image blur-up placeholder (#39785)
This PR is a follow up to PR #39190 so that we can dynamically set the `feComponentTransfer` when we know the image doesn't have transparency (at this time its just jpeg).

We also set the stdDeviation to 1 and the viewbox to the placeholder's width/height to avoid any rounding issues.

Finally, we also fix the conversion from `objectPosition` to `backgroundPosition` because they have different default values according to the spec.
2022-08-23 18:56:52 -05:00

91 lines
2.9 KiB
JavaScript

import loaderUtils from 'next/dist/compiled/loader-utils3'
import { resizeImage, getImageSize } from '../../../server/image-optimizer'
const BLUR_IMG_SIZE = 8
const BLUR_QUALITY = 70
const VALID_BLUR_EXT = ['jpeg', 'png', 'webp', 'avif'] // should match next/client/image.tsx
function nextImageLoader(content) {
const imageLoaderSpan = this.currentTraceSpan.traceChild('next-image-loader')
return imageLoaderSpan.traceAsyncFn(async () => {
const { isServer, isDev, assetPrefix, basePath } = this.getOptions()
const context = this.rootContext
const opts = { context, content }
const interpolatedName = loaderUtils.interpolateName(
this,
'/static/media/[name].[hash:8].[ext]',
opts
)
const outputPath = assetPrefix + '/_next' + interpolatedName
let extension = loaderUtils.interpolateName(this, '[ext]', opts)
if (extension === 'jpg') {
extension = 'jpeg'
}
const imageSizeSpan = imageLoaderSpan.traceChild('image-size-calculation')
const imageSize = await imageSizeSpan.traceAsyncFn(() =>
getImageSize(content, extension)
)
let blurDataURL
let blurWidth
let blurHeight
if (VALID_BLUR_EXT.includes(extension)) {
if (isDev) {
const prefix = 'http://localhost'
const url = new URL(`${basePath || ''}/_next/image`, prefix)
url.searchParams.set('url', outputPath)
url.searchParams.set('w', BLUR_IMG_SIZE)
url.searchParams.set('q', BLUR_QUALITY)
blurDataURL = url.href.slice(prefix.length)
} else {
// Shrink the image's largest dimension
if (imageSize.width >= imageSize.height) {
blurWidth = BLUR_IMG_SIZE
blurHeight = Math.round(
(imageSize.height / imageSize.width) * BLUR_IMG_SIZE
)
} else {
blurWidth = Math.round(
(imageSize.width / imageSize.height) * BLUR_IMG_SIZE
)
blurHeight = BLUR_IMG_SIZE
}
const resizeImageSpan = imageLoaderSpan.traceChild('image-resize')
const resizedImage = await resizeImageSpan.traceAsyncFn(() =>
resizeImage(content, blurWidth, blurHeight, extension, BLUR_QUALITY)
)
const blurDataURLSpan = imageLoaderSpan.traceChild(
'image-base64-tostring'
)
blurDataURL = blurDataURLSpan.traceFn(
() =>
`data:image/${extension};base64,${resizedImage.toString('base64')}`
)
}
}
const stringifiedData = imageLoaderSpan
.traceChild('image-data-stringify')
.traceFn(() =>
JSON.stringify({
src: outputPath,
height: imageSize.height,
width: imageSize.width,
blurDataURL,
blurWidth,
blurHeight,
})
)
if (!isServer) {
this.emitFile(interpolatedName, content, null)
}
return `export default ${stringifiedData};`
})
}
export const raw = true
export default nextImageLoader