@inoo-ch/payload-image-optimizer 1.0.0

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/components/ImageBox.d.ts +22 -0
  4. package/dist/components/ImageBox.js +51 -0
  5. package/dist/components/ImageBox.js.map +1 -0
  6. package/dist/components/OptimizationStatus.d.ts +4 -0
  7. package/dist/components/OptimizationStatus.js +196 -0
  8. package/dist/components/OptimizationStatus.js.map +1 -0
  9. package/dist/components/RegenerationButton.d.ts +2 -0
  10. package/dist/components/RegenerationButton.js +212 -0
  11. package/dist/components/RegenerationButton.js.map +1 -0
  12. package/dist/defaults.d.ts +3 -0
  13. package/dist/defaults.js +35 -0
  14. package/dist/defaults.js.map +1 -0
  15. package/dist/endpoints/regenerate.d.ts +4 -0
  16. package/dist/endpoints/regenerate.js +144 -0
  17. package/dist/endpoints/regenerate.js.map +1 -0
  18. package/dist/exports/client.d.ts +6 -0
  19. package/dist/exports/client.js +6 -0
  20. package/dist/exports/client.js.map +1 -0
  21. package/dist/exports/rsc.d.ts +1 -0
  22. package/dist/exports/rsc.js +3 -0
  23. package/dist/exports/rsc.js.map +1 -0
  24. package/dist/fields/imageOptimizerField.d.ts +2 -0
  25. package/dist/fields/imageOptimizerField.js +75 -0
  26. package/dist/fields/imageOptimizerField.js.map +1 -0
  27. package/dist/hooks/afterChange.d.ts +3 -0
  28. package/dist/hooks/afterChange.js +40 -0
  29. package/dist/hooks/afterChange.js.map +1 -0
  30. package/dist/hooks/beforeChange.d.ts +3 -0
  31. package/dist/hooks/beforeChange.js +26 -0
  32. package/dist/hooks/beforeChange.js.map +1 -0
  33. package/dist/index.d.ts +5 -0
  34. package/dist/index.js +127 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/next-image.d.js +2 -0
  37. package/dist/next-image.d.js.map +1 -0
  38. package/dist/processing/index.d.ts +17 -0
  39. package/dist/processing/index.js +46 -0
  40. package/dist/processing/index.js.map +1 -0
  41. package/dist/tasks/convertFormats.d.ts +12 -0
  42. package/dist/tasks/convertFormats.js +84 -0
  43. package/dist/tasks/convertFormats.js.map +1 -0
  44. package/dist/tasks/regenerateDocument.d.ts +18 -0
  45. package/dist/tasks/regenerateDocument.js +121 -0
  46. package/dist/tasks/regenerateDocument.js.map +1 -0
  47. package/dist/types.d.ts +35 -0
  48. package/dist/types.js +3 -0
  49. package/dist/types.js.map +1 -0
  50. package/dist/utilities/getImageOptimizerProps.d.ts +32 -0
  51. package/dist/utilities/getImageOptimizerProps.js +55 -0
  52. package/dist/utilities/getImageOptimizerProps.js.map +1 -0
  53. package/dist/utilities/thumbhash.d.ts +2 -0
  54. package/dist/utilities/thumbhash.js +11 -0
  55. package/dist/utilities/thumbhash.js.map +1 -0
  56. package/package.json +141 -0
