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 = Buffer.from(svg).toString("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
- if (typeof src === "string") {
15
- const entry = images[src];
16
- if (entry) {
17
- if (entry.src)
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 (!width && !props.fill && entry.width) {
23
- width = entry.width;
24
- height = entry.height;
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: string): Promise<OptimizedImage[]>;
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.join(root, "public");
46
- const files = await collectImages(options.dirs, root);
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 next = {};
48
+ const nextCache = {};
49
+ const files = await collectImages(options.dirs, root);
49
50
  const entries = [];
50
51
  for (const file of files) {
51
- const key = path.relative(root, file);
52
- const cached = cache[key];
52
+ const relativeKey = path.relative(root, file);
53
+ const cached = cache[relativeKey];
53
54
  const targets = targetAndSiblingPaths(file, publicRoot, options, root);
54
- if (await isFresh(file, cached, targets)) {
55
- next[key] = cached;
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 stats = await stat(file);
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, next);
65
- await writeManifest(path.resolve(root, options.manifest), entries);
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.54",
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",