2021-06-04 10:06:00 +02:00
|
|
|
import loaderUtils from 'next/dist/compiled/loader-utils'
|
2021-10-12 01:17:47 +02:00
|
|
|
import { resizeImage, getImageSize } from '../../../server/image-optimizer'
|
2021-06-04 10:06:00 +02:00
|
|
|
|
2021-06-10 20:51:35 +02:00
|
|
|
const BLUR_IMG_SIZE = 8
|
|
|
|
const BLUR_QUALITY = 70
|
2021-10-12 01:17:47 +02:00
|
|
|
const VALID_BLUR_EXT = ['jpeg', 'png', 'webp', 'avif'] // should match next/client/image.tsx
|
2021-06-04 10:06:00 +02:00
|
|
|
|
2021-07-09 17:30:16 +02:00
|
|
|
function nextImageLoader(content) {
|
|
|
|
const imageLoaderSpan = this.currentTraceSpan.traceChild('next-image-loader')
|
|
|
|
return imageLoaderSpan.traceAsyncFn(async () => {
|
2021-09-27 23:13:23 +02:00
|
|
|
const { isServer, isDev, assetPrefix, basePath } =
|
|
|
|
loaderUtils.getOptions(this)
|
2021-07-09 17:30:16 +02:00
|
|
|
const context = this.rootContext
|
|
|
|
const opts = { context, content }
|
|
|
|
const interpolatedName = loaderUtils.interpolateName(
|
|
|
|
this,
|
2021-10-22 15:25:54 +02:00
|
|
|
'/static/media/[name].[hash:8].[ext]',
|
2021-07-09 17:30:16 +02:00
|
|
|
opts
|
2021-06-04 10:06:00 +02:00
|
|
|
)
|
2021-09-24 01:26:51 +02:00
|
|
|
const outputPath = assetPrefix + '/_next' + interpolatedName
|
2021-06-04 10:06:00 +02:00
|
|
|
|
2021-07-09 17:30:16 +02:00
|
|
|
let extension = loaderUtils.interpolateName(this, '[ext]', opts)
|
|
|
|
if (extension === 'jpg') {
|
|
|
|
extension = 'jpeg'
|
|
|
|
}
|
|
|
|
|
|
|
|
const imageSizeSpan = imageLoaderSpan.traceChild('image-size-calculation')
|
2021-10-12 01:17:47 +02:00
|
|
|
const imageSize = await imageSizeSpan.traceAsyncFn(() =>
|
|
|
|
getImageSize(content, extension)
|
|
|
|
)
|
2021-07-09 17:30:16 +02:00
|
|
|
let blurDataURL
|
2021-07-10 22:27:14 +02:00
|
|
|
|
2021-07-09 17:30:16 +02:00
|
|
|
if (VALID_BLUR_EXT.includes(extension)) {
|
2021-07-10 22:27:14 +02:00
|
|
|
if (isDev) {
|
|
|
|
const prefix = 'http://localhost'
|
2021-09-27 23:13:23 +02:00
|
|
|
const url = new URL(`${basePath || ''}/_next/image`, prefix)
|
2021-09-24 01:26:51 +02:00
|
|
|
url.searchParams.set('url', outputPath)
|
2021-07-10 22:27:14 +02:00
|
|
|
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
|
2021-07-23 01:11:17 +02:00
|
|
|
const dimension =
|
|
|
|
imageSize.width >= imageSize.height ? 'width' : 'height'
|
2021-07-09 17:30:16 +02:00
|
|
|
|
2021-07-10 22:27:14 +02:00
|
|
|
const resizeImageSpan = imageLoaderSpan.traceChild('image-resize')
|
|
|
|
const resizedImage = await resizeImageSpan.traceAsyncFn(() =>
|
2021-07-23 01:11:17 +02:00
|
|
|
resizeImage(
|
|
|
|
content,
|
|
|
|
dimension,
|
|
|
|
BLUR_IMG_SIZE,
|
|
|
|
extension,
|
|
|
|
BLUR_QUALITY
|
|
|
|
)
|
2021-07-10 22:27:14 +02:00
|
|
|
)
|
|
|
|
const blurDataURLSpan = imageLoaderSpan.traceChild(
|
|
|
|
'image-base64-tostring'
|
|
|
|
)
|
|
|
|
blurDataURL = blurDataURLSpan.traceFn(
|
|
|
|
() =>
|
|
|
|
`data:image/${extension};base64,${resizedImage.toString('base64')}`
|
|
|
|
)
|
|
|
|
}
|
2021-07-09 17:30:16 +02:00
|
|
|
}
|
2021-06-04 10:06:00 +02:00
|
|
|
|
2021-07-09 17:30:16 +02:00
|
|
|
const stringifiedData = imageLoaderSpan
|
|
|
|
.traceChild('image-data-stringify')
|
|
|
|
.traceFn(() =>
|
|
|
|
JSON.stringify({
|
2021-07-10 22:27:14 +02:00
|
|
|
src: outputPath,
|
2021-07-09 17:30:16 +02:00
|
|
|
height: imageSize.height,
|
|
|
|
width: imageSize.width,
|
|
|
|
blurDataURL,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
if (!isServer) {
|
|
|
|
this.emitFile(interpolatedName, content, null)
|
|
|
|
}
|
|
|
|
|
|
|
|
return `export default ${stringifiedData};`
|
|
|
|
})
|
2021-06-04 10:06:00 +02:00
|
|
|
}
|
|
|
|
export const raw = true
|
|
|
|
export default nextImageLoader
|