@@ -0,0 +1,55 @@
1
+ import { thumbHashToDataURL } from 'thumbhash';
2
+ /**
3
+ * Extracts image optimization props from a Payload media resource.
4
+ *
5
+ * Returns props that can be spread onto a Next.js `<Image>` component to add
6
+ * ThumbHash blur placeholders and focal-point-based object positioning.
7
+ *
8
+ * Works with any component — including the Payload website template's `ImageMedia`:
9
+ *
10
+ * ```tsx
11
+ * import { getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'
12
+ *
13
+ * const optimizerProps = getImageOptimizerProps(resource)
14
+ * <NextImage {...existingProps} {...optimizerProps} />
15
+ * ```
16
+ */ export function getImageOptimizerProps(resource) {
17
+ if (!resource) {
18
+ return {
19
+ placeholder: 'empty',
20
+ style: {
21
+ objectPosition: 'center'
22
+ }
23
+ };
24
+ }
25
+ const objectPosition = resource.focalX != null && resource.focalY != null ? `${resource.focalX}% ${resource.focalY}%` : 'center';
26
+ const thumbHash = resource.imageOptimizer?.thumbHash;
27
+ if (!thumbHash) {
28
+ return {
29
+ placeholder: 'empty',
30
+ style: {
31
+ objectPosition
32
+ }
33
+ };
34
+ }
35
+ try {
36
+ const bytes = Uint8Array.from(atob(thumbHash), (c)=>c.charCodeAt(0));
37
+ const blurDataURL = thumbHashToDataURL(bytes);
38
+ return {
39
+ placeholder: 'blur',
40
+ blurDataURL,
41
+ style: {
42
+ objectPosition
43
+ }
44
+ };
45
+ } catch {
46
+ return {
47
+ placeholder: 'empty',
48
+ style: {
49
+ objectPosition
50
+ }
51
+ };
52
+ }
53
+ }
54
+
55
+ //# sourceMappingURL=getImageOptimizerProps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/getImageOptimizerProps.ts"],"sourcesContent":["import { thumbHashToDataURL } from 'thumbhash'\n\ntype ImageOptimizerData = {\n thumbHash?: string | null\n}\n\ntype ResourceWithOptimizer = {\n imageOptimizer?: ImageOptimizerData | null\n focalX?: number | null\n focalY?: number | null\n}\n\nexport type ImageOptimizerProps = {\n placeholder: 'blur' | 'empty'\n blurDataURL?: string\n style: {\n objectPosition: string\n }\n}\n\n/**\n * Extracts image optimization props from a Payload media resource.\n *\n * Returns props that can be spread onto a Next.js `<Image>` component to add\n * ThumbHash blur placeholders and focal-point-based object positioning.\n *\n * Works with any component — including the Payload website template's `ImageMedia`:\n *\n * ```tsx\n * import { getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'\n *\n * const optimizerProps = getImageOptimizerProps(resource)\n * <NextImage {...existingProps} {...optimizerProps} />\n * ```\n */\nexport function getImageOptimizerProps(resource: ResourceWithOptimizer | null | undefined): ImageOptimizerProps {\n if (!resource) {\n return { placeholder: 'empty', style: { objectPosition: 'center' } }\n }\n\n const objectPosition =\n resource.focalX != null && resource.focalY != null\n ? `${resource.focalX}% ${resource.focalY}%`\n : 'center'\n\n const thumbHash = resource.imageOptimizer?.thumbHash\n if (!thumbHash) {\n return { placeholder: 'empty', style: { objectPosition } }\n }\n\n try {\n const bytes = Uint8Array.from(atob(thumbHash), (c) => c.charCodeAt(0))\n const blurDataURL = thumbHashToDataURL(bytes)\n return { placeholder: 'blur', blurDataURL, style: { objectPosition } }\n } catch {\n return { placeholder: 'empty', style: { objectPosition } }\n }\n}\n"],"names":["thumbHashToDataURL","getImageOptimizerProps","resource","placeholder","style","objectPosition","focalX","focalY","thumbHash","imageOptimizer","bytes","Uint8Array","from","atob","c","charCodeAt","blurDataURL"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,YAAW;AAoB9C;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,uBAAuBC,QAAkD;IACvF,IAAI,CAACA,UAAU;QACb,OAAO;YAAEC,aAAa;YAASC,OAAO;gBAAEC,gBAAgB;YAAS;QAAE;IACrE;IAEA,MAAMA,iBACJH,SAASI,MAAM,IAAI,QAAQJ,SAASK,MAAM,IAAI,OAC1C,GAAGL,SAASI,MAAM,CAAC,EAAE,EAAEJ,SAASK,MAAM,CAAC,CAAC,CAAC,GACzC;IAEN,MAAMC,YAAYN,SAASO,cAAc,EAAED;IAC3C,IAAI,CAACA,WAAW;QACd,OAAO;YAAEL,aAAa;YAASC,OAAO;gBAAEC;YAAe;QAAE;IAC3D;IAEA,IAAI;QACF,MAAMK,QAAQC,WAAWC,IAAI,CAACC,KAAKL,YAAY,CAACM,IAAMA,EAAEC,UAAU,CAAC;QACnE,MAAMC,cAAchB,mBAAmBU;QACvC,OAAO;YAAEP,aAAa;YAAQa;YAAaZ,OAAO;gBAAEC;YAAe;QAAE;IACvE,EAAE,OAAM;QACN,OAAO;YAAEF,aAAa;YAASC,OAAO;gBAAEC;YAAe;QAAE;IAC3D;AACF"}
@@ -0,0 +1,2 @@
1
+ export declare function encodeImageToThumbHash(buffer: Buffer, width: number, height: number): string;
2
+ export declare function decodeThumbHashToDataURL(thumbHash: string): string;
@@ -0,0 +1,11 @@
1
+ import { rgbaToThumbHash, thumbHashToDataURL } from 'thumbhash';
2
+ export function encodeImageToThumbHash(buffer, width, height) {
3
+ const thumbHash = rgbaToThumbHash(width, height, buffer);
4
+ return Buffer.from(thumbHash).toString('base64');
5
+ }
6
+ export function decodeThumbHashToDataURL(thumbHash) {
7
+ const thumbHashBuffer = Buffer.from(thumbHash, 'base64');
8
+ return thumbHashToDataURL(thumbHashBuffer);
9
+ }
10
+
11
+ //# sourceMappingURL=thumbhash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/thumbhash.ts"],"sourcesContent":["import { rgbaToThumbHash, thumbHashToDataURL } from 'thumbhash'\n\nexport function encodeImageToThumbHash(\n buffer: Buffer,\n width: number,\n height: number,\n): string {\n const thumbHash = rgbaToThumbHash(width, height, buffer)\n return Buffer.from(thumbHash).toString('base64')\n}\n\nexport function decodeThumbHashToDataURL(thumbHash: string): string {\n const thumbHashBuffer = Buffer.from(thumbHash, 'base64')\n return thumbHashToDataURL(thumbHashBuffer)\n}\n"],"names":["rgbaToThumbHash","thumbHashToDataURL","encodeImageToThumbHash","buffer","width","height","thumbHash","Buffer","from","toString","decodeThumbHashToDataURL","thumbHashBuffer"],"mappings":"AAAA,SAASA,eAAe,EAAEC,kBAAkB,QAAQ,YAAW;AAE/D,OAAO,SAASC,uBACdC,MAAc,EACdC,KAAa,EACbC,MAAc;IAEd,MAAMC,YAAYN,gBAAgBI,OAAOC,QAAQF;IACjD,OAAOI,OAAOC,IAAI,CAACF,WAAWG,QAAQ,CAAC;AACzC;AAEA,OAAO,SAASC,yBAAyBJ,SAAiB;IACxD,MAAMK,kBAAkBJ,OAAOC,IAAI,CAACF,WAAW;IAC/C,OAAOL,mBAAmBU;AAC5B"}
package/package.json ADDED
@@ -0,0 +1,141 @@
1
+ {
2
+ "name": "@inoo-ch/payload-image-optimizer",
3
+ "version": "1.0.0",
4
+ "description": "Payload CMS plugin for automatic image optimization — WebP/AVIF conversion, resize, EXIF strip, ThumbHash placeholders, and bulk regeneration",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "payload",
8
+ "payloadcms",
9
+ "plugin",
10
+ "image",
11
+ "optimization",
12
+ "webp",
13
+ "avif",
14
+ "thumbhash",
15
+ "sharp",
16
+ "resize",
17
+ "compress"
18
+ ],
19
+ "homepage": "https://github.com/payloadcms-plugins/image-optimizer",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/payloadcms-plugins/image-optimizer"
23
+ },
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "import": "./src/index.ts",
28
+ "types": "./src/index.ts",
29
+ "default": "./src/index.ts"
30
+ },
31
+ "./client": {
32
+ "import": "./src/exports/client.ts",
33
+ "types": "./src/exports/client.ts",
34
+ "default": "./src/exports/client.ts"
35
+ },
36
+ "./rsc": {
37
+ "import": "./src/exports/rsc.ts",
38
+ "types": "./src/exports/rsc.ts",
39
+ "default": "./src/exports/rsc.ts"
40
+ }
41
+ },
42
+ "main": "./src/index.ts",
43
+ "types": "./src/index.ts",
44
+ "files": [
45
+ "dist"
46
+ ],
47
+ "scripts": {
48
+ "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
49
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
50
+ "build:types": "tsc --outDir dist --rootDir ./src",
51
+ "clean": "rimraf {dist,*.tsbuildinfo}",
52
+ "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
53
+ "dev": "next dev dev --turbo",
54
+ "dev:generate-importmap": "pnpm dev:payload generate:importmap",
55
+ "dev:generate-types": "pnpm dev:payload generate:types",
56
+ "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
57
+ "generate:importmap": "pnpm dev:generate-importmap",
58
+ "generate:types": "pnpm dev:generate-types",
59
+ "lint": "eslint",
60
+ "lint:fix": "eslint ./src --fix",
61
+ "test": "pnpm test:int && pnpm test:e2e",
62
+ "test:e2e": "playwright test",
63
+ "test:int": "vitest"
64
+ },
65
+ "devDependencies": {
66
+ "@eslint/eslintrc": "^3.2.0",
67
+ "@payloadcms/db-mongodb": "3.79.0",
68
+ "@payloadcms/db-postgres": "3.79.0",
69
+ "@payloadcms/db-sqlite": "3.79.0",
70
+ "@payloadcms/eslint-config": "3.9.0",
71
+ "@payloadcms/next": "3.79.0",
72
+ "@payloadcms/richtext-lexical": "3.79.0",
73
+ "@payloadcms/ui": "3.79.0",
74
+ "@playwright/test": "1.58.2",
75
+ "@swc-node/register": "1.10.9",
76
+ "@swc/cli": "0.6.0",
77
+ "@types/node": "22.19.9",
78
+ "@types/react": "19.2.9",
79
+ "@types/react-dom": "19.2.3",
80
+ "copyfiles": "2.4.1",
81
+ "cross-env": "^7.0.3",
82
+ "eslint": "^9.23.0",
83
+ "eslint-config-next": "15.4.11",
84
+ "graphql": "^16.8.1",
85
+ "mongodb-memory-server": "10.1.4",
86
+ "next": "15.4.11",
87
+ "open": "^10.1.0",
88
+ "payload": "3.79.0",
89
+ "prettier": "^3.4.2",
90
+ "qs-esm": "7.0.2",
91
+ "react": "19.2.1",
92
+ "react-dom": "19.2.1",
93
+ "rimraf": "3.0.2",
94
+ "sharp": "0.34.2",
95
+ "sort-package-json": "^2.10.0",
96
+ "typescript": "5.7.3",
97
+ "vite-tsconfig-paths": "6.0.5",
98
+ "vitest": "4.0.18"
99
+ },
100
+ "peerDependencies": {
101
+ "next": "^14.0.0 || ^15.0.0",
102
+ "payload": "^3.0.0",
103
+ "react": "^18.0.0 || ^19.0.0"
104
+ },
105
+ "engines": {
106
+ "node": "^18.20.2 || >=20.9.0",
107
+ "pnpm": "^9 || ^10"
108
+ },
109
+ "publishConfig": {
110
+ "exports": {
111
+ ".": {
112
+ "import": "./dist/index.js",
113
+ "types": "./dist/index.d.ts",
114
+ "default": "./dist/index.js"
115
+ },
116
+ "./client": {
117
+ "import": "./dist/exports/client.js",
118
+ "types": "./dist/exports/client.d.ts",
119
+ "default": "./dist/exports/client.js"
120
+ },
121
+ "./rsc": {
122
+ "import": "./dist/exports/rsc.js",
123
+ "types": "./dist/exports/rsc.d.ts",
124
+ "default": "./dist/exports/rsc.js"
125
+ }
126
+ },
127
+ "main": "./dist/index.js",
128
+ "types": "./dist/index.d.ts"
129
+ },
130
+ "pnpm": {
131
+ "onlyBuiltDependencies": [
132
+ "sharp",
133
+ "esbuild",
134
+ "unrs-resolver"
135
+ ]
136
+ },
137
+ "registry": "https://registry.npmjs.org/",
138
+ "dependencies": {
139
+ "thumbhash": "^0.1.1"
140
+ }
141
+ }