aboutsummaryrefslogtreecommitdiff
path: root/packages/integrations/vercel/src/image/build-service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/vercel/src/image/build-service.ts')
-rw-r--r--packages/integrations/vercel/src/image/build-service.ts71
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;