local assets 1/2

This commit is contained in:
lovebird 2025-03-17 18:16:44 +01:00
parent 32b7d4bfed
commit 5720a69acf
13 changed files with 389 additions and 393 deletions

View File

@ -2,6 +2,6 @@
export default new Map([
["src/content/infopages/community.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fcommunity.mdx&astroContentModuleFlag=true")],
["src/content/infopages/contact.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fcontact.mdx&astroContentModuleFlag=true")],
["src/content/infopages/software.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fsoftware.mdx&astroContentModuleFlag=true")],
["src/content/infopages/resources.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fresources.mdx&astroContentModuleFlag=true")]]);
["src/content/infopages/resources.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fresources.mdx&astroContentModuleFlag=true")],
["src/content/infopages/software.mdx", () => import("astro:content-layer-deferred-module?astro%3Acontent-layer-deferred-module=&fileName=src%2Fcontent%2Finfopages%2Fsoftware.mdx&astroContentModuleFlag=true")]]);

View File

@ -9,14 +9,12 @@ import getBackgroundStyles from "./utils/getBackgroundStyles.js";
export default async function renderImg(props) {
const type = "Img";
if (props.src === undefined) {
props.src = 'https://picsum.photos/640/640'
console.error("The 'src' attribute is required for the 'Img' component.", props)
}
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
const {
src,
@ -37,8 +35,8 @@ export default async function renderImg(props) {
const artDirectives = [],
fallbackFormat = format,
fadeInTransition = false,
includeSourceFormat = false;
fadeInTransition = true,
includeSourceFormat = true;
const {
img: imgAttributes = {},

View File

@ -33,7 +33,7 @@ export default async function ({
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
await getProcessedImage(src, transformConfigs);
await delay(100);
await delay(250);
src = path;
rest.aspect = `${imageWidth / imageHeight}`;
@ -81,23 +81,19 @@ export default async function ({
// Ensure artDirectedImages is an array
const images = Array.isArray(artDirectedImages) ? [...artDirectedImages, mainImage] : [mainImage];
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
const returnObject = {
uuid,
images,
};
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;
} catch (error) {
console.error("Error processing images:", error);
debugger
throw error;
}
}

View File

@ -1,59 +1,59 @@
// @ts-check
import { fileURLToPath } from "node:url";
import { extname, relative, resolve } from "node:path";
import { getSrcPath } from "./getSrcPath.js";
import getResolvedSrc from "./getResolvedSrc.js";
import { cwd, sharp } from "../../utils/runtimeChecks.js";
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
const { getImageDetails } = await (sharp
? import("./imagetools.js")
: import("./codecs.js"));
export default async function getProcessedImage(src, transformConfigs) {
throwErrorIfUnsupported(src, extname(src).slice(1));
let base;
if (src.match("(http://|https://|data:image/).*")) {
({ src, base } = await getResolvedSrc(src))
} else {
const {
default: { isSsrBuild },
} = await import("../../astroViteConfigs.js")
if (isSsrBuild) {
const filename = fileURLToPath(import.meta.url)
const assetPath = resolve(filename, "../../client") + src
src = "/" + relative(cwd, assetPath);
}
}
const {
w,
h,
ar,
width = w,
height = h,
aspect = ar,
...rest
} = transformConfigs;
const path = src.replace(/\\/g, `/`);
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
await getSrcPath(src),
width,
height,
aspect
);
return {
path,
base,
rest,
image,
imageWidth,
imageHeight,
imageFormat,
};
}
// @ts-check
import { fileURLToPath } from "node:url";
import { extname, relative, resolve } from "node:path";
import { getSrcPath } from "./getSrcPath.js";
import getResolvedSrc from "./getResolvedSrc.js";
import { cwd, sharp } from "../../utils/runtimeChecks.js";
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
const { getImageDetails } = await (sharp
? import("./imagetools.js")
: import("./codecs.js"));
export default async function getProcessedImage(src, transformConfigs) {
throwErrorIfUnsupported(src, extname(src).slice(1));
let base;
if (src.match("(http://|https://|data:image/).*")) {
({ src, base } = await getResolvedSrc(src))
} else {
const {
default: { isSsrBuild },
} = await import("../../astroViteConfigs.js")
if (isSsrBuild) {
const filename = fileURLToPath(import.meta.url)
const assetPath = resolve(filename, "../../client") + src
src = "/" + relative(cwd, assetPath);
}
}
const {
w,
h,
ar,
width = w,
height = h,
aspect = ar,
...rest
} = transformConfigs;
const path = src.replace(/\\/g, `/`);
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
await getSrcPath(src),
width,
height,
aspect
);
return {
path,
base,
rest,
image,
imageWidth,
imageHeight,
imageFormat,
};
}

View File

@ -1,32 +1,31 @@
import fs from "node:fs";
import path from "node:path";
// To strip off params when checking for file on disk.
const paramPattern = /\?.*/;
/**
* getSrcPath allows the use of `src` attributes relative to either the public folder or project root.
*
* It first checks to see if the src is a file relative to the project root.
* If the file isn't found, it will look in the public folder.
* Finally, if it still can't be found, the original input will be returned.
*/
export async function getSrcPath(src) {
const { default: astroViteConfigs } = await import(
"../../astroViteConfigs.js"
);
// If this is already resolved to a file, return it.
if (fs.existsSync(src.replace(paramPattern, ""))) return src;
const rootPath = path.join(astroViteConfigs.rootDir, src);
const rootTest = rootPath.replace(paramPattern, "");
if (fs.existsSync(rootTest)) return rootPath;
const publicPath = path.join(astroViteConfigs.publicDir, src);
const publicTest = publicPath.replace(paramPattern, "");
if (fs.existsSync(publicTest)) return publicPath;
// Fallback
return src;
}
import fs from "node:fs";
import path from "node:path";
// To strip off params when checking for file on disk.
const paramPattern = /\?.*/;
/**
* getSrcPath allows the use of `src` attributes relative to either the public folder or project root.
*
* It first checks to see if the src is a file relative to the project root.
* If the file isn't found, it will look in the public folder.
* Finally, if it still can't be found, the original input will be returned.
*/
export async function getSrcPath(src) {
const { default: astroViteConfigs } = await import(
"../../astroViteConfigs.js"
);
// If this is already resolved to a file, return it.
if (fs.existsSync(src.replace(paramPattern, ""))) return src;
const rootPath = path.join(astroViteConfigs.rootDir, src);
const rootTest = rootPath.replace(paramPattern, "");
if (fs.existsSync(rootTest)) return rootPath;
const publicPath = path.join(astroViteConfigs.publicDir, src);
const publicTest = publicPath.replace(paramPattern, "");
if (fs.existsSync(publicTest)) return publicPath;
// Fallback
return src;
}

View File

@ -1,39 +1,37 @@
// @ts-check
import { getSrcPath } from "./getSrcPath.js";
export default async function getSrcset(
src,
base,
breakpoints,
format,
options
) {
options = {
format,
w: breakpoints,
...options,
};
const keys = Object.keys(options);
const params = keys.length
? keys
.map((key) =>
Array.isArray(options[key])
? `&${key}=${options[key].join(";")}`
: `&${key}=${options[key]}`
)
.join("")
: "";
const id = `${src}?${params.slice(1)}`;
const fullPath = await getSrcPath(id);
const { default: load } = await import("../../plugin/hooks/load.js");
// @ts-ignore
const srcset = (await load(fullPath, base)).slice(16, -1);
return srcset;
}
// @ts-check
import { getSrcPath } from "./getSrcPath.js";
export default async function getSrcset(
src,
base,
breakpoints,
format,
options
) {
options = {
format,
w: breakpoints,
...options,
};
const keys = Object.keys(options);
const params = keys.length
? keys
.map((key) =>
Array.isArray(options[key])
? `&${key}=${options[key].join(";")}`
: `&${key}=${options[key]}`
)
.join("")
: "";
const id = `${src}?${params.slice(1)}`
const fullPath = await getSrcPath(id);
const { default: load } = await import("../../plugin/hooks/load.js");
//console.log("get source set", fullPath, src, id);
const loaded = await load(fullPath, base)
if (!loaded) {
return "";
}
const srcset = loaded.slice(16, -1)
return srcset;
}

View File

@ -1,40 +1,44 @@
// @ts-check
import {
builtins,
loadImage,
applyTransforms,
generateTransforms,
} from "imagetools-core";
export {
loadImage
} from "imagetools-core";
export async function getImageDetails(path, width, height, aspect) {
const loadedImage = loadImage(path);
if (aspect && !width && !height) {
if (!width && !height) {
({ width } = await loadedImage.metadata());
}
if (width) {
height = width / aspect;
}
if (height) {
width = height * aspect;
}
}
const { image, metadata } = await applyTransforms(
generateTransforms({ width, height }, builtins).transforms,
loadedImage
);
const {
width: imageWidth,
height: imageHeight,
format: imageFormat,
} = metadata;
return { image, imageWidth, imageHeight, imageFormat };
}
// @ts-check
import fs from "node:fs";
import {
builtins,
loadImage,
applyTransforms,
generateTransforms,
} from "imagetools-core";
export {
loadImage
} from "imagetools-core";
export async function getImageDetails(path, width, height, aspect) {
if (!fs.existsSync(path)) {
return null
}
const loadedImage = loadImage(path);
if (aspect && !width && !height) {
if (!width && !height) {
({ width } = await loadedImage.metadata());
}
if (width) {
height = width / aspect;
}
if (height) {
width = height * aspect;
}
}
const { image, metadata } = await applyTransforms(
generateTransforms({ width, height }, builtins).transforms,
loadedImage
);
const {
width: imageWidth,
height: imageHeight,
format: imageFormat,
} = metadata;
return { image, imageWidth, imageHeight, imageFormat };
}

View File

@ -1,165 +1,163 @@
// @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";
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)
);
const getHash = (width) =>
objectHash(
{ width, options, rootRelativePosixSrc },
// @ts-ignore
{ algorithm: "sha256" }
);
const base =
typeof arguments[1] === "string"
? arguments[1]
: path.basename(src, path.extname(src));
const config = Object.fromEntries(searchParams);
const { image: loadedImage, width: imageWidth } =
store.get(src) || store.set(src, await getLoadedImage(src, ext)).get(src);
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 };
const { image, buffer } = raw
? {
image: sharp && loadedImage,
buffer: !sharp && loadedImage.data,
}
: await getTransformedImage({
src,
image: loadedImage,
config,
type,
});
const imageObject = { hash, type, image, buffer };
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}"`;
}
}
// @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";
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)
);
const getHash = (width) =>
objectHash(
{ width, options, rootRelativePosixSrc },
// @ts-ignore
{ algorithm: "sha256" }
);
const base =
typeof arguments[1] === "string"
? arguments[1]
: path.basename(src, path.extname(src));
const config = Object.fromEntries(searchParams);
const { image: loadedImage, width: imageWidth } =
store.get(src) || store.set(src, await getLoadedImage(src, ext)).get(src);
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 };
const { image, buffer } = raw
? {
image: sharp && loadedImage,
buffer: !sharp && loadedImage.data,
}
: await getTransformedImage({
src,
image: loadedImage,
config,
type,
});
const imageObject = { hash, type, image, buffer };
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}"`;
}
}

