UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

182 lines (181 loc) • 5.81 kB
import { AstroError, AstroErrorData } from "../../core/errors/index.js"; import { resolveDefaultOutputFormat } from "../utils/inferSourceFormat.js"; import { detector } from "../utils/vendor/image-size/detector.js"; import { baseService, parseQuality } from "./service.js"; let sharp; const qualityTable = { low: 25, mid: 50, high: 80, max: 100 }; function resolveSharpQuality(quality) { if (!quality) return void 0; const parsedQuality = parseQuality(quality); if (typeof parsedQuality === "number") { return parsedQuality; } return quality in qualityTable ? qualityTable[quality] : void 0; } function resolveSharpEncoderOptions(transform, inputFormat, serviceConfig = {}) { const quality = resolveSharpQuality(transform.quality); if (transform.format === void 0) { return quality === void 0 ? void 0 : { quality }; } switch (transform.format) { case "jpg": case "jpeg": return { ...serviceConfig.jpeg, ...quality === void 0 ? {} : { quality } }; case "png": return { ...serviceConfig.png, ...quality === void 0 ? {} : { quality } }; case "webp": { const webpOptions = { ...serviceConfig.webp, ...quality === void 0 ? {} : { quality } }; if (inputFormat === "gif") { webpOptions.loop ??= 0; } return webpOptions; } case "avif": return { ...serviceConfig.avif, ...quality === void 0 ? {} : { quality } }; default: return quality === void 0 ? void 0 : { quality }; } } async function loadSharp() { let sharpImport; try { sharpImport = (await import("sharp")).default; } catch { throw new AstroError(AstroErrorData.MissingSharp); } sharpImport.cache(false); return sharpImport; } const fitMap = { fill: "fill", contain: "inside", cover: "cover", none: "outside", "scale-down": "inside", outside: "outside", inside: "inside" }; const sharpService = { validateOptions: baseService.validateOptions, getURL: baseService.getURL, parseURL: baseService.parseURL, getHTMLAttributes: baseService.getHTMLAttributes, getSrcSet: baseService.getSrcSet, getRemoteSize: baseService.getRemoteSize, async transform(inputBuffer, transformOptions, config) { if (!sharp) sharp = await loadSharp(); const transform = transformOptions; const kernel = config.service.config.kernel; const bufferFormat = detector(inputBuffer); const outputFormat = transform.format ?? resolveDefaultOutputFormat(bufferFormat); if (outputFormat === "svg") { if (bufferFormat && bufferFormat !== "svg") { console.warn( `\u26A0\uFE0F Astro expected an SVG for "${transform.src}" but the source is ${bufferFormat}. Passing it through as ${bufferFormat} instead.` ); return { data: inputBuffer, format: bufferFormat }; } return { data: inputBuffer, format: "svg" }; } if (!bufferFormat) { throw new AstroError({ ...AstroErrorData.NoImageMetadata, message: AstroErrorData.NoImageMetadata.message(transform.src) }); } if (bufferFormat === "svg" && !config.dangerouslyProcessSVG) { throw new AstroError({ ...AstroErrorData.UnsupportedImageFormat, message: `SVG image processing is disabled, but the source for "${transform.src}" is an SVG. Pass it through unchanged by setting \`format="svg"\` on the component, or set \`image.dangerouslyProcessSVG: true\` to rasterize SVG sources.` }); } const result = sharp(inputBuffer, { failOnError: false, pages: -1, limitInputPixels: config.service.config.limitInputPixels }); result.rotate(); if (transform.width && transform.height) { const fit = transform.fit ? fitMap[transform.fit] ?? "inside" : void 0; result.resize({ width: Math.round(transform.width), height: Math.round(transform.height), kernel, fit, position: transform.position, withoutEnlargement: true }); } else if (transform.height && !transform.width) { result.resize({ height: Math.round(transform.height), withoutEnlargement: true, kernel }); } else if (transform.width) { result.resize({ width: Math.round(transform.width), withoutEnlargement: true, kernel }); } if (transform.background) { result.flatten({ background: transform.background }); } const encoderOptions = resolveSharpEncoderOptions( { format: outputFormat, quality: transform.quality }, bufferFormat, config.service.config ); if (outputFormat === "webp") { result.webp(encoderOptions); } else if (outputFormat === "png") { result.png(encoderOptions); } else if (outputFormat === "avif") { result.avif(encoderOptions); } else if (outputFormat === "jpeg" || outputFormat === "jpg") { result.jpeg(encoderOptions); } else { result.toFormat(outputFormat, encoderOptions); } let data; let info; try { ({ data, info } = await result.toBuffer({ resolveWithObject: true })); } catch { console.warn( `\u26A0\uFE0F Astro could not optimize image "${transform.src}". Sharp doesn't support this format. The image will be used unoptimized. Consider converting to WebP or placing in the public/ folder.` ); return { data: inputBuffer, format: bufferFormat }; } const needsCopy = "buffer" in data && data.buffer instanceof SharedArrayBuffer; return { data: needsCopy ? new Uint8Array(data) : data, format: info.format }; } }; var sharp_default = sharpService; export { sharp_default as default, resolveSharpEncoderOptions };