// @ts-check import path from "node:path"; import objectHash from "object-hash"; import { store } from "../index.js"; import { getCachedBuffer } from "../utils/cache.js"; import { getSrcPath } from "../../api/utils/getSrcPath.js"; import { getAssetPath, getConfigOptions } from "../utils/shared.js"; import { sharp, supportedImageTypes } from "../../utils/runtimeChecks.js"; import { get_cached_object, set_cached_object } from '@polymech/cache'; const { getLoadedImage, getTransformedImage } = await (sharp ? import("../utils/imagetools.js") : import("../utils/codecs.js")); export default async function load(id) { try { var fileURL = new URL(`file://${id}`); } catch (error) { return null; } const { search, searchParams } = fileURL; id = id.replace(search, ""); const ext = path.extname(id).slice(1); if (!supportedImageTypes.includes(ext)) return null; const { default: astroViteConfigs } = await import( // @ts-ignore "../../astroViteConfigs.js" ); const { environment, projectBase, assetFileNames } = astroViteConfigs; const src = await getSrcPath(id); const rootRelativePosixSrc = path.posix.normalize( path.relative("", src).split(path.sep).join(path.posix.sep) ); const getHash = (width) => objectHash( { width, options, rootRelativePosixSrc }, // @ts-ignore { algorithm: "sha256" } ); const base = typeof arguments[1] === "string" ? arguments[1] : path.basename(src, path.extname(src)); const config = Object.fromEntries(searchParams); const { image: loadedImage, width: imageWidth } = store.get(src) || store.set(src, await getLoadedImage(src, ext)).get(src); const { type, widths, options, extension, raw, inline } = getConfigOptions( config, ext, imageWidth ); if (raw) { const testConfig = { ...config }; delete testConfig.raw; delete testConfig.inline; delete testConfig.base64; if (Object.keys(testConfig).length > 0) { throw new Error( "If raw is set, no other options can be set except inline and base64" ); } } if (inline) { if (widths.length > 1) { throw new Error( `The base64 or inline parameter can't be used with multiple widths` ); } const [width] = widths; const hash = getHash(width); if (store.has(hash)) { return `export default "${store.get(hash)}"`; } else { const config = { width, ...options }; const { image, buffer } = raw ? { image: sharp ? loadedImage : null, buffer: !sharp ? loadedImage.data : null, } : await getTransformedImage({ src, image: loadedImage, config, type, }) const dataUri = `data:${type};base64,${( buffer || (await getCachedBuffer(hash, image)) ).toString("base64")}` store.set(hash, dataUri) return `export default "${dataUri}"` } } else { const sources = await Promise.all( widths.map(async (width) => { const hash = getHash(width); const assetPath = getAssetPath( base, assetFileNames, extension, width, hash ); if (!store.has(assetPath)) { const config = { width, ...options }; // Create cache key for this specific image transformation const cacheKey = { src: id, width, type, extension, options: objectHash(options) }; let imageObject = null; // Only use cache in production builds if (environment === "production") { imageObject = await get_cached_object(cacheKey, 'imagetools-plugin'); if (imageObject) { console.log(`[imagetools-cache] Cache hit for ${assetPath}`); } } // Process image if not cached if (!imageObject) { const { image, buffer } = raw ? { image: sharp && loadedImage, buffer: !sharp && loadedImage.data, } : await getTransformedImage({ src, image: loadedImage, config, type, }); imageObject = { hash, type, image, buffer }; // Cache the processed result in production if (environment === "production") { await set_cached_object(cacheKey, 'imagetools-plugin', imageObject, { src: id, width, type, timestamp: Date.now() }); console.log(`[imagetools-cache] Cached ${assetPath}`); } } store.set(assetPath, imageObject); } const modulePath = environment === "dev" ? assetPath : projectBase + assetPath; return { width, modulePath }; }) ); const srcset = sources.length > 1 ? sources .map(({ width, modulePath }) => `${modulePath} ${width}w`) .join(", ") : sources[0].modulePath; return `export default "${srcset}"`; } }