View File

@ -1,26 +1,26 @@
// @ts-check
import {
builtins,
loadImage,
applyTransforms,
generateTransforms,
} from "imagetools-core";
export const getLoadedImage = async (src) => {
const image = loadImage(src);
const { width } = await image.metadata();
return { image, width };
};
export const getTransformedImage = async ({ image, config }) => {
const { transforms } = generateTransforms(config, builtins);
const { image: encodedImage } = await applyTransforms(
transforms,
image.clone()
);
return { image: encodedImage, buffer: null };
};
// @ts-check
import {
builtins,
loadImage,
applyTransforms,
generateTransforms,
} from "imagetools-core";
export const getLoadedImage = async (src) => {
const image = loadImage(src);
const { width } = await image.metadata();
return { image, width };
};
export const getTransformedImage = async ({ image, config }) => {
const { transforms } = generateTransforms(config, builtins);
const { image: encodedImage } = await applyTransforms(
transforms,
image.clone()
);
return { image: encodedImage, buffer: null };
};

View File

@ -34,6 +34,10 @@ export const PRODUCT_HUGO_TEMPLATE = './osr/hugo/root.html'
export const PRODUCTS_TARGET_SRC = './src/content/en/retail'
export const PRODUCTS_TARGET = (lang) => `./content/${lang}/products`
// Product assets
export const ASSETS_LOCALE = false
// OSRL - Language
export const IS_DEV = true
export const OSRL_ENV = 'astro-release'

