img tools - cache

This commit is contained in:
babayaga 2025-08-27 21:12:10 +02:00
parent 1217408a6e
commit ac520a6e65
14 changed files with 140 additions and 49 deletions

View File

@ -19,12 +19,13 @@ export default async function load(id) {
const { search, searchParams } = fileURL;
id = id.replace(search, "");
const ext = path.extname(id).slice(1);
if (!supportedImageTypes.includes(ext)) return null;
debugger
const { default: astroViteConfigs } = await import(
// @ts-ignore

View File

@ -33,6 +33,7 @@ export default async function ({
transformConfigs,
}) {
try {
const args = Array.from(arguments);
const hash = objectHash(args);
@ -41,11 +42,6 @@ export default async function ({
return imagesData.get(hash);
}
// Caching removed from this level to ensure proper Vite store population
// Cache is now handled at the plugin level where it can properly manage the store
const start = performance.now();
const {
path,
base,
@ -119,6 +115,8 @@ export default async function ({
return returnObject;
} catch (error) {
console.error(`Error processing images:: ${src}`, error, error.stack);
debugger
throw error;
}
}

View File

@ -1,6 +1,8 @@
// @ts-check
import { fileURLToPath } from "node:url";
import { extname, relative, resolve } from "node:path";
import { fileURLToPath, URL } from "node:url";
import { extname, relative, resolve, join } from "node:path";
import fs from "node:fs/promises";
import { existsSync } from "node:fs";
import { getSrcPath } from "./getSrcPath.js";
import getResolvedSrc from "./getResolvedSrc.js";
@ -16,7 +18,14 @@ export default async function getProcessedImage(
transformConfigs,
{ skipCache = false } = {}
) {
throwErrorIfUnsupported(src, extname(src).slice(1));
const isRemote = src.startsWith("http");
const ext = isRemote
? extname(new URL(src).pathname).slice(1)
: extname(src).slice(1);
throwErrorIfUnsupported(src, ext);
let base;
if (src.match("(http://|https://|data:image/).*")) {
@ -44,15 +53,14 @@ export default async function getProcessedImage(
...rest
} = transformConfigs;
const path = src.replace(/\\/g, `/`);
let path = src.replace(/\\/g, `/`);
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
await getSrcPath(src),
width,
height,
aspect,
skipCache
);
let imagePath = isRemote ? join(cwd, path) : await getSrcPath(src);
const imageBuffer = await fs.readFile(imagePath);
const { image, imageWidth, imageHeight, imageFormat } =
await getImageDetails(imageBuffer, width, height, aspect, skipCache);
return {
path,

View File

@ -27,6 +27,10 @@ export async function getSrcPath(src) {
const publicTest = publicPath.replace(paramPattern, "");
if (fs.existsSync(publicTest)) return publicPath;
// @todo : remove this
const cwdTest = path.join(process.cwd(), src);
if (fs.existsSync(cwdTest)) return cwdTest;
// Fallback
return src;
}

View File

@ -29,20 +29,19 @@ export default async function getSrcset(
const [cleanSrc] = src.split("?");
const id = `${cleanSrc}?${params.slice(1)}`;
const fullPath = await getSrcPath(id);
const fullPath = await getSrcPath(cleanSrc);
const { default: load } = await import("../../plugin/hooks/load.js");
// @ts-ignore
let srcset = null
try {
const loaded = await load(fullPath, base);
if (loaded) {
srcset = loaded.slice(16, -1);
}
} catch (error) {
console.error(`Error loading image ${fullPath}:`, error);
console.error(`getSrcset::Error loading image ${fullPath}:`, error.message);
return null;
}

View File

@ -2,6 +2,7 @@
import { supportedImageTypes } from "../../utils/runtimeChecks.js";
export default function throwErrorIfUnsupported(src, ext) {
if (!ext && typeof ext !== "string") {
throw new Error(`Failed to load ${src}; Invalid image format`);
}

View File

@ -0,0 +1,20 @@
import { defineConfig } from "imagetools/config"
// https://astro-imagetools-docs.vercel.app/en/global-config-options/
export default defineConfig({
placeholder: "blurred",
format: ["webp", "avif", "jpg"],
fallbackFormat: "jpg",
includeSourceFormat: false,
cacheRoot:".test_cache2",
formatOptions: {
jpg: {
quality: 80,
},
png: {
quality: 80,
},
webp: {
quality: 50,
}
}
});

View File

@ -2,10 +2,10 @@ export default {
"environment": "build",
"isSsrBuild": false,
"projectBase": "",
"publicDir": "C:\\Users\\zx\\Desktop\\polymech\\site2\\public\\",
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\site2\\",
"publicDir": "C:\\Users\\zx\\Desktop\\polymech\\site-min\\public\\",
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\site-min\\",
"mode": "production",
"outDir": "C:\\Users\\zx\\Desktop\\polymech\\site2\\dist\\",
"outDir": "C:\\Users\\zx\\Desktop\\polymech\\site-min\\dist\\",
"assetsDir": "_astro",
"sourcemap": false,
"assetFileNames": "/_astro/[name]@[width].[hash][extname]"

View File

@ -30,7 +30,6 @@ export async function saveAndCopyAsset(
assetsDirExists = true;
}
console.log(`cp ${src} ${dest}`);
await fs.copyFile(src, dest).catch(async (error) => {
if (error.code === "ENOENT") {
const imageBuffer = buffer || (await image.toBuffer());

View File

@ -14,7 +14,8 @@
"scripts": {
"test:watch": "vitest",
"test": "vitest run",
"test:src": "vitest run tests/src.js"
"test:src": "vitest run tests/src.js",
"test:image": "vitest run tests/image.js"
},
"repository": {
"type": "git",

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,36 +1,52 @@
import fs from "node:fs";
import path from "node:path";
import { vi } from "vitest";
const testCachePath = path.join(process.cwd(), ".test_cache/");
const testCachePath = path.join(process.cwd(), "test_cache/");
export const invalid_urls = ["https://example.com/non-existent-image.jpg"];
export const real_urls = [
"https://picsum.photos/200/300",
"https://fastly.picsum.photos/id/343/200/300.jpg?hmac=_7ttvLezG-XONDvp0ILwQCv50ivQa_oewm7m6xV2uZA",
"https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/gallery/latest_controller.jpg",
"https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800&h=400&fit=crop",
"https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/samples/30mm.jpg",
"https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/samples/DSC05051.JPG",
];
export const default_options = {
format: ["webp", "avif", "jpg"],
formatOptions: {
jpg: {
quality: 80,
},
png: {
quality: 80,
},
webp: {
quality: 50,
},
},
placeholder: "blurred",
includeSourceFormat: false,
fallbackFormat: ["webp", "avif", "jpg"],
// cacheRoot: testCachePath,
sizes: (breakpoints) => {
const maxWidth = breakpoints[breakpoints.length - 1];
return `(min-width: ${maxWidth}px) ${maxWidth}px, 100vw`;
},
};
export function setup() {
// Create a temporary cache directory for tests
if (!fs.existsSync(testCachePath)) {
fs.mkdirSync(testCachePath, { recursive: true });
}
// Mock runtimeChecks to use the test cache path
vi.mock("../utils/runtimeChecks.js", () => ({
cwd: process.cwd(),
fsCachePath: testCachePath,
supportedImageTypes: [
"avif",
"jpeg",
"jpg",
"png",
"webp",
"heic",
"heif",
"tiff",
"gif",
],
}));
}
export function teardown() {
// Clean up the temporary cache directory
if (fs.existsSync(testCachePath)) {
fs.rmSync(testCachePath, { recursive: true, force: true });
// fs.rmSync(testCachePath, { recursive: true, force: true });
}
}

View File

@ -0,0 +1,44 @@
import { describe, it, expect, beforeAll, afterAll, vi, beforeEach } from "vitest";
import getImage from "../api/utils/getImage.js";
import {
setup,
teardown,
invalid_urls,
real_urls,
default_options,
} from "./commons.js";
describe("getImage", () => {
beforeAll(() => setup());
afterAll(teardown);
beforeEach(() => {
vi.clearAllMocks();
});
it("should return a default image for invalid URLs", async () => {
for (const src of invalid_urls) {
const result = await getImage({
src,
...default_options,
transformConfigs: {},
});
expect(result.images[0].sources[0].srcset).toContain("default");
}
}, 20000);
it("should process and return image data for valid URLs", async () => {
for (const src of real_urls) {
const result = await getImage({
src,
...default_options,
transformConfigs: {},
});
expect(result).toHaveProperty("uuid");
expect(result).toHaveProperty("images");
expect(Array.isArray(result.images)).toBe(true);
expect(result.images.length).toBeGreaterThan(0);
}
}, 20000);
});

View File

@ -4,5 +4,5 @@ export default defineConfig({
test: {
include: ["tests/**/*.js"],
},
root: ".",
root: ".",
});