fix:imagetools
This commit is contained in:
parent
0f6bcc4267
commit
a64acb9f71
File diff suppressed because one or more lines are too long
8
package-lock.json
generated
8
package-lock.json
generated
@ -67,6 +67,7 @@
|
|||||||
"sharp": "^0.29.3",
|
"sharp": "^0.29.3",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"tailwindcss": "^4.0.7",
|
"tailwindcss": "^4.0.7",
|
||||||
|
"ts-retry": "^6.0.0",
|
||||||
"type-fest": "^4.34.1",
|
"type-fest": "^4.34.1",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
@ -15132,6 +15133,12 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-retry": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-retry/-/ts-retry-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-WsVRE/P+VNYbiQC3E6TeIXBRCQj7vzjN4MlXd84AC88K7WwuWShN7A3Q/QSV/yd1hjO8qn2Cevdqny2HMwKUaA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tsconfck": {
|
"node_modules/tsconfck": {
|
||||||
"version": "3.1.5",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz",
|
||||||
@ -16759,6 +16766,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astropub/codecs": "0.4.4",
|
"@astropub/codecs": "0.4.4",
|
||||||
|
"@polymech/fs": "file:../../../polymech-mono/packages/fs",
|
||||||
"file-type": "17.1.1",
|
"file-type": "17.1.1",
|
||||||
"find-cache-dir": "3.3.2",
|
"find-cache-dir": "3.3.2",
|
||||||
"find-up": "^6.3.0",
|
"find-up": "^6.3.0",
|
||||||
|
|||||||
@ -3,6 +3,9 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.js"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev --mode dev --host=0.0.0.0 --o-images=medium",
|
"dev": "astro dev --mode dev --host=0.0.0.0 --o-images=medium",
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
@ -77,6 +80,7 @@
|
|||||||
"sharp": "^0.29.3",
|
"sharp": "^0.29.3",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"tailwindcss": "^4.0.7",
|
"tailwindcss": "^4.0.7",
|
||||||
|
"ts-retry": "^6.0.0",
|
||||||
"type-fest": "^4.34.1",
|
"type-fest": "^4.34.1",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
|||||||
@ -4,12 +4,7 @@ import objectHash from "object-hash";
|
|||||||
import getImageSources from "./getImageSources.js";
|
import getImageSources from "./getImageSources.js";
|
||||||
import getProcessedImage from "./getProcessedImage.js";
|
import getProcessedImage from "./getProcessedImage.js";
|
||||||
import getArtDirectedImages from "./getArtDirectedImages.js";
|
import getArtDirectedImages from "./getArtDirectedImages.js";
|
||||||
import pMap from "p-map";
|
|
||||||
|
|
||||||
const imagesData = new Map();
|
const imagesData = new Map();
|
||||||
|
|
||||||
const delay = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
|
|
||||||
export default async function ({
|
export default async function ({
|
||||||
src,
|
src,
|
||||||
type,
|
type,
|
||||||
@ -23,29 +18,26 @@ export default async function ({
|
|||||||
artDirectives,
|
artDirectives,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
}) {
|
}) {
|
||||||
try {
|
|
||||||
const args = Array.from(arguments);
|
const args = Array.from(arguments);
|
||||||
|
|
||||||
const hash = objectHash(args);
|
const hash = objectHash(args);
|
||||||
|
|
||||||
if (imagesData.has(hash)) {
|
if (imagesData.has(hash)) {
|
||||||
return imagesData.get(hash);
|
return imagesData.get(hash);
|
||||||
}
|
}
|
||||||
// const start = performance.now();
|
|
||||||
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
|
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
|
||||||
await getProcessedImage(src, transformConfigs);
|
await getProcessedImage(src, transformConfigs);
|
||||||
|
|
||||||
await delay();
|
|
||||||
src = path;
|
src = path;
|
||||||
|
|
||||||
rest.aspect = `${imageWidth / imageHeight}`;
|
rest.aspect = `${imageWidth / imageHeight}`;
|
||||||
|
|
||||||
if (!fallbackFormat) {
|
if (!fallbackFormat) {
|
||||||
fallbackFormat = imageFormat;
|
fallbackFormat = imageFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch both image sources and art-directed images
|
const [mainImage, artDirectedImages] = await Promise.all([
|
||||||
const [mainImage, artDirectedImages] = await pMap(
|
getImageSources(
|
||||||
[
|
|
||||||
async () =>
|
|
||||||
await getImageSources(
|
|
||||||
src,
|
src,
|
||||||
base,
|
base,
|
||||||
image,
|
image,
|
||||||
@ -60,8 +52,7 @@ export default async function ({
|
|||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
rest
|
rest
|
||||||
),
|
),
|
||||||
async () => {
|
getArtDirectedImages(
|
||||||
return await getArtDirectedImages(
|
|
||||||
artDirectives,
|
artDirectives,
|
||||||
placeholder,
|
placeholder,
|
||||||
format,
|
format,
|
||||||
@ -71,28 +62,18 @@ export default async function ({
|
|||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
rest
|
rest
|
||||||
);
|
),
|
||||||
},
|
]);
|
||||||
],
|
|
||||||
async (task) => await task(),
|
const images = [...artDirectedImages, mainImage];
|
||||||
{ concurrency: 1 }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure artDirectedImages is an array
|
|
||||||
const images = Array.isArray(artDirectedImages) ? [...artDirectedImages, mainImage] : [mainImage];
|
|
||||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||||
|
|
||||||
const returnObject = {
|
const returnObject = {
|
||||||
uuid,
|
uuid,
|
||||||
images,
|
images,
|
||||||
};
|
};
|
||||||
|
|
||||||
imagesData.set(hash, returnObject);
|
imagesData.set(hash, returnObject)
|
||||||
//const end = performance.now();
|
|
||||||
//console.log( `Responsive Image sets generated for ${type} at ${args[0].src} in ${end - start}ms`);
|
|
||||||
return returnObject;
|
return returnObject;
|
||||||
} catch (error) {
|
|
||||||
console.error("Error processing images:", error);
|
|
||||||
debugger
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -3,8 +3,9 @@ import getSrcset from "./getSrcset.js";
|
|||||||
import getConfigOptions from "./getConfigOptions.js";
|
import getConfigOptions from "./getConfigOptions.js";
|
||||||
import getFallbackImage from "./getFallbackImage.js";
|
import getFallbackImage from "./getFallbackImage.js";
|
||||||
import pMap from "p-map";
|
import pMap from "p-map";
|
||||||
|
import { DEFAULT_IO_DELAY } from "../../constants.js";
|
||||||
|
|
||||||
function delay(ms = 150) {
|
function delay(ms = DEFAULT_IO_DELAY) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,28 +6,32 @@ import { getSrcPath } from "./getSrcPath.js";
|
|||||||
import getResolvedSrc from "./getResolvedSrc.js";
|
import getResolvedSrc from "./getResolvedSrc.js";
|
||||||
import { cwd, sharp } from "../../utils/runtimeChecks.js";
|
import { cwd, sharp } from "../../utils/runtimeChecks.js";
|
||||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||||
|
import { retryAsync } from "ts-retry";
|
||||||
|
import { DEFAULT_IO_DELAY } from "../../constants.js";
|
||||||
|
|
||||||
const { getImageDetails } = await (sharp
|
const { getImageDetails } = await (sharp
|
||||||
? import("./imagetools.js")
|
? import("./imagetools.js")
|
||||||
: import("./codecs.js"));
|
: import("./codecs.js"));
|
||||||
|
|
||||||
export default async function getProcessedImage(src, transformConfigs) {
|
|
||||||
throwErrorIfUnsupported(src, extname(src).slice(1));
|
|
||||||
|
|
||||||
|
export async function _getProcessedImage(src, transformConfigs) {
|
||||||
|
throwErrorIfUnsupported(src, extname(src).slice(1));
|
||||||
let base;
|
let base;
|
||||||
|
|
||||||
if (src.match("(http://|https://|data:image/).*")) {
|
if (src.match("(http://|https://|data:image/).*")) {
|
||||||
({ src, base } = await getResolvedSrc(src))
|
({ src, base } = await getResolvedSrc(src));
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
default: { isSsrBuild },
|
default: { isSsrBuild },
|
||||||
} = await import("../../astroViteConfigs.js")
|
} = await import("../../astroViteConfigs.js");
|
||||||
|
|
||||||
if (isSsrBuild) {
|
if (isSsrBuild) {
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url);
|
||||||
const assetPath = resolve(filename, "../../client") + src
|
const assetPath = resolve(filename, "../../client") + src;
|
||||||
src = "/" + relative(cwd, assetPath);
|
src = "/" + relative(cwd, assetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
@ -57,3 +61,8 @@ export default async function getProcessedImage(src, transformConfigs) {
|
|||||||
imageFormat,
|
imageFormat,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default async function getProcessedImage(src, transformConfigs) {
|
||||||
|
//@todo : hiccups in vips/sharp
|
||||||
|
return retryAsync(() => _getProcessedImage(src, transformConfigs), { delay: DEFAULT_IO_DELAY, maxTry: 3 })
|
||||||
|
}
|
||||||
@ -13,9 +13,7 @@ const { fileTypeFromBuffer } = await import("file-type");
|
|||||||
|
|
||||||
export default async function getResolvedSrc(src) {
|
export default async function getResolvedSrc(src) {
|
||||||
const token = crypto.createHash("md5").update(src).digest("hex");
|
const token = crypto.createHash("md5").update(src).digest("hex");
|
||||||
|
|
||||||
let filepath = fsCachePath + token;
|
let filepath = fsCachePath + token;
|
||||||
|
|
||||||
const fileExists = (() => {
|
const fileExists = (() => {
|
||||||
for (const type of supportedImageTypes) {
|
for (const type of supportedImageTypes) {
|
||||||
const fileExists = fs.existsSync(filepath + `.${type}`);
|
const fileExists = fs.existsSync(filepath + `.${type}`);
|
||||||
@ -28,18 +26,14 @@ export default async function getResolvedSrc(src) {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
try {
|
||||||
if (!fileExists) {
|
if (!fileExists) {
|
||||||
const buffer = Buffer.from(await (await fetch(src)).arrayBuffer());
|
const buffer = Buffer.from(await (await fetch(src)).arrayBuffer());
|
||||||
|
|
||||||
const { ext } = (await fileTypeFromBuffer(buffer)) || {};
|
const { ext } = (await fileTypeFromBuffer(buffer)) || {};
|
||||||
|
|
||||||
throwErrorIfUnsupported(src, ext);
|
throwErrorIfUnsupported(src, ext);
|
||||||
|
|
||||||
filepath += `.${ext}`;
|
filepath += `.${ext}`;
|
||||||
|
|
||||||
fs.writeFileSync(filepath, buffer);
|
fs.writeFileSync(filepath, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const base = /^https?:/.test(src)
|
const base = /^https?:/.test(src)
|
||||||
? parse(new URL(src).pathname).name
|
? parse(new URL(src).pathname).name
|
||||||
: undefined;
|
: undefined;
|
||||||
@ -47,4 +41,8 @@ export default async function getResolvedSrc(src) {
|
|||||||
src = join("/", relative(cwd, filepath));
|
src = join("/", relative(cwd, filepath));
|
||||||
|
|
||||||
return { src, base };
|
return { src, base };
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error while resolving src: ${src} : ${e.message}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/imagetools/constants.js
Normal file
1
packages/imagetools/constants.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const DEFAULT_IO_DELAY = 350;
|
||||||
@ -5,6 +5,7 @@ import { posix as path, resolve } from "node:path"
|
|||||||
import { saveAndCopyAsset } from "./utils/saveAndCopyAsset.js"
|
import { saveAndCopyAsset } from "./utils/saveAndCopyAsset.js"
|
||||||
import vitePluginAstroImageTools, { store } from "../plugin/index.js"
|
import vitePluginAstroImageTools, { store } from "../plugin/index.js"
|
||||||
import pMap from "p-map"
|
import pMap from "p-map"
|
||||||
|
import { DEFAULT_IO_DELAY } from "../constants.js"
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url);
|
const filename = fileURLToPath(import.meta.url);
|
||||||
const astroViteConfigsPath = resolve(filename, "../../astroViteConfigs.js");
|
const astroViteConfigsPath = resolve(filename, "../../astroViteConfigs.js");
|
||||||
@ -60,7 +61,7 @@ export default {
|
|||||||
assetPaths,
|
assetPaths,
|
||||||
async ([assetPath, { hash, image, buffer }]) => {
|
async ([assetPath, { hash, image, buffer }]) => {
|
||||||
// delay, otherwise unknown errors occur (sharp/vips)
|
// delay, otherwise unknown errors occur (sharp/vips)
|
||||||
await new Promise((resolve) => setTimeout(resolve, 150));
|
await new Promise((resolve) => setTimeout(resolve, DEFAULT_IO_DELAY));
|
||||||
try {
|
try {
|
||||||
await saveAndCopyAsset(
|
await saveAndCopyAsset(
|
||||||
hash,
|
hash,
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export const PRODUCTS_TARGET = (lang) => `./content/${lang}/products`
|
|||||||
|
|
||||||
// Product assets
|
// Product assets
|
||||||
|
|
||||||
export const ASSETS_LOCALE = true
|
export const ASSETS_LOCAL = false
|
||||||
|
|
||||||
// OSRL - Language
|
// OSRL - Language
|
||||||
export const IS_DEV = true
|
export const IS_DEV = true
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { sync as mv } from '@polymech/fs/move'
|
|||||||
import { logger } from '@/base/index.js'
|
import { logger } from '@/base/index.js'
|
||||||
|
|
||||||
import { removeArrayValues, removeArrays, removeBufferValues, removeEmptyObjects } from '@/base/objects.js'
|
import { removeArrayValues, removeArrays, removeBufferValues, removeEmptyObjects } from '@/base/objects.js'
|
||||||
import { ITEM_ASSET_URL, PRODUCT_CONFIG, PRODUCT_ROOT, DEFAULT_IMAGE_URL, ASSETS_LOCALE } from '../app/config.js'
|
import { ITEM_ASSET_URL, PRODUCT_CONFIG, PRODUCT_ROOT, DEFAULT_IMAGE_URL, ASSETS_LOCAL } from '../app/config.js'
|
||||||
import { GalleryImage, MetaJSON } from './images.js'
|
import { GalleryImage, MetaJSON } from './images.js'
|
||||||
|
|
||||||
import { validateFilename, sanitizeFilename } from "@polymech/fs/utils"
|
import { validateFilename, sanitizeFilename } from "@polymech/fs/utils"
|
||||||
@ -138,7 +138,7 @@ export const gallery = async (
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ASSETS_LOCALE) {
|
if (!ASSETS_LOCAL) {
|
||||||
galleryFiles = await pMap(galleryFiles, async (f) => (await default_filter(assetUrl(f))) ? f : null, { concurrency: 5 })
|
galleryFiles = await pMap(galleryFiles, async (f) => (await default_filter(assetUrl(f))) ? f : null, { concurrency: 5 })
|
||||||
galleryFiles = galleryFiles.filter((f) => f !== null)
|
galleryFiles = galleryFiles.filter((f) => f !== null)
|
||||||
galleryFiles = default_sort(galleryFiles)
|
galleryFiles = default_sort(galleryFiles)
|
||||||
@ -181,7 +181,7 @@ export const gallery = async (
|
|||||||
delete imageMeta.exif.icc
|
delete imageMeta.exif.icc
|
||||||
delete imageMeta.exif.xmp
|
delete imageMeta.exif.xmp
|
||||||
delete imageMeta.exif.iptc
|
delete imageMeta.exif.iptc
|
||||||
const src = ASSETS_LOCALE ? filePath : await image_url(assetUrl(file))
|
const src = ASSETS_LOCAL ? filePath : await image_url(assetUrl(file))
|
||||||
const ret: GalleryImage =
|
const ret: GalleryImage =
|
||||||
{
|
{
|
||||||
name: path.parse(file).name,
|
name: path.parse(file).name,
|
||||||
|
|||||||
Reference in New Issue
Block a user