'use strict'; const promises = require('node:fs/promises'); const node_path = require('node:path'); const CanvasKitInit = require('canvaskit-wasm'); const colorette = require('colorette'); const glob = require('glob'); const ora = require('ora'); const thumbhash = require('thumbhash'); const vite = require('vite'); const uncrypto = require('uncrypto'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const CanvasKitInit__default = /*#__PURE__*/_interopDefaultCompat(CanvasKitInit); const ora__default = /*#__PURE__*/_interopDefaultCompat(ora); function binaryToBase64(binary) { return btoa(String.fromCharCode(...binary)); } async function digestUint8ArrayDataSha256(data) { const hashBuffer = await uncrypto.subtle.digest("SHA-256", data); return Array.from(new Uint8Array(hashBuffer)); } async function hash(data, length = 10) { const hashResult = await digestUint8ArrayDataSha256(data); const hashBase64 = binaryToBase64(new Uint8Array(hashResult)); if (length > 0) return hashBase64.substring(0, length); return hashBase64; } function normalizeBase64(base64) { return base64.replace("/", "_").replace("+", "-").replace("=", "-"); } async function calculateThumbHashForFile(imageData) { const canvasKit = await CanvasKitInit__default(); const image = canvasKit.MakeImageFromEncoded(imageData); if (!image) throw new Error("Failed to make image from encoded data."); const width = image.width(); const height = image.height(); const scale = 100 / Math.max(width, height); const resizedWidth = Math.round(width * scale); const resizedHeight = Math.round(height * scale); const canvas = canvasKit.MakeCanvas(resizedWidth, resizedHeight); const context = canvas.getContext("2d"); context.drawImage(image, 0, 0, resizedWidth, resizedHeight); const pixels = context.getImageData(0, 0, resizedWidth, resizedHeight); const thumbHashBinary = thumbhash.rgbaToThumbHash(pixels.width, pixels.height, pixels.data); const thumbHashBase64 = binaryToBase64(thumbHashBinary); const thumbHashDataURL = await thumbhash.thumbHashToDataURL(thumbHashBinary); return { dataBase64: thumbHashBase64, dataUrl: thumbHashDataURL, width: resizedWidth, height: resizedHeight, originalWidth: width, originalHeight: height }; } function createThumbHashDataFromThumbHash(rootDir, assetDir, baseUrl, imageFileName, imageFullFileName, imageFullHash, imageFileHash, thumbHash) { const fileName = node_path.relative(rootDir, imageFileName); const thumbhashData = { ...thumbHash, assetFileName: vite.normalizePath(node_path.relative(rootDir, imageFullFileName)), assetFullFileName: vite.normalizePath(imageFullFileName), assetFullHash: imageFullHash, assetFileHash: imageFileHash, assetUrl: vite.normalizePath(node_path.join(assetDir, fileName)), assetUrlWithBase: vite.normalizePath(node_path.join(baseUrl, assetDir, fileName)) }; if (!thumbhashData.assetUrlWithBase.startsWith("/")) thumbhashData.assetUrlWithBase = `/${thumbhashData.assetUrlWithBase}`; return thumbhashData; } function getCacheDir(vitePressCacheDir) { return node_path.join(vitePressCacheDir, "@nolebase", "vitepress-plugin-thumbnail-hash", "thumbhashes"); } function getMapFilename(cacheDir) { return node_path.join(cacheDir, "map.json"); } async function exists(path) { try { await promises.stat(path); return true; } catch (error) { if (!(error instanceof Error)) throw error; if (!("code" in error)) throw error; if (error.code !== "ENOENT") throw error; return false; } } async function mkdirIfNotExist(dir) { const targetDirExists = await exists(dir); if (targetDirExists) return; await promises.mkdir(dir, { recursive: true }); } async function readCachedMapFile(path) { const targetPathExists = await exists(path); if (!targetPathExists) return {}; const content = await promises.readFile(path); return JSON.parse(content.toString("utf-8")); } function ThumbnailHashImages() { return { name: "@nolebase/vitepress-plugin-thumbnail-hash/images", enforce: "pre", config() { return { optimizeDeps: { exclude: [ "@nolebase/vitepress-plugin-thumbnail-hash/client" ] }, ssr: { noExternal: [ "@nolebase/vitepress-plugin-thumbnail-hash" ] } }; }, async configResolved(config) { const root = config.root; const vitepressConfig = config.vitepress; const startsAt = Date.now(); const moduleNamePrefix = colorette.cyan("@nolebase/vitepress-plugin-thumbnail-hash/images"); const grayPrefix = colorette.gray(":"); const spinnerPrefix = `${moduleNamePrefix}${grayPrefix}`; const spinner = ora__default({ discardStdin: false, isEnabled: config.command === "serve" }); spinner.start(`${spinnerPrefix} Prepare to generate hashes for images...`); const cacheDir = getCacheDir(vitepressConfig.cacheDir); await mkdirIfNotExist(cacheDir); const thumbhashMap = await readCachedMapFile(getMapFilename(cacheDir)); spinner.text = `${spinnerPrefix} Searching for images...`; const files = await glob.glob(`${root}/**/*.+(jpg|jpeg|png)`, { nodir: true }); spinner.text = `${spinnerPrefix} Calculating thumbhashes for images...`; const thumbhashes = await Promise.all(files.map(async (file) => { const cacheHit = thumbhashMap[vite.normalizePath(node_path.relative(root, file))]; if (cacheHit) return cacheHit; const readImageRawData = await promises.readFile(file); const imageFullHash = await hash(readImageRawData, -1); const imageFileHash = normalizeBase64(imageFullHash.substring(0, 10)); const calculatedThumbhashData = await calculateThumbHashForFile(readImageRawData); return createThumbHashDataFromThumbHash( root, vitepressConfig.assetsDir, vitepressConfig.site.base, file, file, imageFullHash, imageFileHash, calculatedThumbhashData ); })); spinner.text = `${spinnerPrefix} Aggregating calculated thumbhash data...`; for (const thumbhash of thumbhashes) thumbhashMap[thumbhash.assetFileName] = thumbhash; spinner.text = `${spinnerPrefix} Writing thumbhash data to cache...`; await promises.writeFile(getMapFilename(cacheDir), JSON.stringify(thumbhashMap, null, 2)); const elapsed = Date.now() - startsAt; spinner.succeed(`${spinnerPrefix} Done. ${colorette.gray(`(${elapsed}ms)`)}`); } }; } exports.ThumbnailHashImages = ThumbnailHashImages; //# sourceMappingURL=index.cjs.map