// @ts-check import crypto from "node:crypto"; import getImage from "./utils/getImage.js"; import getLinkElement from "./utils/getLinkElement.js"; import getStyleElement from "./utils/getStyleElement.js"; import getFilteredProps from "./utils/getFilteredProps.js"; import getContainerElement from "./utils/getContainerElement.js"; export default async function renderBackgroundImage(props) { const type = "BackgroundImage"; const { filteredProps, transformConfigs } = getFilteredProps(type, props); const { src, tag, content, preload, attributes, placeholder, breakpoints, backgroundSize, backgroundPosition, format, fallbackFormat, includeSourceFormat, formatOptions, artDirectives, } = filteredProps; const { link: linkAttributes = {}, style: styleAttributes = {}, container: containerAttributes = {}, } = attributes; const sizes = ""; const { uuid, images } = await getImage({ src, type, sizes, format, breakpoints, placeholder, artDirectives, fallbackFormat, includeSourceFormat, formatOptions, transformConfigs, }); const className = `astro-imagetools-background-image-${uuid}`; const { imagesizes } = images[images.length - 1]; const link = getLinkElement({ images, preload, imagesizes, linkAttributes }); const backgroundImageStylesArray = images.map(({ media, sources }) => { const uuid = crypto.randomBytes(4).toString("hex").toUpperCase() const fallbackUrlCustomVariable = `--astro-imagetools-background-image-fallback-url${uuid}` const newSources = {}; sources.forEach(({ src, format, srcset }) => { const sources = srcset .split(", ") .map((source) => [ source.slice(0, source.lastIndexOf(" ")), source.slice(source.lastIndexOf(" ") + 1, -1), ]); sources.forEach(([path, width]) => { if (!newSources[width]) { newSources[width] = []; } newSources[width].push({ src, format, path }); }); }); const widths = Object.keys(newSources) .map((width) => parseInt(width)) .reverse(); const maxWidth = Math.max(...widths); const styles = widths .map((width) => { const sources = newSources[width]; const styles = sources .map( ({ format, path }, i) => ` ${i !== sources.length - 1 ? `.${format} ` : ""}.${className} { background-repeat: no-repeat; background-image: url(${path}), var(${fallbackUrlCustomVariable}); background-size: ${backgroundSize}; background-position: ${backgroundPosition}; } ` ) .reverse() .join(""); return width === maxWidth ? styles : ` @media screen and (max-width: ${width}px) { ${styles} } `; }) .join(""); return { fallbackUrlCustomVariable, styles: media ? ` @media ${media} { ${styles} } ` : styles, }; }); const containerStyles = ` .${className} { position: relative; ${images .map(({ fallback }, i) => { const fallbackUrlCustomVariable = backgroundImageStylesArray[i].fallbackUrlCustomVariable; return `${fallbackUrlCustomVariable}: url("${encodeURI(fallback)}");`; }) .join("\n")} } `; const backgroundStyles = backgroundImageStylesArray.map(({ styles }) => styles).join("\n") + containerStyles; const style = getStyleElement({ styleAttributes, backgroundStyles }); const htmlElement = getContainerElement({ tag, content, className, containerAttributes, }); return { link, style, htmlElement }; }