@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.
- package/dist/components/ImageBox.d.ts +1 -15
- package/dist/components/ImageBox.js.map +1 -1
- package/dist/components/OptimizationStatus.js +84 -19
- package/dist/components/OptimizationStatus.js.map +1 -1
- package/dist/fields/imageOptimizerField.d.ts +4 -2
- package/dist/fields/imageOptimizerField.js +57 -54
- package/dist/fields/imageOptimizerField.js.map +1 -1
- package/dist/hooks/afterChange.js +2 -4
- package/dist/hooks/afterChange.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +123 -105
- package/dist/index.js.map +1 -1
- package/dist/tasks/convertFormats.js +2 -4
- package/dist/tasks/convertFormats.js.map +1 -1
- package/dist/tasks/regenerateDocument.js +2 -4
- package/dist/tasks/regenerateDocument.js.map +1 -1
- package/dist/translations/index.d.ts +1 -0
- package/dist/translations/index.js +64 -0
- package/dist/translations/index.js.map +1 -0
- package/dist/types.d.ts +19 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities/getImageOptimizerProps.d.ts +2 -10
- package/dist/utilities/getImageOptimizerProps.js.map +1 -1
- package/dist/utilities/resolveStaticDir.d.ts +3 -0
- package/dist/utilities/resolveStaticDir.js +10 -0
- package/dist/utilities/resolveStaticDir.js.map +1 -0
- package/package.json +3 -2
- package/src/components/ImageBox.tsx +1 -16
- package/src/components/OptimizationStatus.tsx +99 -20
- package/src/fields/imageOptimizerField.ts +65 -59
- package/src/hooks/afterChange.ts +2 -6
- package/src/index.ts +94 -95
- package/src/tasks/convertFormats.ts +2 -5
- package/src/tasks/regenerateDocument.ts +2 -5
- package/src/translations/index.ts +62 -0
- package/src/types.ts +20 -1
- package/src/utilities/getImageOptimizerProps.ts +2 -10
- 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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
102
|
-
|
|
132
|
+
path: '/image-optimizer/regenerate',
|
|
133
|
+
method: 'post',
|
|
134
|
+
handler: createRegenerateHandler(resolvedConfig)
|
|
103
135
|
},
|
|
104
136
|
{
|
|
105
|
-
|
|
106
|
-
|
|
137
|
+
path: '/image-optimizer/regenerate',
|
|
138
|
+
method: 'get',
|
|
139
|
+
handler: createRegenerateStatusHandler(resolvedConfig)
|
|
107
140
|
}
|
|
108
|
-
]
|
|
109
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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":"
|
|
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
|
|
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:
|
|
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\
|
|
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"}
|