198 lines
5.4 KiB
JavaScript
198 lines
5.4 KiB
JavaScript
// @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)
|
|
);
|
|
|
|
// Include search params in the hash calculation to force re-processing on change
|
|
const getHash = (width) =>
|
|
objectHash(
|
|
{ width, options, rootRelativePosixSrc, search },
|
|
// @ts-ignore
|
|
{ algorithm: "sha256" }
|
|
);
|
|
|
|
const base =
|
|
typeof arguments[1] === "string"
|
|
? arguments[1]
|
|
: path.basename(src, path.extname(src));
|
|
|
|
const config = Object.fromEntries(searchParams);
|
|
|
|
// Use the full ID (including search params) + src for the store key
|
|
// This ensures that the same file with different params is treated as different source
|
|
const storeKey = src + search;
|
|
const { image: loadedImage, width: imageWidth } =
|
|
store.get(storeKey) || store.set(storeKey, await getLoadedImage(src, ext)).get(storeKey);
|
|
|
|
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}"`;
|
|
}
|
|
}
|