diff --git a/package.json b/package.json index 9f453461d..a747901be 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "read-stream": "^2.1.1", "rimraf": "3.0.2", "semver": "^7.5.4", + "sharp": "0.32.6", "shelljs": "0.8.5", "simple-git": "^3.20.0", "slash": "3.0.0", diff --git a/packages/payload/package.json b/packages/payload/package.json index f25214a44..8435cec09 100644 --- a/packages/payload/package.json +++ b/packages/payload/package.json @@ -65,8 +65,7 @@ "probe-image-size": "6.0.0", "sanitize-filename": "1.6.3", "scheduler": "0.23.0", - "scmp": "2.1.0", - "sharp": "0.32.6" + "scmp": "2.1.0" }, "devDependencies": { "@monaco-editor/react": "4.5.1", @@ -113,7 +112,8 @@ "release-it": "17.1.1", "rimraf": "3.0.2", "serve-static": "1.15.0", - "ts-essentials": "7.0.3" + "ts-essentials": "7.0.3", + "sharp": "0.32.6" }, "engines": { "node": ">=18.17.0" diff --git a/packages/payload/src/config/schema.ts b/packages/payload/src/config/schema.ts index 6a74dd0fb..2ed972a83 100644 --- a/packages/payload/src/config/schema.ts +++ b/packages/payload/src/config/schema.ts @@ -177,6 +177,7 @@ export default joi.object({ return value }), + sharp: joi.any(), telemetry: joi.boolean(), typescript: joi.object({ declare: joi.boolean(), diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index e40b4dfde..e550463bd 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -5,6 +5,7 @@ import type { Transporter } from 'nodemailer' import type SMTPConnection from 'nodemailer/lib/smtp-connection' import type { DestinationStream, LoggerOptions } from 'pino' import type React from 'react' +import type { default as sharp } from 'sharp' import type { DeepRequired } from 'ts-essentials' import type { Payload } from '..' @@ -359,6 +360,23 @@ export type LocalizationConfig = Prettify< LocalizationConfigWithLabels | LocalizationConfigWithNoLabels > +export type SharpDependency = ( + input?: + | ArrayBuffer + | Buffer + | Float32Array + | Float64Array + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array + | string, + options?: sharp.SharpOptions, +) => sharp.Sharp + /** * This is the central configuration * @@ -635,6 +653,11 @@ export type Config = { * @see https://payloadcms.com/docs/configuration/overview#options */ serverURL?: string + /** + * Pass in a local copy of Sharp if you'd like to use it. + * + */ + sharp?: SharpDependency /** Send anonymous telemetry data about general usage. */ telemetry?: boolean /** Control how typescript interfaces are generated from your collections. */ diff --git a/packages/payload/src/uploads/cropImage.ts b/packages/payload/src/uploads/cropImage.ts index 62627b8a7..3f08a58f6 100644 --- a/packages/payload/src/uploads/cropImage.ts +++ b/packages/payload/src/uploads/cropImage.ts @@ -1,10 +1,8 @@ -import sharp from 'sharp' - export const percentToPixel = (value, dimension) => { return Math.floor((parseFloat(value) / 100) * dimension) } -export default async function cropImage({ cropData, dimensions, file }) { +export default async function cropImage({ cropData, dimensions, file, sharp }) { try { const { height, width, x, y } = cropData diff --git a/packages/payload/src/uploads/generateFileData.ts b/packages/payload/src/uploads/generateFileData.ts index 8e763912f..ae0c4b685 100644 --- a/packages/payload/src/uploads/generateFileData.ts +++ b/packages/payload/src/uploads/generateFileData.ts @@ -4,7 +4,6 @@ import { fromBuffer } from 'file-type' import fs from 'fs' import mkdirp from 'mkdirp' import sanitize from 'sanitize-filename' -import sharp from 'sharp' import type { Collection } from '../collections/config/types' import type { SanitizedConfig } from '../config/types' @@ -49,6 +48,8 @@ export const generateFileData = async ({ } } + const { sharp } = req.payload.config + let file = req.file const { searchParams } = req @@ -113,7 +114,7 @@ export const generateFileData = async ({ if (fileIsAnimated) sharpOptions.animated = true - if (fileHasAdjustments) { + if (fileHasAdjustments && sharp) { if (file.tempFilePath) { sharpFile = sharp(file.tempFilePath, sharpOptions).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081 } else { @@ -180,8 +181,8 @@ export const generateFileData = async ({ fileData.filename = fsSafeName let fileForResize = file - if (cropData) { - const { data: croppedImage, info } = await cropImage({ cropData, dimensions, file }) + if (cropData && sharp) { + const { data: croppedImage, info } = await cropImage({ cropData, dimensions, file, sharp }) filesToSave.push({ buffer: croppedImage, @@ -223,7 +224,7 @@ export const generateFileData = async ({ } } - if (Array.isArray(imageSizes) && fileSupportsResize) { + if (Array.isArray(imageSizes) && fileSupportsResize && sharp) { req.payloadUploadSizes = {} const { sizeData, sizesToSave } = await resizeAndTransformImageSizes({ config: collectionConfig, @@ -238,6 +239,7 @@ export const generateFileData = async ({ mimeType: fileData.mimeType, req, savedFilename: fsSafeName || file.name, + sharp, staticPath, }) diff --git a/packages/payload/src/uploads/imageResizer.ts b/packages/payload/src/uploads/imageResizer.ts index 5bb061a58..de8ba2c55 100644 --- a/packages/payload/src/uploads/imageResizer.ts +++ b/packages/payload/src/uploads/imageResizer.ts @@ -1,11 +1,12 @@ -import type { OutputInfo } from 'sharp' +import type { OutputInfo, default as Sharp } from 'sharp' +import type sharp from 'sharp' import { fromBuffer } from 'file-type' import fs from 'fs' import sanitize from 'sanitize-filename' -import sharp from 'sharp' import type { SanitizedCollectionConfig } from '../collections/config/types' +import type { SharpDependency } from '../exports/config' import type { UploadEdits } from '../exports/types' import type { CustomPayloadRequest, PayloadRequest } from '../types' import type { FileSize, FileSizes, FileToSave, ImageSize, ProbedImageSize } from './types' @@ -24,6 +25,7 @@ type ResizeArgs = { } } savedFilename: string + sharp: SharpDependency staticPath: string } @@ -213,6 +215,7 @@ export default async function resizeAndTransformImageSizes({ mimeType, req, savedFilename, + sharp, staticPath, }: ResizeArgs): Promise { const { imageSizes } = config.upload diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5854dbae1..0c33b1488 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,6 +230,9 @@ importers: semver: specifier: ^7.5.4 version: 7.6.0 + sharp: + specifier: 0.32.6 + version: 0.32.6 shelljs: specifier: 0.8.5 version: 0.8.5 @@ -716,9 +719,6 @@ importers: scmp: specifier: 2.1.0 version: 2.1.0 - sharp: - specifier: 0.32.6 - version: 0.32.6 devDependencies: '@monaco-editor/react': specifier: 4.5.1 @@ -852,6 +852,9 @@ importers: serve-static: specifier: 1.15.0 version: 1.15.0 + sharp: + specifier: 0.32.6 + version: 0.32.6 ts-essentials: specifier: 7.0.3 version: 7.0.3(typescript@5.2.2) @@ -7496,6 +7499,7 @@ packages: /b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + dev: true /babel-jest@29.7.0(@babel/core@7.24.0): resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} @@ -7579,6 +7583,7 @@ packages: /bare-events@2.2.0: resolution: {integrity: sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==} requiresBuild: true + dev: true optional: true /bare-fs@2.2.1: @@ -7589,13 +7594,13 @@ packages: bare-os: 2.2.0 bare-path: 2.1.0 streamx: 2.16.1 - dev: false + dev: true optional: true /bare-os@2.2.0: resolution: {integrity: sha512-hD0rOPfYWOMpVirTACt4/nK8mC55La12K5fY1ij8HAdfQakD62M+H4o4tpfKzVGLgRDTuk3vjA4GqGXXCeFbag==} requiresBuild: true - dev: false + dev: true optional: true /bare-path@2.1.0: @@ -7603,7 +7608,7 @@ packages: requiresBuild: true dependencies: bare-os: 2.2.0 - dev: false + dev: true optional: true /base64-js@1.5.1: @@ -7955,7 +7960,7 @@ packages: /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - dev: false + dev: true /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} @@ -8096,7 +8101,7 @@ packages: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false + dev: true /color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} @@ -8104,7 +8109,7 @@ packages: dependencies: color-convert: 2.0.1 color-string: 1.9.1 - dev: false + dev: true /colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} @@ -8875,6 +8880,7 @@ packages: engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 + dev: true /dedent@1.5.1: resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} @@ -8933,6 +8939,7 @@ packages: /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} + dev: true /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -10113,7 +10120,7 @@ packages: /expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - dev: false + dev: true /expand-tilde@2.0.2: resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} @@ -10223,6 +10230,7 @@ packages: /fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: true /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} @@ -10537,7 +10545,7 @@ packages: /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: false + dev: true /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} @@ -10740,7 +10748,7 @@ packages: /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - dev: false + dev: true /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -11402,7 +11410,7 @@ packages: /is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + dev: true /is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} @@ -13218,6 +13226,7 @@ packages: /mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + dev: true /mimic-response@4.0.0: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} @@ -13281,7 +13290,7 @@ packages: /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - dev: false + dev: true /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} @@ -13450,7 +13459,7 @@ packages: /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - dev: false + dev: true /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -13587,11 +13596,11 @@ packages: engines: {node: '>=10'} dependencies: semver: 7.6.0 - dev: false + dev: true /node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} - dev: false + dev: true /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} @@ -15130,7 +15139,7 @@ packages: simple-get: 4.0.1 tar-fs: 2.1.1 tunnel-agent: 0.6.0 - dev: false + dev: true /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -15318,6 +15327,7 @@ packages: /queue-tick@1.0.1: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: true /quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -15354,6 +15364,7 @@ packages: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 + dev: true /react-animate-height@2.1.2(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-A9jfz/4CTdsIsE7WCQtO9UkOpMBcBRh8LxyHl2eoZz1ki02jpyUL5xt58gabd0CyeLQ8fRyQ+s2lyV2Ufu8Owg==} @@ -16175,7 +16186,7 @@ packages: simple-get: 4.0.1 tar-fs: 3.0.5 tunnel-agent: 0.6.0 - dev: false + dev: true /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} @@ -16230,7 +16241,7 @@ packages: /simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - dev: false + dev: true /simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} @@ -16238,7 +16249,7 @@ packages: decompress-response: 6.0.0 once: 1.4.0 simple-concat: 1.0.1 - dev: false + dev: true /simple-git@3.22.0: resolution: {integrity: sha512-6JujwSs0ac82jkGjMHiCnTifvf1crOiY/+tfs/Pqih6iow7VrpNKRRNdWm6RtaXpvvv/JGNYhlUtLhGFqHF+Yw==} @@ -16254,7 +16265,7 @@ packages: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 - dev: false + dev: true /simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} @@ -16515,6 +16526,7 @@ packages: queue-tick: 1.0.1 optionalDependencies: bare-events: 2.2.0 + dev: true /string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} @@ -16649,6 +16661,7 @@ packages: /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} + dev: true /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} @@ -16805,7 +16818,7 @@ packages: mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - dev: false + dev: true /tar-fs@3.0.5: resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} @@ -16815,7 +16828,7 @@ packages: optionalDependencies: bare-fs: 2.2.1 bare-path: 2.1.0 - dev: false + dev: true /tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -16826,7 +16839,7 @@ packages: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 - dev: false + dev: true /tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} @@ -16834,6 +16847,7 @@ packages: b4a: 1.6.6 fast-fifo: 1.3.2 streamx: 2.16.1 + dev: true /teeny-request@9.0.0: resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} @@ -17228,7 +17242,7 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 - dev: false + dev: true /tunnel@0.0.6: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} diff --git a/test/buildConfigWithDefaults.ts b/test/buildConfigWithDefaults.ts index bb56a2dd6..1f6ee2a63 100644 --- a/test/buildConfigWithDefaults.ts +++ b/test/buildConfigWithDefaults.ts @@ -1,4 +1,5 @@ import path from 'path' +import sharp from 'sharp' import type { Config, SanitizedConfig } from '../packages/payload/src/config/types' @@ -161,6 +162,7 @@ export function buildConfigWithDefaults(testConfig?: Partial): Promise