View File

@ -14,7 +14,7 @@ import { sync as mv } from '@polymech/fs/move'
import { logger } from '@/base/index.js'
import { removeArrayValues, removeArrays, removeBufferValues, removeEmptyObjects } from '@/base/objects.js'
import { ITEM_ASSET_URL, PRODUCT_CONFIG, PRODUCT_ROOT, DEFAULT_IMAGE_URL } from '../app/config.js'
import { ITEM_ASSET_URL, PRODUCT_CONFIG, PRODUCT_ROOT, DEFAULT_IMAGE_URL, ASSETS_LOCALE } from '../app/config.js'
import { GalleryImage, MetaJSON } from './images.js'
import { validateFilename, sanitizeFilename } from "@polymech/fs/utils"
@ -42,11 +42,11 @@ export const default_sort = (files: string[]): string[] => {
})
}
export const default_sanitize = (paths: string[]): string[] =>{
export const default_sanitize = (paths: string[]): string[] => {
return paths.map((filePath) => {
const dir = path.dirname(filePath);
const originalFilename = path.basename(filePath);
const sanitizedFilename = sanitizeFilename(originalFilename, { lowercase: false, whitespace: true });
const sanitizedFilename = sanitizeFilename(originalFilename)
if (originalFilename === sanitizedFilename) {
return filePath;
}
@ -75,6 +75,10 @@ export const default_filter = async (url: string) => {
return true
}
export const default_filter_locale = async (url: string) => {
return url && exists(url)
}
export const image_url = async (src, fallback = DEFAULT_IMAGE_URL) => {
let safeSrc = src
try {
@ -133,9 +137,14 @@ export const gallery = async (
))
}
galleryFiles = await pMap(galleryFiles, async (f) => (await default_filter(assetUrl(f))) ? f : null, { concurrency: 5 })
galleryFiles = galleryFiles.filter((f) => f !== null)
galleryFiles = default_sort(galleryFiles)
if (!ASSETS_LOCALE) {
galleryFiles = await pMap(galleryFiles, async (f) => (await default_filter(assetUrl(f))) ? f : null, { concurrency: 5 })
galleryFiles = galleryFiles.filter((f) => f !== null)
galleryFiles = default_sort(galleryFiles)
} else {
galleryFiles = galleryFiles.filter(default_filter_locale)
galleryFiles = default_sort(galleryFiles)
}
return await pMap(galleryFiles, async (file: string) => {
const parts = path.parse(file)
@ -171,7 +180,7 @@ export const gallery = async (
delete imageMeta.exif.icc
delete imageMeta.exif.xmp
delete imageMeta.exif.iptc
const src = await image_url(assetUrl(file))
const src = ASSETS_LOCALE ? filePath : await image_url(assetUrl(file))
const ret: GalleryImage =
{
name: path.parse(file).name,

View File

@ -150,17 +150,6 @@ const tracking = config?.tracking?.googleAnalytics
</script>
)}
{ REDIRECT && <script>
const currentPath = window.location.pathname;
if (!/^\/[a-z]{2}(\/|$)/i.test(currentPath)) {
let language = navigator.language
language = language.split('-')[0]
language = "en"
window.location.href = `/${language}`;
}
</script>
<link rel="sitemap" href="/sitemap-index.xml" />
}

View File

@ -5,6 +5,7 @@ import StoreEntries from "@/components/store/StoreEntries.astro"
import CtaTwo from "@/components/cta/CtaTwo.astro"
const allProducts = await getCollection("store")
const locale = Astro.currentLocale || "en"
const REDIRECT = false
---
<BaseLayout>
<CtaTwo />
@ -31,7 +32,7 @@ const locale = Astro.currentLocale || "en"
</div>
</section>
{ <script>
{ REDIRECT && <script>
const currentPath = window.location.pathname;
if (!/^\/[a-z]{2}(\/|$)/i.test(currentPath)) {
let language = navigator.language