@inoo-ch/payload-image-optimizer 1.1.1 → 1.2.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 (38) hide show
  1. package/dist/components/ImageBox.d.ts +1 -15
  2. package/dist/components/ImageBox.js.map +1 -1
  3. package/dist/components/OptimizationStatus.js +84 -19
  4. package/dist/components/OptimizationStatus.js.map +1 -1
  5. package/dist/fields/imageOptimizerField.d.ts +4 -2
  6. package/dist/fields/imageOptimizerField.js +57 -54
  7. package/dist/fields/imageOptimizerField.js.map +1 -1
  8. package/dist/hooks/afterChange.js +2 -4
  9. package/dist/hooks/afterChange.js.map +1 -1
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.js +123 -105
  12. package/dist/index.js.map +1 -1
  13. package/dist/tasks/convertFormats.js +2 -4
  14. package/dist/tasks/convertFormats.js.map +1 -1
  15. package/dist/tasks/regenerateDocument.js +2 -4
  16. package/dist/tasks/regenerateDocument.js.map +1 -1
  17. package/dist/translations/index.d.ts +1 -0
  18. package/dist/translations/index.js +64 -0
  19. package/dist/translations/index.js.map +1 -0
  20. package/dist/types.d.ts +19 -1
  21. package/dist/types.js.map +1 -1
  22. package/dist/utilities/getImageOptimizerProps.d.ts +2 -10
  23. package/dist/utilities/getImageOptimizerProps.js.map +1 -1
  24. package/dist/utilities/resolveStaticDir.d.ts +3 -0
  25. package/dist/utilities/resolveStaticDir.js +10 -0
  26. package/dist/utilities/resolveStaticDir.js.map +1 -0
  27. package/package.json +3 -2
  28. package/src/components/ImageBox.tsx +1 -16
  29. package/src/components/OptimizationStatus.tsx +99 -20
  30. package/src/fields/imageOptimizerField.ts +65 -59
  31. package/src/hooks/afterChange.ts +2 -6
  32. package/src/index.ts +94 -95
  33. package/src/tasks/convertFormats.ts +2 -5
  34. package/src/tasks/regenerateDocument.ts +2 -5
  35. package/src/translations/index.ts +62 -0
  36. package/src/types.ts +20 -1
  37. package/src/utilities/getImageOptimizerProps.ts +2 -10
  38. package/src/utilities/resolveStaticDir.ts +12 -0
package/dist/index.js CHANGED
@@ -1,127 +1,145 @@
1
+ import { deepMergeSimple } from 'payload/shared';
1
2
  import { resolveConfig } from './defaults.js';
3
+ import { translations } from './translations/index.js';
2
4
  import { getImageOptimizerField } from './fields/imageOptimizerField.js';
3
5
  import { createBeforeChangeHook } from './hooks/beforeChange.js';
4
6
  import { createAfterChangeHook } from './hooks/afterChange.js';
5
7
  import { createConvertFormatsHandler } from './tasks/convertFormats.js';
6
8
  import { createRegenerateDocumentHandler } from './tasks/regenerateDocument.js';
7
9
  import { createRegenerateHandler, createRegenerateStatusHandler } from './endpoints/regenerate.js';
10
+ export { defaultImageOptimizerFields } from './fields/imageOptimizerField.js';
8
11
  export { encodeImageToThumbHash, decodeThumbHashToDataURL } from './utilities/thumbhash.js';
