diff --git a/.changeset/poor-apes-cheat.md b/.changeset/poor-apes-cheat.md new file mode 100644 index 000000000000..1d8c380d0b86 --- /dev/null +++ b/.changeset/poor-apes-cheat.md @@ -0,0 +1,21 @@ +--- +'astro': minor +--- + +Adds an option for the Sharp image service to allow large images to be processed. Set `limitInputPixels: false` to bypass the default image size limit: + +```js +// astro.config.mjs +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + image: { + service: { + entrypoint: 'astro/assets/services/sharp', + config: { + limitInputPixels: false, + }, + }, + }, +}); +``` diff --git a/packages/astro/config.d.ts b/packages/astro/config.d.ts index a12f862d651b..b859f0b055fc 100644 --- a/packages/astro/config.d.ts +++ b/packages/astro/config.d.ts @@ -2,6 +2,7 @@ type ViteUserConfig = import('vite').UserConfig; type ViteUserConfigFn = import('vite').UserConfigFn; type AstroUserConfig = import('./dist/@types/astro.js').AstroUserConfig; type ImageServiceConfig = import('./dist/@types/astro.js').ImageServiceConfig; +type SharpImageServiceConfig = import('./dist/assets/services/sharp.js').SharpImageServiceConfig; /** * See the full Astro Configuration API Documentation @@ -17,7 +18,7 @@ export function getViteConfig(config: ViteUserConfig): ViteUserConfigFn; /** * Return the configuration needed to use the Sharp-based image service */ -export function sharpImageService(): ImageServiceConfig; +export function sharpImageService(config?: SharpImageServiceConfig): ImageServiceConfig; /** * Return the configuration needed to use the Squoosh-based image service diff --git a/packages/astro/config.mjs b/packages/astro/config.mjs index 208313287817..389387bddf02 100644 --- a/packages/astro/config.mjs +++ b/packages/astro/config.mjs @@ -1,9 +1,9 @@ export { defineConfig, getViteConfig } from './dist/config/index.js'; -export function sharpImageService() { +export function sharpImageService(config = {}) { return { entrypoint: 'astro/assets/services/sharp', - config: {}, + config, }; } diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 5af1a7417e7d..cadc704cd68e 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1096,8 +1096,13 @@ export interface AstroUserConfig { * ```js * { * image: { - * // Example: Enable the Sharp-based image service - * service: { entrypoint: 'astro/assets/services/sharp' }, + * // Example: Enable the Sharp-based image service with a custom config + * service: { + * entrypoint: 'astro/assets/services/sharp', + * config: { + * limitInputPixels: false, + * }, + * }, * }, * } * ``` diff --git a/packages/astro/src/assets/services/sharp.ts b/packages/astro/src/assets/services/sharp.ts index 21529913892c..74bf921d9ce2 100644 --- a/packages/astro/src/assets/services/sharp.ts +++ b/packages/astro/src/assets/services/sharp.ts @@ -8,6 +8,13 @@ import { type LocalImageService, } from './service.js'; +export interface SharpImageServiceConfig { + /** + * The `limitInputPixels` option passed to Sharp. See https://sharp.pixelplumbing.com/api-constructor for more information + */ + limitInputPixels?: number; +} + let sharp: typeof import('sharp'); const qualityTable: Record = { @@ -28,13 +35,13 @@ async function loadSharp() { return sharpImport; } -const sharpService: LocalImageService = { +const sharpService: LocalImageService = { validateOptions: baseService.validateOptions, getURL: baseService.getURL, parseURL: baseService.parseURL, getHTMLAttributes: baseService.getHTMLAttributes, getSrcSet: baseService.getSrcSet, - async transform(inputBuffer, transformOptions) { + async transform(inputBuffer, transformOptions, config) { if (!sharp) sharp = await loadSharp(); const transform: BaseServiceTransform = transformOptions as BaseServiceTransform; @@ -43,7 +50,11 @@ const sharpService: LocalImageService = { // TODO: Sharp has some support for SVGs, we could probably support this once Sharp is the default and only service. if (transform.format === 'svg') return { data: inputBuffer, format: 'svg' }; - let result = sharp(inputBuffer, { failOnError: false, pages: -1 }); + const result = sharp(inputBuffer, { + failOnError: false, + pages: -1, + limitInputPixels: config.service.config.limitInputPixels, + }); // always call rotate to adjust for EXIF data orientation result.rotate();