diff options
Diffstat (limited to 'packages/integrations/vercel/src/image/build-service.ts')
-rw-r--r-- | packages/integrations/vercel/src/image/build-service.ts | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/packages/integrations/vercel/src/image/build-service.ts b/packages/integrations/vercel/src/image/build-service.ts new file mode 100644 index 000000000..9bda259fe --- /dev/null +++ b/packages/integrations/vercel/src/image/build-service.ts @@ -0,0 +1,71 @@ +import type { ExternalImageService } from 'astro'; +import { baseService } from 'astro/assets'; +import { isESMImportedImage } from 'astro/assets/utils'; +import { sharedValidateOptions } from './shared.js'; + +const service: ExternalImageService = { + ...baseService, + validateOptions: (options, serviceOptions) => + sharedValidateOptions(options, serviceOptions.service.config, 'production'), + getHTMLAttributes(options) { + const { inputtedWidth, ...props } = options; + + // If `validateOptions` returned a different width than the one of the image, use it for attributes + if (inputtedWidth) { + props.width = inputtedWidth; + } + + let targetWidth = props.width; + let targetHeight = props.height; + if (isESMImportedImage(props.src)) { + const aspectRatio = props.src.width / props.src.height; + if (targetHeight && !targetWidth) { + // If we have a height but no width, use height to calculate the width + targetWidth = Math.round(targetHeight * aspectRatio); + } else if (targetWidth && !targetHeight) { + // If we have a width but no height, use width to calculate the height + targetHeight = Math.round(targetWidth / aspectRatio); + } else if (!targetWidth && !targetHeight) { + // If we have neither width or height, use the original image's dimensions + targetWidth = props.src.width; + targetHeight = props.src.height; + } + } + + const { src, width, height, format, quality, densities, widths, formats, ...attributes } = + options; + + return { + ...attributes, + width: targetWidth, + height: targetHeight, + loading: attributes.loading ?? 'lazy', + decoding: attributes.decoding ?? 'async', + }; + }, + getURL(options) { + // For SVG files, return the original source path + if (isESMImportedImage(options.src) && options.src.format === 'svg') { + return options.src.src; + } + + // For non-SVG files, continue with the Vercel image processing + const fileSrc = isESMImportedImage(options.src) + ? removeLeadingForwardSlash(options.src.src) + : options.src; + + const searchParams = new URLSearchParams(); + searchParams.append('url', fileSrc); + + options.width && searchParams.append('w', options.width.toString()); + options.quality && searchParams.append('q', options.quality.toString()); + + return '/_vercel/image?' + searchParams; + }, +}; + +function removeLeadingForwardSlash(path: string) { + return path.startsWith('/') ? path.substring(1) : path; +} + +export default service; |