9
12
  export const imageOptimizer = (pluginOptions)=>(config)=>{
10
13
  const resolvedConfig = resolveConfig(pluginOptions);
11
- if (!config.collections) {
12
- config.collections = [];
13
- }
14
- // Inject imageOptimizer fields into targeted upload collections
15
- for(const collectionSlug in resolvedConfig.collections){
16
- const collection = config.collections.find((c)=>c.slug === collectionSlug);
17
- if (collection) {
18
- collection.fields.push(getImageOptimizerField());
14
+ const targetSlugs = Object.keys(resolvedConfig.collections);
15
+ // Inject fields (and hooks when enabled) into targeted upload collections
16
+ const collections = (config.collections || []).map((collection)=>{
17
+ if (!targetSlugs.includes(collection.slug)) {
18
+ return collection;
19
19
  }
20
- }
21
- // If disabled, keep fields for schema consistency but skip hooks/tasks
22
- if (resolvedConfig.disabled) {
23
- return config;
24
- }
25
- // Inject hooks into targeted upload collections
26
- for(const collectionSlug in resolvedConfig.collections){
27
- const collection = config.collections.find((c)=>c.slug === collectionSlug);
28
- if (collection) {
29
- if (!collection.hooks) {
30
- collection.hooks = {};
31
- }
32
- if (!collection.hooks.beforeChange) {
33
- collection.hooks.beforeChange = [];
34
- }
35
- collection.hooks.beforeChange.push(createBeforeChangeHook(resolvedConfig, collectionSlug));
36
- if (!collection.hooks.afterChange) {
37
- collection.hooks.afterChange = [];
38
- }
39
- collection.hooks.afterChange.push(createAfterChangeHook(resolvedConfig, collectionSlug));
40
- // Add RegenerationButton to the collection list view
41
- if (!collection.admin) {
42
- collection.admin = {};
43
- }
44
- if (!collection.admin.components) {
45
- collection.admin.components = {};
46
- }
47
- if (!collection.admin.components.beforeListTable) {
48
- collection.admin.components.beforeListTable = [];
49
- }
50
- collection.admin.components.beforeListTable.push('@inoo-ch/payload-image-optimizer/client#RegenerationButton');
20
+ // Always inject fields for schema consistency (even when disabled)
21
+ const fields = [
22
+ ...collection.fields,
23
+ getImageOptimizerField(pluginOptions.fieldsOverride)
24
+ ];
25
+ if (resolvedConfig.disabled) {
26
+ return {
27
+ ...collection,
28
+ fields
29
+ };
51
30
  }
52
- }
53
- // Register async format conversion job task
54
- if (!config.jobs) {
55
- config.jobs = {
56
- tasks: []
57
- };
58
- }
59
- if (!config.jobs.tasks) {
60
- config.jobs.tasks = [];
61
- }
62
- config.jobs.tasks.push({
63
- slug: 'imageOptimizer_convertFormats',
64
- inputSchema: [
65
- {
66
- name: 'collectionSlug',
67
- type: 'text',
68
- required: true
31
+ return {
32
+ ...collection,
33
+ fields,
34
+ hooks: {
35
+ ...collection.hooks,
36
+ beforeChange: [
37
+ ...collection.hooks?.beforeChange || [],
38
+ createBeforeChangeHook(resolvedConfig, collection.slug)
39
+ ],
40
+ afterChange: [
41
+ ...collection.hooks?.afterChange || [],
42
+ createAfterChangeHook(resolvedConfig, collection.slug)
43
+ ]
69
44
  },
70
- {
71
- name: 'docId',
72
- type: 'text',
73
- required: true
74
- }
75
- ],
76
- outputSchema: [
77
- {
78
- name: 'variantsGenerated',
79
- type: 'number'
45
+ admin: {
46
+ ...collection.admin,
47
+ components: {
48
+ ...collection.admin?.components,
49
+ beforeListTable: [
50
+ ...collection.admin?.components?.beforeListTable || [],
51
+ '@inoo-ch/payload-image-optimizer/client#RegenerationButton'
52
+ ]
53
+ }
80
54
  }
81
- ],
82
- retries: 2,
83
- handler: createConvertFormatsHandler(resolvedConfig)
55
+ };
84
56
  });
85
- config.jobs.tasks.push({
86
- slug: 'imageOptimizer_regenerateDocument',
87
- inputSchema: [
88
- {
89
- name: 'collectionSlug',
90
- type: 'text',
91
- required: true
92
- },
93
- {
94
- name: 'docId',
95
- type: 'text',
96
- required: true
97
- }
98
- ],
99
- outputSchema: [
57
+ const i18n = {
58
+ ...config.i18n,
59
+ translations: deepMergeSimple(translations, config.i18n?.translations ?? {})
60
+ };
61
+ // If disabled, return with fields injected but no tasks/endpoints
62
+ if (resolvedConfig.disabled) {
63
+ return {
64
+ ...config,
65
+ collections,
66
+ i18n
67
+ };
68
+ }
69
+ return {
70
+ ...config,
71
+ collections,
72
+ i18n,
73
+ jobs: {
74
+ ...config.jobs,
75
+ tasks: [
76
+ ...config.jobs?.tasks || [],
77
+ {
78
+ slug: 'imageOptimizer_convertFormats',
79
+ inputSchema: [
80
+ {
81
+ name: 'collectionSlug',
82
+ type: 'text',
83
+ required: true
84
+ },
85
+ {
86
+ name: 'docId',
87
+ type: 'text',
88
+ required: true
89
+ }
90
+ ],
91
+ outputSchema: [
92
+ {
93
+ name: 'variantsGenerated',
94
+ type: 'number'
95
+ }
96
+ ],
97
+ retries: 2,
98
+ handler: createConvertFormatsHandler(resolvedConfig)
99
+ },
100
+ {
101
+ slug: 'imageOptimizer_regenerateDocument',
102
+ inputSchema: [
103
+ {
104
+ name: 'collectionSlug',
105
+ type: 'text',
106
+ required: true
107
+ },
108
+ {
109
+ name: 'docId',
110
+ type: 'text',
111
+ required: true
112
+ }
113
+ ],
114
+ outputSchema: [
115
+ {
116
+ name: 'status',
117
+ type: 'text'
118
+ },
119
+ {
120
+ name: 'reason',
121
+ type: 'text'
122
+ }
123
+ ],
124
+ retries: 2,
125
+ handler: createRegenerateDocumentHandler(resolvedConfig)
126
+ }
127
+ ]
128
+ },
129
+ endpoints: [
130
+ ...config.endpoints ?? [],
100
131
  {
101
- name: 'status',
102
- type: 'text'
132
+ path: '/image-optimizer/regenerate',
133
+ method: 'post',
134
+ handler: createRegenerateHandler(resolvedConfig)
103
135
  },
104
136
  {
105
- name: 'reason',
106
- type: 'text'
137
+ path: '/image-optimizer/regenerate',
138
+ method: 'get',
139
+ handler: createRegenerateStatusHandler(resolvedConfig)
107
140
  }
108
- ],
109
- retries: 2,
110
- handler: createRegenerateDocumentHandler(resolvedConfig)
111
- });
112
- // Register regeneration endpoints
113
- if (!config.endpoints) config.endpoints = [];
114
- config.endpoints.push({
115
- path: '/image-optimizer/regenerate',
116
- method: 'post',
117
- handler: createRegenerateHandler(resolvedConfig)
118
- });
119
- config.endpoints.push({
120
- path: '/image-optimizer/regenerate',
121
- method: 'get',
122
- handler: createRegenerateStatusHandler(resolvedConfig)
123
- });
124
- return config;
141
+ ]
142
+ };
125
143
  };
126
144
 
127
145
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { ImageOptimizerConfig } from './types.js'\nimport { resolveConfig } from './defaults.js'\nimport { getImageOptimizerField } from './fields/imageOptimizerField.js'\nimport { createBeforeChangeHook } from './hooks/beforeChange.js'\nimport { createAfterChangeHook } from './hooks/afterChange.js'\nimport { createConvertFormatsHandler } from './tasks/convertFormats.js'\nimport { createRegenerateDocumentHandler } from './tasks/regenerateDocument.js'\nimport { createRegenerateHandler, createRegenerateStatusHandler } from './endpoints/regenerate.js'\n\nexport type { ImageOptimizerConfig, ImageFormat, FormatQuality, CollectionOptimizerConfig } from './types.js'\n\nexport { encodeImageToThumbHash, decodeThumbHashToDataURL } from './utilities/thumbhash.js'\n\nexport const imageOptimizer =\n (pluginOptions: ImageOptimizerConfig) =>\n (config: Config): Config => {\n const resolvedConfig = resolveConfig(pluginOptions)\n\n if (!config.collections) {\n config.collections = []\n }\n\n // Inject imageOptimizer fields into targeted upload collections\n for (const collectionSlug in resolvedConfig.collections) {\n const collection = config.collections.find((c) => c.slug === collectionSlug)\n\n if (collection) {\n collection.fields.push(getImageOptimizerField())\n }\n }\n\n // If disabled, keep fields for schema consistency but skip hooks/tasks\n if (resolvedConfig.disabled) {\n return config\n }\n\n // Inject hooks into targeted upload collections\n for (const collectionSlug in resolvedConfig.collections) {\n const collection = config.collections.find((c) => c.slug === collectionSlug)\n\n if (collection) {\n if (!collection.hooks) {\n collection.hooks = {}\n }\n\n if (!collection.hooks.beforeChange) {\n collection.hooks.beforeChange = []\n }\n collection.hooks.beforeChange.push(createBeforeChangeHook(resolvedConfig, collectionSlug))\n\n if (!collection.hooks.afterChange) {\n collection.hooks.afterChange = []\n }\n collection.hooks.afterChange.push(createAfterChangeHook(resolvedConfig, collectionSlug))\n\n // Add RegenerationButton to the collection list view\n if (!collection.admin) {\n collection.admin = {}\n }\n if (!collection.admin.components) {\n collection.admin.components = {}\n }\n if (!collection.admin.components.beforeListTable) {\n collection.admin.components.beforeListTable = []\n }\n collection.admin.components.beforeListTable.push(\n '@inoo-ch/payload-image-optimizer/client#RegenerationButton',\n )\n }\n }\n\n // Register async format conversion job task\n if (!config.jobs) {\n config.jobs = { tasks: [] }\n }\n if (!config.jobs!.tasks) {\n config.jobs!.tasks = []\n }\n\n config.jobs!.tasks!.push({\n slug: 'imageOptimizer_convertFormats',\n inputSchema: [\n { name: 'collectionSlug', type: 'text', required: true },\n { name: 'docId', type: 'text', required: true },\n ],\n outputSchema: [\n { name: 'variantsGenerated', type: 'number' },\n ],\n retries: 2,\n handler: createConvertFormatsHandler(resolvedConfig),\n } as any)\n\n config.jobs!.tasks!.push({\n slug: 'imageOptimizer_regenerateDocument',\n inputSchema: [\n { name: 'collectionSlug', type: 'text', required: true },\n { name: 'docId', type: 'text', required: true },\n ],\n outputSchema: [\n { name: 'status', type: 'text' },\n { name: 'reason', type: 'text' },\n ],\n retries: 2,\n handler: createRegenerateDocumentHandler(resolvedConfig),\n } as any)\n\n // Register regeneration endpoints\n if (!config.endpoints) config.endpoints = []\n\n config.endpoints.push({\n path: '/image-optimizer/regenerate',\n method: 'post',\n handler: createRegenerateHandler(resolvedConfig),\n })\n\n config.endpoints.push({\n path: '/image-optimizer/regenerate',\n method: 'get',\n handler: createRegenerateStatusHandler(resolvedConfig),\n })\n\n return config\n }\n"],"names":["resolveConfig","getImageOptimizerField","createBeforeChangeHook","createAfterChangeHook","createConvertFormatsHandler","createRegenerateDocumentHandler","createRegenerateHandler","createRegenerateStatusHandler","encodeImageToThumbHash","decodeThumbHashToDataURL","imageOptimizer","pluginOptions","config","resolvedConfig","collections","collectionSlug","collection","find","c","slug","fields","push","disabled","hooks","beforeChange","afterChange","admin","components","beforeListTable","jobs","tasks","inputSchema","name","type","required","outputSchema","retries","handler","endpoints","path","method"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport { deepMergeSimple } from 'payload/shared'\n\nimport type { ImageOptimizerConfig } from './types.js'\nimport { resolveConfig } from './defaults.js'\nimport { translations } from './translations/index.js'\nimport { getImageOptimizerField } from './fields/imageOptimizerField.js'\nimport { createBeforeChangeHook } from './hooks/beforeChange.js'\nimport { createAfterChangeHook } from './hooks/afterChange.js'\nimport { createConvertFormatsHandler } from './tasks/convertFormats.js'\nimport { createRegenerateDocumentHandler } from './tasks/regenerateDocument.js'\nimport { createRegenerateHandler, createRegenerateStatusHandler } from './endpoints/regenerate.js'\n\nexport type { ImageOptimizerConfig, ImageFormat, FormatQuality, CollectionOptimizerConfig, ImageOptimizerData, MediaResource, FieldsOverride } from './types.js'\nexport { defaultImageOptimizerFields } from './fields/imageOptimizerField.js'\n\nexport { encodeImageToThumbHash, decodeThumbHashToDataURL } from './utilities/thumbhash.js'\n\nexport const imageOptimizer =\n (pluginOptions: ImageOptimizerConfig) =>\n (config: Config): Config => {\n const resolvedConfig = resolveConfig(pluginOptions)\n const targetSlugs = Object.keys(resolvedConfig.collections)\n\n // Inject fields (and hooks when enabled) into targeted upload collections\n const collections = (config.collections || []).map((collection) => {\n if (!targetSlugs.includes(collection.slug)) {\n return collection\n }\n\n // Always inject fields for schema consistency (even when disabled)\n const fields = [...collection.fields, getImageOptimizerField(pluginOptions.fieldsOverride)]\n\n if (resolvedConfig.disabled) {\n return { ...collection, fields }\n }\n\n return {\n ...collection,\n fields,\n hooks: {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange || []),\n createBeforeChangeHook(resolvedConfig, collection.slug),\n ],\n afterChange: [\n ...(collection.hooks?.afterChange || []),\n createAfterChangeHook(resolvedConfig, collection.slug),\n ],\n },\n admin: {\n ...collection.admin,\n components: {\n ...collection.admin?.components,\n beforeListTable: [\n ...(collection.admin?.components?.beforeListTable || []),\n '@inoo-ch/payload-image-optimizer/client#RegenerationButton',\n ],\n },\n },\n }\n })\n\n const i18n = {\n ...config.i18n,\n translations: deepMergeSimple(translations, config.i18n?.translations ?? {}),\n }\n\n // If disabled, return with fields injected but no tasks/endpoints\n if (resolvedConfig.disabled) {\n return { ...config, collections, i18n }\n }\n\n return {\n ...config,\n collections,\n i18n,\n jobs: {\n ...config.jobs,\n tasks: [\n ...(config.jobs?.tasks || []),\n {\n slug: 'imageOptimizer_convertFormats',\n inputSchema: [\n { name: 'collectionSlug', type: 'text', required: true },\n { name: 'docId', type: 'text', required: true },\n ],\n outputSchema: [\n { name: 'variantsGenerated', type: 'number' },\n ],\n retries: 2,\n handler: createConvertFormatsHandler(resolvedConfig),\n } as any,\n {\n slug: 'imageOptimizer_regenerateDocument',\n inputSchema: [\n { name: 'collectionSlug', type: 'text', required: true },\n { name: 'docId', type: 'text', required: true },\n ],\n outputSchema: [\n { name: 'status', type: 'text' },\n { name: 'reason', type: 'text' },\n ],\n retries: 2,\n handler: createRegenerateDocumentHandler(resolvedConfig),\n } as any,\n ],\n },\n endpoints: [\n ...(config.endpoints ?? []),\n {\n path: '/image-optimizer/regenerate',\n method: 'post',\n handler: createRegenerateHandler(resolvedConfig),\n },\n {\n path: '/image-optimizer/regenerate',\n method: 'get',\n handler: createRegenerateStatusHandler(resolvedConfig),\n },\n ],\n }\n }\n"],"names":["deepMergeSimple","resolveConfig","translations","getImageOptimizerField","createBeforeChangeHook","createAfterChangeHook","createConvertFormatsHandler","createRegenerateDocumentHandler","createRegenerateHandler","createRegenerateStatusHandler","defaultImageOptimizerFields","encodeImageToThumbHash","decodeThumbHashToDataURL","imageOptimizer","pluginOptions","config","resolvedConfig","targetSlugs","Object","keys","collections","map","collection","includes","slug","fields","fieldsOverride","disabled","hooks","beforeChange","afterChange","admin","components","beforeListTable","i18n","jobs","tasks","inputSchema","name","type","required","outputSchema","retries","handler","endpoints","path","method"],"mappings":"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"}
@@ -2,6 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { resolveCollectionConfig } from '../defaults.js';
4
4
  import { convertFormat } from '../processing/index.js';
5
+ import { resolveStaticDir } from '../utilities/resolveStaticDir.js';
5
6
  export const createConvertFormatsHandler = (resolvedConfig)=>{
6
7
  return async ({ input, req })=>{
7
8
  try {
@@ -10,13 +11,10 @@ export const createConvertFormatsHandler = (resolvedConfig)=>{
10
11
  id: input.docId
11
12
  });
12
13
  const collectionConfig = req.payload.collections[input.collectionSlug].config;
13
- let staticDir = typeof collectionConfig.upload === 'object' ? collectionConfig.upload.staticDir || '' : '';
14
+ const staticDir = resolveStaticDir(collectionConfig);
14
15
  if (!staticDir) {
15
16
  throw new Error(`No staticDir configured for collection "${input.collectionSlug}"`);
16
17
  }
17
- if (!path.isAbsolute(staticDir)) {
18
- staticDir = path.resolve(process.cwd(), staticDir);
19
- }
20
18
  // Sanitize filename to prevent path traversal
21
19
  const safeFilename = path.basename(doc.filename);
22
20
  const filePath = path.join(staticDir, safeFilename);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tasks/convertFormats.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\n\nimport type { CollectionSlug } from 'payload'\n\nimport type { ResolvedImageOptimizerConfig } from '../types.js'\nimport { resolveCollectionConfig } from '../defaults.js'\nimport { convertFormat } from '../processing/index.js'\n\nexport const createConvertFormatsHandler = (resolvedConfig: ResolvedImageOptimizerConfig) => {\n return async ({ input, req }: { input: { collectionSlug: string; docId: string }; req: any }) => {\n try {\n const doc = await req.payload.findByID({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n })\n\n const collectionConfig = req.payload.collections[input.collectionSlug as keyof typeof req.payload.collections].config\n\n let staticDir: string =\n typeof collectionConfig.upload === 'object' ? collectionConfig.upload.staticDir || '' : ''\n if (!staticDir) {\n throw new Error(`No staticDir configured for collection \"${input.collectionSlug}\"`)\n }\n if (!path.isAbsolute(staticDir)) {\n staticDir = path.resolve(process.cwd(), staticDir)\n }\n\n // Sanitize filename to prevent path traversal\n const safeFilename = path.basename(doc.filename)\n const filePath = path.join(staticDir, safeFilename)\n const fileBuffer = await fs.readFile(filePath)\n\n const variants: Array<{\n filename: string\n filesize: number\n format: string\n height: number\n mimeType: string\n url: string\n width: number\n }> = []\n\n const perCollectionConfig = resolveCollectionConfig(resolvedConfig, input.collectionSlug)\n\n // When replaceOriginal is on, the main file is already in the primary format —\n // skip it and only generate variants for the remaining formats.\n const formatsToGenerate = perCollectionConfig.replaceOriginal && perCollectionConfig.formats.length > 0\n ? perCollectionConfig.formats.slice(1)\n : perCollectionConfig.formats\n\n for (const format of formatsToGenerate) {\n const result = await convertFormat(fileBuffer, format.format, format.quality)\n const variantFilename = `${path.parse(safeFilename).name}-optimized.${format.format}`\n\n await fs.writeFile(path.join(staticDir, variantFilename), result.buffer)\n\n variants.push({\n format: format.format,\n filename: variantFilename,\n filesize: result.size,\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n url: `/api/${input.collectionSlug}/file/${variantFilename}`,\n })\n }\n\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: {\n imageOptimizer: {\n status: 'complete',\n variants,\n },\n },\n context: { imageOptimizer_skip: true },\n })\n\n return { output: { variantsGenerated: variants.length } }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n\n try {\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: {\n imageOptimizer: {\n status: 'error',\n error: errorMessage,\n },\n },\n context: { imageOptimizer_skip: true },\n })\n } catch (updateErr) {\n req.payload.logger.error(\n { err: updateErr },\n 'Failed to persist error status for image optimizer',\n )\n }\n\n throw err\n }\n }\n}\n"],"names":["fs","path","resolveCollectionConfig","convertFormat","createConvertFormatsHandler","resolvedConfig","input","req","doc","payload","findByID","collection","collectionSlug","id","docId","collectionConfig","collections","config","staticDir","upload","Error","isAbsolute","resolve","process","cwd","safeFilename","basename","filename","filePath","join","fileBuffer","readFile","variants","perCollectionConfig","formatsToGenerate","replaceOriginal","formats","length","slice","format","result","quality","variantFilename","parse","name","writeFile","buffer","push","filesize","size","width","height","mimeType","url","update","data","imageOptimizer","status","context","imageOptimizer_skip","output","variantsGenerated","err","errorMessage","message","String","error","updateErr","logger"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/tasks/convertFormats.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\n\nimport type { CollectionSlug } from 'payload'\n\nimport type { ResolvedImageOptimizerConfig } from '../types.js'\nimport { resolveCollectionConfig } from '../defaults.js'\nimport { convertFormat } from '../processing/index.js'\nimport { resolveStaticDir } from '../utilities/resolveStaticDir.js'\n\nexport const createConvertFormatsHandler = (resolvedConfig: ResolvedImageOptimizerConfig) => {\n return async ({ input, req }: { input: { collectionSlug: string; docId: string }; req: any }) => {\n try {\n const doc = await req.payload.findByID({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n })\n\n const collectionConfig = req.payload.collections[input.collectionSlug as keyof typeof req.payload.collections].config\n const staticDir = resolveStaticDir(collectionConfig)\n\n if (!staticDir) {\n throw new Error(`No staticDir configured for collection \"${input.collectionSlug}\"`)\n }\n\n // Sanitize filename to prevent path traversal\n const safeFilename = path.basename(doc.filename)\n const filePath = path.join(staticDir, safeFilename)\n const fileBuffer = await fs.readFile(filePath)\n\n const variants: Array<{\n filename: string\n filesize: number\n format: string\n height: number\n mimeType: string\n url: string\n width: number\n }> = []\n\n const perCollectionConfig = resolveCollectionConfig(resolvedConfig, input.collectionSlug)\n\n // When replaceOriginal is on, the main file is already in the primary format —\n // skip it and only generate variants for the remaining formats.\n const formatsToGenerate = perCollectionConfig.replaceOriginal && perCollectionConfig.formats.length > 0\n ? perCollectionConfig.formats.slice(1)\n : perCollectionConfig.formats\n\n for (const format of formatsToGenerate) {\n const result = await convertFormat(fileBuffer, format.format, format.quality)\n const variantFilename = `${path.parse(safeFilename).name}-optimized.${format.format}`\n\n await fs.writeFile(path.join(staticDir, variantFilename), result.buffer)\n\n variants.push({\n format: format.format,\n filename: variantFilename,\n filesize: result.size,\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n url: `/api/${input.collectionSlug}/file/${variantFilename}`,\n })\n }\n\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: {\n imageOptimizer: {\n status: 'complete',\n variants,\n },\n },\n context: { imageOptimizer_skip: true },\n })\n\n return { output: { variantsGenerated: variants.length } }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n\n try {\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: {\n imageOptimizer: {\n status: 'error',\n error: errorMessage,\n },\n },\n context: { imageOptimizer_skip: true },\n })\n } catch (updateErr) {\n req.payload.logger.error(\n { err: updateErr },\n 'Failed to persist error status for image optimizer',\n )\n }\n\n throw err\n }\n }\n}\n"],"names":["fs","path","resolveCollectionConfig","convertFormat","resolveStaticDir","createConvertFormatsHandler","resolvedConfig","input","req","doc","payload","findByID","collection","collectionSlug","id","docId","collectionConfig","collections","config","staticDir","Error","safeFilename","basename","filename","filePath","join","fileBuffer","readFile","variants","perCollectionConfig","formatsToGenerate","replaceOriginal","formats","length","slice","format","result","quality","variantFilename","parse","name","writeFile","buffer","push","filesize","size","width","height","mimeType","url","update","data","imageOptimizer","status","context","imageOptimizer_skip","output","variantsGenerated","err","errorMessage","message","String","error","updateErr","logger"],"mappings":"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"}
@@ -2,6 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { resolveCollectionConfig } from '../defaults.js';
4
4
  import { stripAndResize, generateThumbHash, convertFormat } from '../processing/index.js';
5
+ import { resolveStaticDir } from '../utilities/resolveStaticDir.js';
5
6
  export const createRegenerateDocumentHandler = (resolvedConfig)=>{
6
7
  return async ({ input, req })=>{
7
8
  try {
@@ -19,13 +20,10 @@ export const createRegenerateDocumentHandler = (resolvedConfig)=>{
19
20
  };
20
21
  }
21
22
  const collectionConfig = req.payload.collections[input.collectionSlug].config;
22
- let staticDir = typeof collectionConfig.upload === 'object' ? collectionConfig.upload.staticDir || '' : '';
23
+ const staticDir = resolveStaticDir(collectionConfig);
23
24
  if (!staticDir) {
24
25
  throw new Error(`No staticDir configured for collection "${input.collectionSlug}"`);
25
26
  }
26
- if (!path.isAbsolute(staticDir)) {
27
- staticDir = path.resolve(process.cwd(), staticDir);
28
- }
29
27
  // Sanitize filename to prevent path traversal
30
28
  const safeFilename = path.basename(doc.filename);
31
29
  const filePath = path.join(staticDir, safeFilename);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tasks/regenerateDocument.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\n\nimport type { CollectionSlug } from 'payload'\n\nimport type { ResolvedImageOptimizerConfig } from '../types.js'\nimport { resolveCollectionConfig } from '../defaults.js'\nimport { stripAndResize, generateThumbHash, convertFormat } from '../processing/index.js'\n\nexport const createRegenerateDocumentHandler = (resolvedConfig: ResolvedImageOptimizerConfig) => {\n return async ({ input, req }: { input: { collectionSlug: string; docId: string }; req: any }) => {\n try {\n const doc = await req.payload.findByID({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n })\n\n // Skip non-image documents\n if (!doc.mimeType || !doc.mimeType.startsWith('image/')) {\n return { output: { status: 'skipped', reason: 'not-image' } }\n }\n\n const collectionConfig = req.payload.collections[input.collectionSlug as keyof typeof req.payload.collections].config\n\n let staticDir: string =\n typeof collectionConfig.upload === 'object' ? collectionConfig.upload.staticDir || '' : ''\n if (!staticDir) {\n throw new Error(`No staticDir configured for collection \"${input.collectionSlug}\"`)\n }\n if (!path.isAbsolute(staticDir)) {\n staticDir = path.resolve(process.cwd(), staticDir)\n }\n\n // Sanitize filename to prevent path traversal\n const safeFilename = path.basename(doc.filename)\n const filePath = path.join(staticDir, safeFilename)\n\n let fileBuffer: Buffer\n try {\n fileBuffer = await fs.readFile(filePath)\n } catch {\n // If file not on disk, try fetching from URL\n if (doc.url) {\n const url = doc.url.startsWith('http')\n ? doc.url\n : `${process.env.NEXT_PUBLIC_SERVER_URL || ''}${doc.url}`\n const response = await fetch(url)\n fileBuffer = Buffer.from(await response.arrayBuffer())\n } else {\n throw new Error(`File not found: ${filePath}`)\n }\n }\n\n const originalSize = fileBuffer.length\n const perCollectionConfig = resolveCollectionConfig(resolvedConfig, input.collectionSlug)\n\n // Step 1: Strip metadata + resize\n const processed = await stripAndResize(\n fileBuffer,\n perCollectionConfig.maxDimensions,\n resolvedConfig.stripMetadata,\n )\n\n let mainBuffer = processed.buffer\n let mainSize = processed.size\n let newFilename = safeFilename\n let newMimeType: string | undefined\n\n // Step 1b: If replaceOriginal, convert main file to primary format\n if (perCollectionConfig.replaceOriginal && perCollectionConfig.formats.length > 0) {\n const primaryFormat = perCollectionConfig.formats[0]\n const converted = await convertFormat(processed.buffer, primaryFormat.format, primaryFormat.quality)\n mainBuffer = converted.buffer\n mainSize = converted.size\n newFilename = `${path.parse(safeFilename).name}.${primaryFormat.format}`\n newMimeType = converted.mimeType\n }\n\n // Write optimized file to disk\n const newFilePath = path.join(staticDir, newFilename)\n await fs.writeFile(newFilePath, mainBuffer)\n\n // Clean up old file if filename changed\n if (newFilename !== safeFilename) {\n await fs.unlink(filePath).catch(() => {})\n }\n\n // Step 2: Generate ThumbHash\n let thumbHash: string | undefined\n if (resolvedConfig.generateThumbHash) {\n thumbHash = await generateThumbHash(mainBuffer)\n }\n\n // Step 3: Convert to configured formats (skip primary when replaceOriginal)\n const formatsToGenerate = perCollectionConfig.replaceOriginal && perCollectionConfig.formats.length > 0\n ? perCollectionConfig.formats.slice(1)\n : perCollectionConfig.formats\n\n const variants: Array<{\n filename: string\n filesize: number\n format: string\n height: number\n mimeType: string\n url: string\n width: number\n }> = []\n\n for (const format of formatsToGenerate) {\n const result = await convertFormat(mainBuffer, format.format, format.quality)\n const variantFilename = `${path.parse(newFilename).name}-optimized.${format.format}`\n await fs.writeFile(path.join(staticDir, variantFilename), result.buffer)\n\n variants.push({\n format: format.format,\n filename: variantFilename,\n filesize: result.size,\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n url: `/api/${input.collectionSlug}/file/${variantFilename}`,\n })\n }\n\n // Step 4: Update the document with all optimization data\n const updateData: Record<string, any> = {\n imageOptimizer: {\n originalSize,\n optimizedSize: mainSize,\n status: 'complete',\n thumbHash,\n variants,\n error: null,\n },\n }\n\n // Update filename, mimeType, and filesize when replaceOriginal changed them\n if (newFilename !== safeFilename) {\n updateData.filename = newFilename\n updateData.filesize = mainSize\n updateData.mimeType = newMimeType\n }\n\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: updateData,\n context: { imageOptimizer_skip: true },\n })\n\n return { output: { status: 'complete' } }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n\n try {\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: {\n imageOptimizer: {\n status: 'error',\n error: errorMessage,\n },\n },\n context: { imageOptimizer_skip: true },\n })\n } catch (updateErr) {\n req.payload.logger.error(\n { err: updateErr },\n 'Failed to persist error status for image optimizer regeneration',\n )\n }\n\n throw err\n }\n }\n}\n"],"names":["fs","path","resolveCollectionConfig","stripAndResize","generateThumbHash","convertFormat","createRegenerateDocumentHandler","resolvedConfig","input","req","doc","payload","findByID","collection","collectionSlug","id","docId","mimeType","startsWith","output","status","reason","collectionConfig","collections","config","staticDir","upload","Error","isAbsolute","resolve","process","cwd","safeFilename","basename","filename","filePath","join","fileBuffer","readFile","url","env","NEXT_PUBLIC_SERVER_URL","response","fetch","Buffer","from","arrayBuffer","originalSize","length","perCollectionConfig","processed","maxDimensions","stripMetadata","mainBuffer","buffer","mainSize","size","newFilename","newMimeType","replaceOriginal","formats","primaryFormat","converted","format","quality","parse","name","newFilePath","writeFile","unlink","catch","thumbHash","formatsToGenerate","slice","variants","result","variantFilename","push","filesize","width","height","updateData","imageOptimizer","optimizedSize","error","update","data","context","imageOptimizer_skip","err","errorMessage","message","String","updateErr","logger"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/tasks/regenerateDocument.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\n\nimport type { CollectionSlug } from 'payload'\n\nimport type { ResolvedImageOptimizerConfig } from '../types.js'\nimport { resolveCollectionConfig } from '../defaults.js'\nimport { stripAndResize, generateThumbHash, convertFormat } from '../processing/index.js'\nimport { resolveStaticDir } from '../utilities/resolveStaticDir.js'\n\nexport const createRegenerateDocumentHandler = (resolvedConfig: ResolvedImageOptimizerConfig) => {\n return async ({ input, req }: { input: { collectionSlug: string; docId: string }; req: any }) => {\n try {\n const doc = await req.payload.findByID({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n })\n\n // Skip non-image documents\n if (!doc.mimeType || !doc.mimeType.startsWith('image/')) {\n return { output: { status: 'skipped', reason: 'not-image' } }\n }\n\n const collectionConfig = req.payload.collections[input.collectionSlug as keyof typeof req.payload.collections].config\n const staticDir = resolveStaticDir(collectionConfig)\n\n if (!staticDir) {\n throw new Error(`No staticDir configured for collection \"${input.collectionSlug}\"`)\n }\n\n // Sanitize filename to prevent path traversal\n const safeFilename = path.basename(doc.filename)\n const filePath = path.join(staticDir, safeFilename)\n\n let fileBuffer: Buffer\n try {\n fileBuffer = await fs.readFile(filePath)\n } catch {\n // If file not on disk, try fetching from URL\n if (doc.url) {\n const url = doc.url.startsWith('http')\n ? doc.url\n : `${process.env.NEXT_PUBLIC_SERVER_URL || ''}${doc.url}`\n const response = await fetch(url)\n fileBuffer = Buffer.from(await response.arrayBuffer())\n } else {\n throw new Error(`File not found: ${filePath}`)\n }\n }\n\n const originalSize = fileBuffer.length\n const perCollectionConfig = resolveCollectionConfig(resolvedConfig, input.collectionSlug)\n\n // Step 1: Strip metadata + resize\n const processed = await stripAndResize(\n fileBuffer,\n perCollectionConfig.maxDimensions,\n resolvedConfig.stripMetadata,\n )\n\n let mainBuffer = processed.buffer\n let mainSize = processed.size\n let newFilename = safeFilename\n let newMimeType: string | undefined\n\n // Step 1b: If replaceOriginal, convert main file to primary format\n if (perCollectionConfig.replaceOriginal && perCollectionConfig.formats.length > 0) {\n const primaryFormat = perCollectionConfig.formats[0]\n const converted = await convertFormat(processed.buffer, primaryFormat.format, primaryFormat.quality)\n mainBuffer = converted.buffer\n mainSize = converted.size\n newFilename = `${path.parse(safeFilename).name}.${primaryFormat.format}`\n newMimeType = converted.mimeType\n }\n\n // Write optimized file to disk\n const newFilePath = path.join(staticDir, newFilename)\n await fs.writeFile(newFilePath, mainBuffer)\n\n // Clean up old file if filename changed\n if (newFilename !== safeFilename) {\n await fs.unlink(filePath).catch(() => {})\n }\n\n // Step 2: Generate ThumbHash\n let thumbHash: string | undefined\n if (resolvedConfig.generateThumbHash) {\n thumbHash = await generateThumbHash(mainBuffer)\n }\n\n // Step 3: Convert to configured formats (skip primary when replaceOriginal)\n const formatsToGenerate = perCollectionConfig.replaceOriginal && perCollectionConfig.formats.length > 0\n ? perCollectionConfig.formats.slice(1)\n : perCollectionConfig.formats\n\n const variants: Array<{\n filename: string\n filesize: number\n format: string\n height: number\n mimeType: string\n url: string\n width: number\n }> = []\n\n for (const format of formatsToGenerate) {\n const result = await convertFormat(mainBuffer, format.format, format.quality)\n const variantFilename = `${path.parse(newFilename).name}-optimized.${format.format}`\n await fs.writeFile(path.join(staticDir, variantFilename), result.buffer)\n\n variants.push({\n format: format.format,\n filename: variantFilename,\n filesize: result.size,\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n url: `/api/${input.collectionSlug}/file/${variantFilename}`,\n })\n }\n\n // Step 4: Update the document with all optimization data\n const updateData: Record<string, any> = {\n imageOptimizer: {\n originalSize,\n optimizedSize: mainSize,\n status: 'complete',\n thumbHash,\n variants,\n error: null,\n },\n }\n\n // Update filename, mimeType, and filesize when replaceOriginal changed them\n if (newFilename !== safeFilename) {\n updateData.filename = newFilename\n updateData.filesize = mainSize\n updateData.mimeType = newMimeType\n }\n\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: updateData,\n context: { imageOptimizer_skip: true },\n })\n\n return { output: { status: 'complete' } }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err)\n\n try {\n await req.payload.update({\n collection: input.collectionSlug as CollectionSlug,\n id: input.docId,\n data: {\n imageOptimizer: {\n status: 'error',\n error: errorMessage,\n },\n },\n context: { imageOptimizer_skip: true },\n })\n } catch (updateErr) {\n req.payload.logger.error(\n { err: updateErr },\n 'Failed to persist error status for image optimizer regeneration',\n )\n }\n\n throw err\n }\n }\n}\n"],"names":["fs","path","resolveCollectionConfig","stripAndResize","generateThumbHash","convertFormat","resolveStaticDir","createRegenerateDocumentHandler","resolvedConfig","input","req","doc","payload","findByID","collection","collectionSlug","id","docId","mimeType","startsWith","output","status","reason","collectionConfig","collections","config","staticDir","Error","safeFilename","basename","filename","filePath","join","fileBuffer","readFile","url","process","env","NEXT_PUBLIC_SERVER_URL","response","fetch","Buffer","from","arrayBuffer","originalSize","length","perCollectionConfig","processed","maxDimensions","stripMetadata","mainBuffer","buffer","mainSize","size","newFilename","newMimeType","replaceOriginal","formats","primaryFormat","converted","format","quality","parse","name","newFilePath","writeFile","unlink","catch","thumbHash","formatsToGenerate","slice","variants","result","variantFilename","push","filesize","width","height","updateData","imageOptimizer","optimizedSize","error","update","data","context","imageOptimizer_skip","err","errorMessage","message","String","updateErr","logger"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const translations: Record<string, Record<string, Record<string, string>>>;
@@ -0,0 +1,64 @@
1
+ export const translations = {
2
+ en: {
3
+ 'plugin-imageOptimizer': {
4
+ noData: 'No optimization data yet. Upload an image to optimize.',
5
+ blurPreview: 'Blur Preview',
6
+ variants: 'Variants',
7
+ original: 'Original',
8
+ optimized: 'Optimized',
9
+ regenerateImages: 'Regenerate Images',
10
+ regenerating: 'Regenerating...',
11
+ forceReprocess: 'Force re-process all',
12
+ allOptimized: 'All images already optimized.',
13
+ stallMessage: 'Process stalled. {{count}} image(s) failed to process.',
14
+ complete: 'complete',
15
+ errors: 'errors',
16
+ done: 'Done!',
17
+ optimizedCount: '{{complete}}/{{total}} optimized',
18
+ failed: '{{count}} failed.',
19
+ allImagesOptimized: 'All {{count}} images optimized'
20
+ }
21
+ },
22
+ de: {
23
+ 'plugin-imageOptimizer': {
24
+ noData: 'Noch keine Optimierungsdaten. Laden Sie ein Bild hoch.',
25
+ blurPreview: 'Unschärfe-Vorschau',
26
+ variants: 'Varianten',
27
+ original: 'Original',
28
+ optimized: 'Optimiert',
29
+ regenerateImages: 'Bilder regenerieren',
30
+ regenerating: 'Regenerierung...',
31
+ forceReprocess: 'Alle neu verarbeiten',
32
+ allOptimized: 'Alle Bilder sind bereits optimiert.',
33
+ stallMessage: 'Prozess gestoppt. {{count}} Bild(er) konnten nicht verarbeitet werden.',
34
+ complete: 'abgeschlossen',
35
+ errors: 'Fehler',
36
+ done: 'Fertig!',
37
+ optimizedCount: '{{complete}}/{{total}} optimiert',
38
+ failed: '{{count}} fehlgeschlagen.',
39
+ allImagesOptimized: 'Alle {{count}} Bilder optimiert'
40
+ }
41
+ },
42
+ fr: {
43
+ 'plugin-imageOptimizer': {
44
+ noData: "Aucune donnée d'optimisation. Téléchargez une image.",
45
+ blurPreview: 'Aperçu flou',
46
+ variants: 'Variantes',
47
+ original: 'Original',
48
+ optimized: 'Optimisé',
49
+ regenerateImages: 'Régénérer les images',
50
+ regenerating: 'Régénération...',
51
+ forceReprocess: 'Forcer le retraitement',
52
+ allOptimized: 'Toutes les images sont déjà optimisées.',
53
+ stallMessage: 'Processus bloqué. {{count}} image(s) non traitée(s).',
54
+ complete: 'terminé',
55
+ errors: 'erreurs',
56
+ done: 'Terminé !',
57
+ optimizedCount: '{{complete}}/{{total}} optimisé(s)',
58
+ failed: '{{count}} échoué(s).',
59
+ allImagesOptimized: 'Les {{count}} images sont optimisées'
60
+ }
61
+ }
62
+ };
63
+
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/translations/index.ts"],"sourcesContent":["export const translations = {\n en: {\n 'plugin-imageOptimizer': {\n noData: 'No optimization data yet. Upload an image to optimize.',\n blurPreview: 'Blur Preview',\n variants: 'Variants',\n original: 'Original',\n optimized: 'Optimized',\n regenerateImages: 'Regenerate Images',\n regenerating: 'Regenerating...',\n forceReprocess: 'Force re-process all',\n allOptimized: 'All images already optimized.',\n stallMessage: 'Process stalled. {{count}} image(s) failed to process.',\n complete: 'complete',\n errors: 'errors',\n done: 'Done!',\n optimizedCount: '{{complete}}/{{total}} optimized',\n failed: '{{count}} failed.',\n allImagesOptimized: 'All {{count}} images optimized',\n },\n },\n de: {\n 'plugin-imageOptimizer': {\n noData: 'Noch keine Optimierungsdaten. Laden Sie ein Bild hoch.',\n blurPreview: 'Unschärfe-Vorschau',\n variants: 'Varianten',\n original: 'Original',\n optimized: 'Optimiert',\n regenerateImages: 'Bilder regenerieren',\n regenerating: 'Regenerierung...',\n forceReprocess: 'Alle neu verarbeiten',\n allOptimized: 'Alle Bilder sind bereits optimiert.',\n stallMessage: 'Prozess gestoppt. {{count}} Bild(er) konnten nicht verarbeitet werden.',\n complete: 'abgeschlossen',\n errors: 'Fehler',\n done: 'Fertig!',\n optimizedCount: '{{complete}}/{{total}} optimiert',\n failed: '{{count}} fehlgeschlagen.',\n allImagesOptimized: 'Alle {{count}} Bilder optimiert',\n },\n },\n fr: {\n 'plugin-imageOptimizer': {\n noData: \"Aucune donnée d'optimisation. Téléchargez une image.\",\n blurPreview: 'Aperçu flou',\n variants: 'Variantes',\n original: 'Original',\n optimized: 'Optimisé',\n regenerateImages: 'Régénérer les images',\n regenerating: 'Régénération...',\n forceReprocess: 'Forcer le retraitement',\n allOptimized: 'Toutes les images sont déjà optimisées.',\n stallMessage: 'Processus bloqué. {{count}} image(s) non traitée(s).',\n complete: 'terminé',\n errors: 'erreurs',\n done: 'Terminé !',\n optimizedCount: '{{complete}}/{{total}} optimisé(s)',\n failed: '{{count}} échoué(s).',\n allImagesOptimized: 'Les {{count}} images sont optimisées',\n },\n },\n} as Record<string, Record<string, Record<string, string>>>\n"],"names":["translations","en","noData","blurPreview","variants","original","optimized","regenerateImages","regenerating","forceReprocess","allOptimized","stallMessage","complete","errors","done","optimizedCount","failed","allImagesOptimized","de","fr"],"mappings":"AAAA,OAAO,MAAMA,eAAe;IAC1BC,IAAI;QACF,yBAAyB;YACvBC,QAAQ;YACRC,aAAa;YACbC,UAAU;YACVC,UAAU;YACVC,WAAW;YACXC,kBAAkB;YAClBC,cAAc;YACdC,gBAAgB;YAChBC,cAAc;YACdC,cAAc;YACdC,UAAU;YACVC,QAAQ;YACRC,MAAM;YACNC,gBAAgB;YAChBC,QAAQ;YACRC,oBAAoB;QACtB;IACF;IACAC,IAAI;QACF,yBAAyB;YACvBhB,QAAQ;YACRC,aAAa;YACbC,UAAU;YACVC,UAAU;YACVC,WAAW;YACXC,kBAAkB;YAClBC,cAAc;YACdC,gBAAgB;YAChBC,cAAc;YACdC,cAAc;YACdC,UAAU;YACVC,QAAQ;YACRC,MAAM;YACNC,gBAAgB;YAChBC,QAAQ;YACRC,oBAAoB;QACtB;IACF;IACAE,IAAI;QACF,yBAAyB;YACvBjB,QAAQ;YACRC,aAAa;YACbC,UAAU;YACVC,UAAU;YACVC,WAAW;YACXC,kBAAkB;YAClBC,cAAc;YACdC,gBAAgB;YAChBC,cAAc;YACdC,cAAc;YACdC,UAAU;YACVC,QAAQ;YACRC,MAAM;YACNC,gBAAgB;YAChBC,QAAQ;YACRC,oBAAoB;QACtB;IACF;AACF,EAA2D"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CollectionSlug } from 'payload';
1
+ import type { CollectionSlug, Field } from 'payload';
2
2
  export type ImageFormat = 'webp' | 'avif';
3
3
  export type FormatQuality = {
4
4
  format: ImageFormat;
@@ -12,9 +12,13 @@ export type CollectionOptimizerConfig = {
12
12
  };
13
13
  replaceOriginal?: boolean;
14
14
  };
15
+ export type FieldsOverride = (args: {
16
+ defaultFields: Field[];
17
+ }) => Field[];
15
18
  export type ImageOptimizerConfig = {
16
19
  collections: Partial<Record<CollectionSlug, true | CollectionOptimizerConfig>>;
17
20
  disabled?: boolean;
21
+ fieldsOverride?: FieldsOverride;
18
22
  formats?: FormatQuality[];
19
23
  generateThumbHash?: boolean;
20
24
  maxDimensions?: {
@@ -37,3 +41,17 @@ export type ResolvedImageOptimizerConfig = Required<Pick<ImageOptimizerConfig, '
37
41
  disabled: boolean;
38
42
  replaceOriginal: boolean;
39
43
  };
44
+ export type ImageOptimizerData = {
45
+ thumbHash?: string | null;
46
+ };
47
+ export type MediaResource = {
48
+ url?: string | null;
49
+ alt?: string | null;
50
+ width?: number | null;
51
+ height?: number | null;
52
+ filename?: string | null;
53
+ focalX?: number | null;
54
+ focalY?: number | null;
55
+ imageOptimizer?: ImageOptimizerData | null;
56
+ updatedAt?: string;
57
+ };
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug } from 'payload'\n\nexport type ImageFormat = 'webp' | 'avif'\n\nexport type FormatQuality = {\n format: ImageFormat\n quality: number // 1-100\n}\n\nexport type CollectionOptimizerConfig = {\n formats?: FormatQuality[]\n maxDimensions?: { width: number; height: number }\n replaceOriginal?: boolean\n}\n\nexport type ImageOptimizerConfig = {\n collections: Partial<Record<CollectionSlug, true | CollectionOptimizerConfig>>\n disabled?: boolean\n formats?: FormatQuality[]\n generateThumbHash?: boolean\n maxDimensions?: { width: number; height: number }\n replaceOriginal?: boolean\n stripMetadata?: boolean\n}\n\nexport type ResolvedCollectionOptimizerConfig = {\n formats: FormatQuality[]\n maxDimensions: { width: number; height: number }\n replaceOriginal: boolean\n}\n\nexport type ResolvedImageOptimizerConfig = Required<\n Pick<ImageOptimizerConfig, 'formats' | 'generateThumbHash' | 'maxDimensions' | 'stripMetadata'>\n> & {\n collections: ImageOptimizerConfig['collections']\n disabled: boolean\n replaceOriginal: boolean\n}\n"],"names":[],"mappings":"AA+BA,WAMC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, Field } from 'payload'\n\nexport type ImageFormat = 'webp' | 'avif'\n\nexport type FormatQuality = {\n format: ImageFormat\n quality: number // 1-100\n}\n\nexport type CollectionOptimizerConfig = {\n formats?: FormatQuality[]\n maxDimensions?: { width: number; height: number }\n replaceOriginal?: boolean\n}\n\nexport type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]\n\nexport type ImageOptimizerConfig = {\n collections: Partial<Record<CollectionSlug, true | CollectionOptimizerConfig>>\n disabled?: boolean\n fieldsOverride?: FieldsOverride\n formats?: FormatQuality[]\n generateThumbHash?: boolean\n maxDimensions?: { width: number; height: number }\n replaceOriginal?: boolean\n stripMetadata?: boolean\n}\n\nexport type ResolvedCollectionOptimizerConfig = {\n formats: FormatQuality[]\n maxDimensions: { width: number; height: number }\n replaceOriginal: boolean\n}\n\nexport type ResolvedImageOptimizerConfig = Required<\n Pick<ImageOptimizerConfig, 'formats' | 'generateThumbHash' | 'maxDimensions' | 'stripMetadata'>\n> & {\n collections: ImageOptimizerConfig['collections']\n disabled: boolean\n replaceOriginal: boolean\n}\n\nexport type ImageOptimizerData = {\n thumbHash?: string | null\n}\n\nexport type MediaResource = {\n url?: string | null\n alt?: string | null\n width?: number | null\n height?: number | null\n filename?: string | null\n focalX?: number | null\n focalY?: number | null\n imageOptimizer?: ImageOptimizerData | null\n updatedAt?: string\n}\n"],"names":[],"mappings":"AA8CA,WAUC"}
@@ -1,11 +1,4 @@
1
- type ImageOptimizerData = {
2
- thumbHash?: string | null;
3
- };
4
- type ResourceWithOptimizer = {
5
- imageOptimizer?: ImageOptimizerData | null;
6
- focalX?: number | null;
7
- focalY?: number | null;
8
- };
1
+ import type { MediaResource } from '../types.js';
9
2
  export type ImageOptimizerProps = {
10
3
  placeholder: 'blur' | 'empty';
11
4
  blurDataURL?: string;
@@ -28,5 +21,4 @@ export type ImageOptimizerProps = {
28
21
  * <NextImage {...existingProps} {...optimizerProps} />
29
22
  * ```
30
23
  */
31
- export declare function getImageOptimizerProps(resource: ResourceWithOptimizer | null | undefined): ImageOptimizerProps;
32
- export {};
24
+ export declare function getImageOptimizerProps(resource: MediaResource | null | undefined): ImageOptimizerProps;
@@ -1 +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"}
1
+ {"version":3,"sources":["../../src/utilities/getImageOptimizerProps.ts"],"sourcesContent":["import { thumbHashToDataURL } from 'thumbhash'\n\nimport type { MediaResource } from '../types.js'\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: MediaResource | 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;AAY9C;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,uBAAuBC,QAA0C;IAC/E,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,3 @@
1
+ export declare function resolveStaticDir(collectionConfig: {
2
+ upload?: boolean | Record<string, any>;
3
+ }): string;