{"version":3,"sources":["../../../src/uploads/image-resizing/createImageSizes.ts"],"sourcesContent":["import type { Sharp, SharpOptions } from 'sharp'\n\nimport { fileTypeFromBuffer } from 'file-type'\nimport fs from 'fs/promises'\n\nimport type { SanitizedCollectionConfig } from '../../collections/config/types.js'\nimport type { SharpDependency } from '../../config/types.js'\nimport type { PayloadRequest } from '../../types/index.js'\nimport type { WithMetadata } from '../optionallyAppendMetadata.js'\nimport type { FileSize, FileSizes, FileToSave, FocalPoint, ProbedImageSize } from '../types.js'\n\nimport { fileExists } from '../fileExists.js'\nimport { optionallyAppendMetadata } from '../optionallyAppendMetadata.js'\nimport { createImageSize } from './createImageSize.js'\nimport { extractHeightFromImage } from './extractHeightFromImage.js'\nimport { generateImageSizeFilename } from './generateImageSizeFilename.js'\nimport { getImageResizeAction } from './getImageResizeAction.js'\nimport { parseFilename } from './parseFilename.js'\nimport { sanitizeResizeConfig } from './sanitizeResizeConfig.js'\n\n/**\n * For the provided image sizes, handle the resizing and the transforms\n * (format, trim, etc.) of each requested image size and return the result object.\n * This only handles the image sizes. The transforms of the original image\n * are handled in {@link ./generateFileData.ts}.\n *\n * The image will be resized according to the provided\n * resize config. If no image sizes are requested, the resolved data will be empty.\n * For every image that does not need to be resized, a result object with `null`\n * parameters will be returned.\n *\n * @param resizeConfig - the resize config\n * @returns the result of the resize operation(s)\n */\n\ntype ResizeArgs = {\n config: SanitizedCollectionConfig\n dimensions: ProbedImageSize\n file: PayloadRequest['file']\n focalPoint?: FocalPoint\n mimeType: string\n req: PayloadRequest\n savedFilename: string\n sharp?: SharpDependency\n staticPath: string\n withMetadata?: WithMetadata\n}\n\n/** Result from resizing and transforming the requested image sizes */\ntype ImageSizesResult = {\n sizeData: FileSizes\n sizesToSave: FileToSave[]\n}\n\nexport async function createImageSizes({\n config,\n dimensions,\n file,\n focalPoint,\n mimeType,\n req,\n savedFilename,\n sharp,\n staticPath,\n withMetadata,\n}: ResizeArgs): Promise {\n const { imageSizes } = config.upload\n\n if (!imageSizes || !sharp) {\n return {\n sizeData: {},\n sizesToSave: [],\n }\n }\n\n // Determine if the file is animated\n const fileIsAnimatedType = ['image/avif', 'image/gif', 'image/webp'].includes(file!.mimetype)\n const sharpOptions: SharpOptions = {}\n\n if (fileIsAnimatedType) {\n sharpOptions.animated = true\n }\n\n const sharpBase: Sharp | undefined = sharp(\n file!.tempFilePath || file!.data,\n sharpOptions,\n ).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081\n const originalImageMeta = await sharpBase.metadata()\n\n let adjustedDimensions = { ...dimensions }\n\n // Images with an exif orientation of 5, 6, 7, or 8 are auto-rotated by sharp\n // Need to adjust the dimensions to match the original image\n if ([5, 6, 7, 8].includes(originalImageMeta.orientation!)) {\n adjustedDimensions = {\n ...dimensions,\n height: dimensions.width,\n width: dimensions.height,\n }\n }\n\n const resizeImageMeta = {\n height: originalImageMeta.height,\n width: originalImageMeta.width,\n }\n\n const sizes = {} as Record\n const imageSizeFiles: FileToSave[] = []\n\n await Promise.all(\n imageSizes.map(async (imageResizeConfig): Promise => {\n imageResizeConfig = sanitizeResizeConfig(imageResizeConfig)\n\n const resizeAction = getImageResizeAction({\n dimensions,\n hasFocalPoint: Boolean(focalPoint),\n imageResizeConfig,\n })\n\n if (resizeAction === 'omit') {\n sizes[imageResizeConfig.name] = createImageSize({})\n return\n }\n\n const imageToResize = sharpBase.clone()\n let resized = imageToResize\n\n if (resizeAction === 'resizeWithFocalPoint') {\n let { height: resizeHeight, width: resizeWidth } = imageResizeConfig\n\n const originalAspectRatio = adjustedDimensions.width / adjustedDimensions.height\n\n // Calculate resizeWidth based on original aspect ratio if it's undefined\n if (resizeHeight && !resizeWidth) {\n resizeWidth = Math.round(resizeHeight * originalAspectRatio)\n }\n\n // Calculate resizeHeight based on original aspect ratio if it's undefined\n if (resizeWidth && !resizeHeight) {\n resizeHeight = Math.round(resizeWidth / originalAspectRatio)\n }\n\n if (!resizeHeight) {\n resizeHeight = resizeImageMeta.height\n }\n if (!resizeWidth) {\n resizeWidth = resizeImageMeta.width\n }\n\n const resizeAspectRatio = resizeWidth! / resizeHeight!\n const prioritizeHeight = resizeAspectRatio < originalAspectRatio\n // Scales the image before extracting from it\n resized = imageToResize.resize({\n fastShrinkOnLoad: false,\n height: prioritizeHeight ? resizeHeight : undefined,\n width: prioritizeHeight ? undefined : resizeWidth,\n })\n\n const metadataAppendedFile = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata: withMetadata!,\n })\n\n // Must read from buffer, resized.metadata will return the original image metadata\n const { info } = await metadataAppendedFile.toBuffer({ resolveWithObject: true })\n\n resizeImageMeta.height = extractHeightFromImage({\n ...originalImageMeta,\n height: info.height,\n })\n resizeImageMeta.width = info.width\n\n const halfResizeX = resizeWidth! / 2\n const xFocalCenter = resizeImageMeta.width * (focalPoint!.x / 100)\n const calculatedRightPixelBound = xFocalCenter + halfResizeX\n let leftBound = xFocalCenter - halfResizeX\n\n // if the right bound is greater than the image width, adjust the left bound\n // keeping focus on the right\n if (calculatedRightPixelBound > resizeImageMeta.width) {\n leftBound = resizeImageMeta.width - resizeWidth!\n }\n\n // if the left bound is less than 0, adjust the left bound to 0\n // keeping the focus on the left\n if (leftBound < 0) {\n leftBound = 0\n }\n\n const halfResizeY = resizeHeight! / 2\n const yFocalCenter = resizeImageMeta.height * (focalPoint!.y / 100)\n const calculatedBottomPixelBound = yFocalCenter + halfResizeY\n let topBound = yFocalCenter - halfResizeY\n\n // if the bottom bound is greater than the image height, adjust the top bound\n // keeping the image as far right as possible\n if (calculatedBottomPixelBound > resizeImageMeta.height) {\n topBound = resizeImageMeta.height - resizeHeight!\n }\n\n // if the top bound is less than 0, adjust the top bound to 0\n // keeping the image focus near the top\n if (topBound < 0) {\n topBound = 0\n }\n\n resized = resized.extract({\n height: resizeHeight!,\n left: Math.floor(leftBound),\n top: Math.floor(topBound),\n width: resizeWidth!,\n })\n } else {\n resized = imageToResize.resize(imageResizeConfig)\n }\n\n if (imageResizeConfig.formatOptions) {\n resized = resized.toFormat(\n imageResizeConfig.formatOptions.format,\n imageResizeConfig.formatOptions.options,\n )\n }\n\n if (imageResizeConfig.trimOptions) {\n resized = resized.trim(imageResizeConfig.trimOptions)\n }\n\n const metadataAppendedFile = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata: withMetadata!,\n })\n\n const { data: bufferData, info: bufferInfo } = await metadataAppendedFile.toBuffer({\n resolveWithObject: true,\n })\n\n const { name, ext } = parseFilename(savedFilename)\n\n if (req.payloadUploadSizes) {\n req.payloadUploadSizes[imageResizeConfig.name] = bufferData\n }\n\n const mimeInfo = await fileTypeFromBuffer(bufferData)\n\n const imageNameWithDimensions = imageResizeConfig.generateImageName\n ? imageResizeConfig.generateImageName({\n extension: mimeInfo?.ext || ext,\n height: extractHeightFromImage({\n ...originalImageMeta,\n height: bufferInfo.height,\n }),\n originalName: name,\n sizeName: imageResizeConfig.name,\n width: bufferInfo.width,\n })\n : generateImageSizeFilename({\n extension: mimeInfo?.ext || ext,\n height: extractHeightFromImage({\n ...originalImageMeta,\n height: bufferInfo.height,\n }),\n outputImageName: name,\n width: bufferInfo.width,\n })\n\n const imagePath = `${staticPath}/${imageNameWithDimensions}`\n\n if (await fileExists(imagePath)) {\n try {\n await fs.unlink(imagePath)\n } catch {\n // Ignore unlink errors\n }\n }\n\n const { height, size, width } = bufferInfo\n sizes[imageResizeConfig.name] = createImageSize({\n filename: imageNameWithDimensions,\n filesize: size,\n height:\n fileIsAnimatedType && originalImageMeta.pages ? height / originalImageMeta.pages : height,\n mimeType: mimeInfo?.mime || mimeType,\n width,\n })\n\n imageSizeFiles.push({\n buffer: bufferData,\n path: imagePath,\n })\n }),\n )\n\n return {\n sizeData: sizes,\n sizesToSave: imageSizeFiles,\n }\n}\n"],"names":["fileTypeFromBuffer","fs","fileExists","optionallyAppendMetadata","createImageSize","extractHeightFromImage","generateImageSizeFilename","getImageResizeAction","parseFilename","sanitizeResizeConfig","createImageSizes","config","dimensions","file","focalPoint","mimeType","req","savedFilename","sharp","staticPath","withMetadata","imageSizes","upload","sizeData","sizesToSave","fileIsAnimatedType","includes","mimetype","sharpOptions","animated","sharpBase","tempFilePath","data","rotate","originalImageMeta","metadata","adjustedDimensions","orientation","height","width","resizeImageMeta","sizes","imageSizeFiles","Promise","all","map","imageResizeConfig","resizeAction","hasFocalPoint","Boolean","name","imageToResize","clone","resized","resizeHeight","resizeWidth","originalAspectRatio","Math","round","resizeAspectRatio","prioritizeHeight","resize","fastShrinkOnLoad","undefined","metadataAppendedFile","sharpFile","info","toBuffer","resolveWithObject","halfResizeX","xFocalCenter","x","calculatedRightPixelBound","leftBound","halfResizeY","yFocalCenter","y","calculatedBottomPixelBound","topBound","extract","left","floor","top","formatOptions","toFormat","format","options","trimOptions","trim","bufferData","bufferInfo","ext","payloadUploadSizes","mimeInfo","imageNameWithDimensions","generateImageName","extension","originalName","sizeName","outputImageName","imagePath","unlink","size","filename","filesize","pages","mime","push","buffer","path"],"mappings":"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"}