polymech-astro/packages/imagetools_3/integration/index.js
2025-12-25 02:08:14 +01:00

134 lines
5.0 KiB
JavaScript

// @ts-check
import fs from "node:fs/promises"
import { fileURLToPath } from "node:url"
import { posix as path, resolve } from "node:path"
import { saveAndCopyAsset } from "./utils/saveAndCopyAsset.js"
import vitePluginAstroImageTools, { store } from "../plugin/index.js"
import pMap from "p-map"
import { sync as mkdir } from '@polymech/fs/dir'
import { sync as readFile } from '@polymech/fs/read'
import { sync as writeFile } from '@polymech/fs/write'
import { sync as exists } from '@polymech/fs/exists'
const filename = fileURLToPath(import.meta.url);
const astroViteConfigsPath = resolve(filename, "../../astroViteConfigs.js");
export default {
name: "imagetools",
hooks: {
"astro:config:setup": async function ({ config, command, updateConfig }) {
const environment = command;
const isSsrBuild =
command === "build" && !!config.adapter && config.output === "server";
console.log(`[imagetools] config:setup command: ${command}`);
let projectBase = path.normalize(config.base);
if (projectBase.startsWith("./")) projectBase = projectBase.slice(1);
if (!projectBase.startsWith("/")) projectBase = "/" + projectBase;
if (projectBase.endsWith("/")) projectBase = projectBase.slice(0, -1);
const astroViteConfigs = {
environment,
isSsrBuild,
projectBase,
publicDir: fileURLToPath(config.publicDir.href),
rootDir: fileURLToPath(config.root.href),
};
await fs.writeFile(
astroViteConfigsPath,
`export default ${JSON.stringify(astroViteConfigs)}`
);
updateConfig({
vite: {
plugins: [vitePluginAstroImageTools],
},
});
},
"astro:build:done": async function closeBundle({ logger }) {
console.time('[imagetools] build:done');
const { default: astroViteConfigs } = await import(
// @ts-ignore
"../astroViteConfigs.js"
);
const { mode, outDir, assetsDir, isSsrBuild } = astroViteConfigs;
if (mode === "production") {
// 1. Define the manifest path in the current working directory.
const manifestPath = path.join(process.cwd(), "./imagetools-manifest.json")
// 2. Read the manifest from the previous build, if it exists.
let previousAssets = new Map();
try {
const manifestContent = (await readFile(manifestPath, "string")) || "";
previousAssets = new Map(JSON.parse(/** @type {string} */(manifestContent) || "[]"));
} catch (e) {
// Manifest doesn't exist or is invalid, start fresh.
}
// 3. Get assets from the current build's in-memory store.
const currentAssets = new Map(
[...store.entries()].filter(([, { hash = null } = {}]) => hash)
);
logger.info(`[imagetools] Found ${currentAssets.size} new image(s) processed in this build.`);
// 4. Merge previous and current assets. Current assets overwrite previous ones.
const allAssets = new Map([...previousAssets, ...currentAssets]);
logger.info(`[imagetools] Replaying ${allAssets.size} image(s) from the persistent manifest.`);
if (allAssets.size === 0) {
logger.info('[imagetools] No images to process.');
console.timeEnd('[imagetools] build:done');
return;
}
// 5. Process the entire merged list of assets.
await pMap(
[...allAssets.entries()],
async ([assetPath, { hash, image, buffer }]) => {
try {
await saveAndCopyAsset(
hash,
image,
buffer,
outDir,
assetsDir,
assetPath,
isSsrBuild
);
} catch (error) {
logger.error(`Failed to process image ${assetPath}:`, error);
}
},
// higher concurrency causes sharp/vips errors as well
{ concurrency: 10 }
);
// 6. Write the updated asset list back to the manifest for the next build.
try {
// Create a serializable version of the manifest data
const serializableAssets = [...allAssets.entries()].map(
([key, { hash, buffer }]) => {
// We only need the hash and buffer for reconstitution.
// The `image` object is a non-serializable Sharp instance.
return [key, { hash, buffer }];
}
);
const manifestContent = JSON.stringify(serializableAssets, null, 2);
logger.info(`[imagetools] Writing manifest (${(manifestContent.length / 1024).toFixed(2)} KB)...`);
console.time('[imagetools] Manifest write took');
await writeFile(manifestPath, manifestContent);
console.timeEnd('[imagetools] Manifest write took');
} catch (error) {
logger.error("Failed to write the asset manifest.");
console.error(error); // Log the full error object
}
}
console.timeEnd('[imagetools] build:done');
},
},
};