cloudflare-next-intl 0.8.54 → 0.8.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { run } from "../dist/src/image_optimizer/run.js";
|
|
4
|
+
import { resolveOptions } from "../dist/src/image_optimizer/types.js";
|
|
5
|
+
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
const options = resolveOptions();
|
|
8
|
+
const cacheFile = path.resolve(root, options.cacheDir, "manifest.json");
|
|
9
|
+
|
|
10
|
+
console.log("[cfni-image-optimizer] scanning images in", options.dirs.join(", "));
|
|
11
|
+
const entries = await run(root, options, cacheFile);
|
|
12
|
+
console.log(`[cfni-image-optimizer] processed ${entries.length} images into ${options.outDir}`);
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
function toBase64(str) {
|
|
2
|
+
if (typeof Buffer !== "undefined") {
|
|
3
|
+
return Buffer.from(str).toString("base64");
|
|
4
|
+
}
|
|
5
|
+
if (typeof btoa === "function") {
|
|
6
|
+
return btoa(unescape(encodeURIComponent(str)));
|
|
7
|
+
}
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
1
10
|
export function getImageBlurSvg(blurDataURL, blurWidth, blurHeight, objectFit, stdDeviation = 20) {
|
|
2
11
|
const std = stdDeviation;
|
|
3
12
|
const viewBox = blurWidth && blurHeight
|
|
@@ -11,6 +20,6 @@ export function getImageBlurSvg(blurDataURL, blurWidth, blurHeight, objectFit, s
|
|
|
11
20
|
? "xMidYMid slice"
|
|
12
21
|
: "none";
|
|
13
22
|
const svg = `<svg xmlns='http://www.w3.org/2000/svg' ${viewBox}><filter id='b' color-interpolation-filters='sRGB'><feGaussianBlur stdDeviation='${std}'/><feColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/><feFlood x='0' y='0' width='100%' height='100%'/><feComposite operator='out' in='s'/><feComposite in2='SourceGraphic'/><feGaussianBlur stdDeviation='${std}'/></filter><image width='100%' height='100%' x='0' y='0' preserveAspectRatio='${preserveAspectRatio}' style='filter: url(#b);' href='${blurDataURL}'/></svg>`;
|
|
14
|
-
const base64 =
|
|
23
|
+
const base64 = toBase64(svg);
|
|
15
24
|
return `data:image/svg+xml;base64,${base64}`;
|
|
16
25
|
}
|
|
@@ -6,26 +6,71 @@ const manifestData = manifest;
|
|
|
6
6
|
const images = (manifestData && typeof manifestData === "object" && manifestData.images)
|
|
7
7
|
? manifestData.images
|
|
8
8
|
: {};
|
|
9
|
+
function findEntry(srcVal) {
|
|
10
|
+
if (!srcVal)
|
|
11
|
+
return undefined;
|
|
12
|
+
const raw = typeof srcVal === "string"
|
|
13
|
+
? srcVal
|
|
14
|
+
: (typeof srcVal === "object" && srcVal !== null && "src" in srcVal)
|
|
15
|
+
? String(srcVal.src)
|
|
16
|
+
: String(srcVal);
|
|
17
|
+
if (images[raw])
|
|
18
|
+
return images[raw];
|
|
19
|
+
const withoutPublic = raw.replace(/^\/?public/, "");
|
|
20
|
+
if (images[withoutPublic])
|
|
21
|
+
return images[withoutPublic];
|
|
22
|
+
const withSlash = withoutPublic.startsWith("/") ? withoutPublic : "/" + withoutPublic;
|
|
23
|
+
if (images[withSlash])
|
|
24
|
+
return images[withSlash];
|
|
25
|
+
const clean = withSlash.split("?")[0].split("#")[0];
|
|
26
|
+
if (images[clean])
|
|
27
|
+
return images[clean];
|
|
28
|
+
for (const [key, entry] of Object.entries(images)) {
|
|
29
|
+
const filename = key.split("/").pop();
|
|
30
|
+
if (filename && (clean.endsWith("/" + filename) || clean.includes(filename.split(".")[0]))) {
|
|
31
|
+
return entry;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
9
36
|
function resolveProps(props) {
|
|
10
37
|
let src = props.src;
|
|
11
38
|
let blurDataURL = props.blurDataURL;
|
|
12
39
|
let width = props.width;
|
|
13
40
|
let height = props.height;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
41
|
+
let style = props.style;
|
|
42
|
+
const entry = findEntry(src);
|
|
43
|
+
if (entry) {
|
|
44
|
+
if (entry.src) {
|
|
45
|
+
if (typeof src === "string") {
|
|
18
46
|
src = entry.src;
|
|
19
|
-
if (!blurDataURL && props.placeholder === "blur" && entry.blurDataURL) {
|
|
20
|
-
blurDataURL = getImageBlurSvg(entry.blurDataURL, entry.blurWidth, entry.blurHeight, props.style?.objectFit);
|
|
21
47
|
}
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
48
|
+
else if (typeof src === "object" && src !== null && "src" in src) {
|
|
49
|
+
src = { ...src, src: entry.src };
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
src = entry.src;
|
|
25
53
|
}
|
|
26
54
|
}
|
|
55
|
+
if (!blurDataURL && props.placeholder === "blur" && entry.blurDataURL) {
|
|
56
|
+
blurDataURL = getImageBlurSvg(entry.blurDataURL, entry.blurWidth, entry.blurHeight, props.style?.objectFit);
|
|
57
|
+
}
|
|
58
|
+
if (!width && !props.fill && entry.width) {
|
|
59
|
+
width = entry.width;
|
|
60
|
+
height = entry.height;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (props.placeholder === "blur" && blurDataURL) {
|
|
64
|
+
const objectFit = props.style?.objectFit || (props.className?.includes("object-contain") ? "contain" : "cover");
|
|
65
|
+
style = {
|
|
66
|
+
backgroundSize: objectFit,
|
|
67
|
+
backgroundPosition: "50% 50%",
|
|
68
|
+
backgroundRepeat: "no-repeat",
|
|
69
|
+
backgroundImage: `url("${blurDataURL}")`,
|
|
70
|
+
...style,
|
|
71
|
+
};
|
|
27
72
|
}
|
|
28
|
-
return { ...props, src, blurDataURL, width, height };
|
|
73
|
+
return { ...props, src, blurDataURL, width, height, style };
|
|
29
74
|
}
|
|
30
75
|
export default function Image(props) {
|
|
31
76
|
const resolved = resolveProps(props);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { OptimizedImage, ResolvedOptions } from "./types.js";
|
|
2
2
|
export declare function collectImages(dirs: string[], root: string): Promise<string[]>;
|
|
3
|
-
export declare function run(root: string, options: ResolvedOptions, cacheFile
|
|
3
|
+
export declare function run(root: string, options: ResolvedOptions, cacheFile?: string): Promise<OptimizedImage[]>;
|
|
@@ -41,27 +41,33 @@ function targetAndSiblingPaths(absolutePath, publicRoot, options, root) {
|
|
|
41
41
|
}
|
|
42
42
|
return result;
|
|
43
43
|
}
|
|
44
|
-
export async function run(root, options, cacheFile) {
|
|
45
|
-
const publicRoot = path.
|
|
46
|
-
const
|
|
44
|
+
export async function run(root, options, cacheFile = path.resolve(root, options.cacheDir, "manifest.json")) {
|
|
45
|
+
const publicRoot = path.resolve(root, "public");
|
|
46
|
+
const manifestPath = path.resolve(root, options.manifest);
|
|
47
47
|
const cache = await loadCache(cacheFile);
|
|
48
|
-
const
|
|
48
|
+
const nextCache = {};
|
|
49
|
+
const files = await collectImages(options.dirs, root);
|
|
49
50
|
const entries = [];
|
|
50
51
|
for (const file of files) {
|
|
51
|
-
const
|
|
52
|
-
const cached = cache[
|
|
52
|
+
const relativeKey = path.relative(root, file);
|
|
53
|
+
const cached = cache[relativeKey];
|
|
53
54
|
const targets = targetAndSiblingPaths(file, publicRoot, options, root);
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
const fresh = await isFresh(file, cached, targets);
|
|
56
|
+
if (fresh && cached) {
|
|
56
57
|
entries.push(cached.result);
|
|
58
|
+
nextCache[relativeKey] = cached;
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
61
|
const result = await processImage(file, publicRoot, options, root);
|
|
60
|
-
const
|
|
61
|
-
next[key] = { mtimeMs: stats.mtimeMs, size: stats.size, result };
|
|
62
|
+
const fileStat = await stat(file);
|
|
62
63
|
entries.push(result);
|
|
64
|
+
nextCache[relativeKey] = {
|
|
65
|
+
mtimeMs: fileStat.mtimeMs,
|
|
66
|
+
size: fileStat.size,
|
|
67
|
+
result,
|
|
68
|
+
};
|
|
63
69
|
}
|
|
64
|
-
await saveCache(cacheFile,
|
|
65
|
-
await writeManifest(
|
|
70
|
+
await saveCache(cacheFile, nextCache);
|
|
71
|
+
await writeManifest(manifestPath, entries);
|
|
66
72
|
return entries;
|
|
67
73
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-next-intl",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.56",
|
|
4
4
|
"description": "Optimized Next Intl Package Special for App Router and Cloudflare",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
7
8
|
"sideEffects": false,
|
|
8
9
|
"bin": {
|
|
9
10
|
"cfni-db-codegen": "bin/db_codegen.mjs",
|
|
10
|
-
"cfni-db-install-exec": "bin/db_install_exec.mjs"
|
|
11
|
+
"cfni-db-install-exec": "bin/db_install_exec.mjs",
|
|
12
|
+
"cfni-image-optimizer": "bin/image_optimizer.mjs",
|
|
13
|
+
"optimize-images": "bin/image_optimizer.mjs"
|
|
11
14
|
},
|
|
12
15
|
"files": [
|
|
13
16
|
"dist",
|