@ecopages/image-processor 0.2.0-beta.9 → 0.2.0-rc.1
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/README.md +22 -3
- package/package.json +20 -13
- package/src/bun-plugins.js +48 -29
- package/src/component/html.js +12 -6
- package/src/component/jsx.d.ts +12 -0
- package/src/component/jsx.js +18 -0
- package/src/component/react.js +17 -10
- package/src/constants.js +5 -4
- package/src/image-plugins.js +48 -29
- package/src/image-processor.js +147 -135
- package/src/image-renderer.js +222 -220
- package/src/image-runtime-state.d.ts +20 -0
- package/src/image-runtime-state.js +59 -0
- package/src/image-types.d.ts +3 -0
- package/src/image-utils.js +109 -98
- package/src/index.js +3 -3
- package/src/plugin.d.ts +30 -10
- package/src/plugin.js +315 -271
- package/src/types.js +1 -0
- package/src/utils.js +24 -25
package/src/plugin.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
const logger = new Logger("[@ecopages/image-processor]", {
|
|
18
|
-
debug: process.env.ECOPAGES_LOGGER_DEBUG === "true"
|
|
1
|
+
/**
|
|
2
|
+
* ImageProcessorPlugin
|
|
3
|
+
* @module @ecopages/image-processor
|
|
4
|
+
*/
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileSystem } from '@ecopages/file-system';
|
|
7
|
+
import { mergeProcessorOptions, Processor, resolveGeneratedPath, writeGeneratedTypesPackage, } from '@ecopages/core/plugins/processor';
|
|
8
|
+
import { isLitStaticRenderWorkerThread } from '@ecopages/core/build/lit-static-render-worker-context';
|
|
9
|
+
import { Logger } from '@ecopages/logger';
|
|
10
|
+
import { createImagePlugin, createImagePluginBundler } from './image-plugins.js';
|
|
11
|
+
import { ImageProcessor } from './image-processor.js';
|
|
12
|
+
import { getSourceImagePaths, loadProcessedImagesFromDisk } from './image-runtime-state.js';
|
|
13
|
+
import { anyCaseToCamelCase } from './utils.js';
|
|
14
|
+
const logger = new Logger('[@ecopages/image-processor]', {
|
|
15
|
+
debug: process.env.ECOPAGES_LOGGER_DEBUG === 'true',
|
|
19
16
|
});
|
|
20
17
|
const IMAGE_VIRTUAL_MODULE_TYPES = `/**
|
|
21
18
|
* ImageAttributes
|
|
@@ -62,230 +59,284 @@ type ImageSize = {
|
|
|
62
59
|
width: number;
|
|
63
60
|
label: string;
|
|
64
61
|
};`;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
get plugins() {
|
|
97
|
-
return [createImagePlugin(this.processedImages)];
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Replaces image-map contents without swapping the backing object.
|
|
101
|
-
*
|
|
102
|
-
* @remarks
|
|
103
|
-
* The build/runtime virtual-module plugins close over `processedImages`, so
|
|
104
|
-
* mutating the existing object keeps those plugins live after preparation.
|
|
105
|
-
*/
|
|
106
|
-
replaceProcessedImages(images) {
|
|
107
|
-
for (const key of Object.keys(this.processedImages)) {
|
|
108
|
-
delete this.processedImages[key];
|
|
109
|
-
}
|
|
110
|
-
Object.assign(this.processedImages, images);
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Prepares the image virtual-module state before config build seals the app
|
|
114
|
-
* manifest.
|
|
115
|
-
*/
|
|
116
|
-
async prepareBuildContributions() {
|
|
117
|
-
if (this.buildContributionsPrepared) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (process.env.ECOPAGES_LIT_STATIC_RENDER_WORKER === "true") {
|
|
121
|
-
this.buildContributionsPrepared = true;
|
|
122
|
-
return;
|
|
62
|
+
/**
|
|
63
|
+
* ImageProcessorPlugin
|
|
64
|
+
* A Processor for optimizing images.
|
|
65
|
+
*/
|
|
66
|
+
export class ImageProcessorPlugin extends Processor {
|
|
67
|
+
buildContributionsPrepared = false;
|
|
68
|
+
resolvedConfig;
|
|
69
|
+
processedImages = {};
|
|
70
|
+
constructor(config) {
|
|
71
|
+
const acceptedFormats = config.options?.acceptedFormats ?? ['jpg', 'jpeg', 'png', 'webp'];
|
|
72
|
+
const extensionPatterns = acceptedFormats.map((format) => `*.${format.replace(/^\./, '')}`);
|
|
73
|
+
const defaultWatchConfig = {
|
|
74
|
+
paths: [],
|
|
75
|
+
extensions: acceptedFormats,
|
|
76
|
+
onCreate: async (ctx) => this.process([ctx.path]),
|
|
77
|
+
onChange: async (ctx) => this.process([ctx.path]),
|
|
78
|
+
onDelete: async (ctx) => this.deleteProcessedImagesbyPath(ctx.path),
|
|
79
|
+
onError: (error) => logger.error('Watcher error', { error }),
|
|
80
|
+
};
|
|
81
|
+
super({
|
|
82
|
+
...config,
|
|
83
|
+
name: 'ecopages-image-processor',
|
|
84
|
+
description: 'A Processor for optimizing images.',
|
|
85
|
+
capabilities: [
|
|
86
|
+
{
|
|
87
|
+
kind: 'image',
|
|
88
|
+
extensions: extensionPatterns,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
watch: config.watch ? mergeProcessorOptions(defaultWatchConfig, config.watch) : defaultWatchConfig,
|
|
92
|
+
});
|
|
123
93
|
}
|
|
124
|
-
|
|
125
|
-
|
|
94
|
+
get buildPlugins() {
|
|
95
|
+
return [createImagePluginBundler(this.processedImages)];
|
|
126
96
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
distDir: this.context.distDir
|
|
130
|
-
});
|
|
131
|
-
const defaultConfig = {
|
|
132
|
-
sourceDir: `${this.context.srcDir}/public/assets/images`,
|
|
133
|
-
outputDir: `${this.context.distDir}/assets/optimized`,
|
|
134
|
-
publicPath: "/assets/optimized",
|
|
135
|
-
sizes: [],
|
|
136
|
-
quality: 80,
|
|
137
|
-
format: "webp"
|
|
138
|
-
};
|
|
139
|
-
const config = this.options ? mergeProcessorOptions(defaultConfig, this.options) : defaultConfig;
|
|
140
|
-
this.resolvedConfig = config;
|
|
141
|
-
this.processor = new ImageProcessor(config, {
|
|
142
|
-
readCache: (key) => this.readCache(key),
|
|
143
|
-
writeCache: (key, data) => this.writeCache(key, data)
|
|
144
|
-
});
|
|
145
|
-
this.replaceProcessedImages(await this.processor.processDirectory());
|
|
146
|
-
if (this.watchConfig) {
|
|
147
|
-
this.watchConfig.paths = [config.sourceDir];
|
|
97
|
+
get plugins() {
|
|
98
|
+
return [createImagePlugin(this.processedImages)];
|
|
148
99
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Replaces image-map contents without swapping the backing object.
|
|
102
|
+
*
|
|
103
|
+
* @remarks
|
|
104
|
+
* The build/runtime virtual-module plugins close over `processedImages`, so
|
|
105
|
+
* mutating the existing object keeps those plugins live after preparation.
|
|
106
|
+
*/
|
|
107
|
+
replaceProcessedImages(images) {
|
|
108
|
+
for (const key of Object.keys(this.processedImages)) {
|
|
109
|
+
delete this.processedImages[key];
|
|
110
|
+
}
|
|
111
|
+
Object.assign(this.processedImages, images);
|
|
156
112
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Resolves processor config and creates the sharp-backed worker instance.
|
|
115
|
+
*/
|
|
116
|
+
initializeProcessor() {
|
|
117
|
+
if (this.processor && this.resolvedConfig) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!this.context) {
|
|
121
|
+
throw new Error('ImageProcessor requires context to be set');
|
|
122
|
+
}
|
|
123
|
+
logger.debug('Setting up image processor', {
|
|
124
|
+
srcDir: this.context.srcDir,
|
|
125
|
+
distDir: this.context.distDir,
|
|
126
|
+
});
|
|
127
|
+
const defaultConfig = {
|
|
128
|
+
sourceDir: `${this.context.srcDir}/public/assets/images`,
|
|
129
|
+
outputDir: `${this.context.distDir}/assets/optimized`,
|
|
130
|
+
publicPath: '/assets/optimized',
|
|
131
|
+
sizes: [],
|
|
132
|
+
quality: 80,
|
|
133
|
+
format: 'webp',
|
|
134
|
+
};
|
|
135
|
+
const config = this.options ? mergeProcessorOptions(defaultConfig, this.options) : defaultConfig;
|
|
136
|
+
this.resolvedConfig = config;
|
|
137
|
+
this.processor = new ImageProcessor(config, {
|
|
138
|
+
readCache: (key) => this.readCache(key),
|
|
139
|
+
writeCache: (key, data) => this.writeCache(key, data),
|
|
140
|
+
});
|
|
141
|
+
if (this.watchConfig) {
|
|
142
|
+
this.watchConfig.paths = [config.sourceDir];
|
|
143
|
+
}
|
|
166
144
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Prepares build-facing processor state before config build seals the app manifest.
|
|
147
|
+
*
|
|
148
|
+
* @remarks
|
|
149
|
+
* Image processing stays in {@link setup}; this hook only initializes config and
|
|
150
|
+
* restores any previously generated virtual-module state from disk.
|
|
151
|
+
*/
|
|
152
|
+
async prepareBuildContributions() {
|
|
153
|
+
if (this.buildContributionsPrepared) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Lit SSR workers import eco.config in an isolated thread; image outputs are
|
|
157
|
+
// already prepared on the main dev server thread.
|
|
158
|
+
if (isLitStaticRenderWorkerThread()) {
|
|
159
|
+
this.buildContributionsPrepared = true;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
this.initializeProcessor();
|
|
163
|
+
await this.generateSourceTypes();
|
|
164
|
+
await this.syncProcessedImagesFromDisk();
|
|
165
|
+
this.buildContributionsPrepared = true;
|
|
172
166
|
}
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
getRuntimeVirtualModulePath() {
|
|
168
|
+
if (!this.context) {
|
|
169
|
+
throw new Error('ImageProcessor requires context to be set');
|
|
170
|
+
}
|
|
171
|
+
return resolveGeneratedPath('cache', {
|
|
172
|
+
root: this.context.distDir,
|
|
173
|
+
module: this.name,
|
|
174
|
+
subPath: 'virtual-module.ts',
|
|
175
|
+
});
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
writeGeneratedFile(filePath, content) {
|
|
182
|
-
fileSystem.ensureDir(path.dirname(filePath));
|
|
183
|
-
if (fileSystem.exists(filePath) && fileSystem.readFileSync(filePath) === content) {
|
|
184
|
-
return;
|
|
177
|
+
getGeneratedOutputPath(src) {
|
|
178
|
+
if (!this.resolvedConfig) {
|
|
179
|
+
throw new Error('ImageProcessor not initialized');
|
|
180
|
+
}
|
|
181
|
+
return path.join(this.resolvedConfig.outputDir, path.basename(src));
|
|
185
182
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Restores in-memory image state from previously generated dist artifacts.
|
|
185
|
+
*
|
|
186
|
+
* @returns `true` when state was restored without invoking sharp.
|
|
187
|
+
*/
|
|
188
|
+
async syncProcessedImagesFromDisk() {
|
|
189
|
+
if (!this.resolvedConfig) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
const sourceImages = await getSourceImagePaths(this.resolvedConfig);
|
|
193
|
+
const imageMap = await loadProcessedImagesFromDisk({
|
|
194
|
+
resolvedConfig: this.resolvedConfig,
|
|
195
|
+
runtimeVirtualModulePath: this.getRuntimeVirtualModulePath(),
|
|
196
|
+
});
|
|
197
|
+
if (imageMap === null) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
if (sourceImages.length > 0 && Object.keys(imageMap).length !== sourceImages.length) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
this.replaceProcessedImages(imageMap);
|
|
204
|
+
if (Object.keys(imageMap).length > 0) {
|
|
205
|
+
this.generateTypes();
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
191
208
|
}
|
|
192
|
-
|
|
193
|
-
|
|
209
|
+
async ensureGeneratedOutputs() {
|
|
210
|
+
if (!this.processor) {
|
|
211
|
+
throw new Error('ImageProcessor not initialized');
|
|
212
|
+
}
|
|
213
|
+
if (await this.syncProcessedImagesFromDisk()) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
this.replaceProcessedImages(await this.processor.processDirectory());
|
|
217
|
+
this.generateTypes();
|
|
194
218
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
* It is ossible to define which one should be included in the final bundle based on the environment.
|
|
202
|
-
* @returns
|
|
203
|
-
*/
|
|
204
|
-
generateDependencies() {
|
|
205
|
-
const deps = [];
|
|
206
|
-
if (process.env.NODE_ENV === "development") {
|
|
219
|
+
writeGeneratedFile(filePath, content) {
|
|
220
|
+
fileSystem.ensureDir(path.dirname(filePath));
|
|
221
|
+
if (fileSystem.exists(filePath) && fileSystem.readFileSync(filePath) === content) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
fileSystem.write(filePath, content);
|
|
207
225
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Generates virtual-module declarations from source filenames before image
|
|
228
|
+
* processing runs.
|
|
229
|
+
*
|
|
230
|
+
* @remarks
|
|
231
|
+
* Editors need the ambient `ecopages:images` module during config build, but
|
|
232
|
+
* actual image optimization is deferred until {@link setup}. Declaring exports
|
|
233
|
+
* from the source directory preserves typed named imports on a clean checkout.
|
|
234
|
+
*/
|
|
235
|
+
async generateSourceTypes() {
|
|
236
|
+
if (!this.resolvedConfig) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const sourceImages = await getSourceImagePaths(this.resolvedConfig);
|
|
240
|
+
this.writeVirtualModuleTypes(sourceImages.map((imagePath) => path.basename(imagePath)));
|
|
216
241
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
throw new Error("ImageProcessor not initialized");
|
|
242
|
+
/**
|
|
243
|
+
* Ensures optimized image outputs and virtual-module state exist on disk.
|
|
244
|
+
*/
|
|
245
|
+
async setup() {
|
|
246
|
+
if (isLitStaticRenderWorkerThread()) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.initializeProcessor();
|
|
250
|
+
await this.ensureGeneratedOutputs();
|
|
227
251
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Process images.
|
|
254
|
+
* @param images
|
|
255
|
+
*/
|
|
256
|
+
async process(images) {
|
|
257
|
+
if (!this.processor) {
|
|
258
|
+
throw new Error('ImageProcessor not initialized');
|
|
259
|
+
}
|
|
260
|
+
logger.debug('Processing images', { count: images.length });
|
|
261
|
+
for (const image of images) {
|
|
262
|
+
try {
|
|
263
|
+
const result = await this.processor.processImage(image);
|
|
264
|
+
if (result) {
|
|
265
|
+
this.processedImages[path.basename(image)] = result;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
logger.error('Failed to process image', { image, error });
|
|
270
|
+
}
|
|
234
271
|
}
|
|
235
|
-
|
|
236
|
-
logger.error("Failed to process image", { image, error });
|
|
237
|
-
}
|
|
272
|
+
this.generateTypes();
|
|
238
273
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Delete processed images using the original image path.
|
|
276
|
+
* @param imagePath
|
|
277
|
+
*/
|
|
278
|
+
async deleteProcessedImagesbyPath(imagePath) {
|
|
279
|
+
if (!this.processor) {
|
|
280
|
+
throw new Error('ImageProcessor not initialized');
|
|
281
|
+
}
|
|
282
|
+
logger.debug('Deleting processed images', { path: imagePath });
|
|
283
|
+
try {
|
|
284
|
+
const baseNameWithoutExt = path.basename(imagePath, path.extname(imagePath));
|
|
285
|
+
if (!this.options) {
|
|
286
|
+
throw new Error('Options not set');
|
|
287
|
+
}
|
|
288
|
+
const outputDir = this.options.outputDir;
|
|
289
|
+
const files = await fileSystem.glob([`${outputDir}/${baseNameWithoutExt}-*`]);
|
|
290
|
+
await Promise.all(files.map(async (file) => {
|
|
291
|
+
try {
|
|
292
|
+
await fileSystem.removeAsync(file);
|
|
293
|
+
logger.debug('Deleted processed image', { file });
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
logger.error('Failed to delete processed image', { file, error });
|
|
297
|
+
}
|
|
298
|
+
}));
|
|
299
|
+
try {
|
|
300
|
+
const cacheKey = this.processedImages[path.basename(imagePath)].cacheKey;
|
|
301
|
+
const cachePath = this.getCachePath(cacheKey);
|
|
302
|
+
await fileSystem.removeAsync(cachePath);
|
|
303
|
+
logger.debug('Deleted cache file for image', { cachePath });
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
logger.error('Failed to delete cache file for image', { imagePath, error });
|
|
307
|
+
}
|
|
308
|
+
delete this.processedImages[path.basename(imagePath)];
|
|
309
|
+
this.generateTypes();
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
logger.error('Failed to delete processed images', { path: imagePath, error });
|
|
313
|
+
}
|
|
248
314
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
315
|
+
/**
|
|
316
|
+
* Generate types for the virtual module.
|
|
317
|
+
*/
|
|
318
|
+
generateTypes() {
|
|
319
|
+
this.writeVirtualModuleTypes(Object.keys(this.processedImages));
|
|
320
|
+
if (!this.context)
|
|
321
|
+
throw new Error('Processor is not configured correctly');
|
|
322
|
+
const runtimeVirtualModulePath = resolveGeneratedPath('cache', {
|
|
323
|
+
root: this.context.distDir,
|
|
324
|
+
module: this.name,
|
|
325
|
+
subPath: 'virtual-module.ts',
|
|
326
|
+
});
|
|
327
|
+
const runtimeModuleContent = Object.entries(this.processedImages)
|
|
328
|
+
.map(([key, value]) => {
|
|
329
|
+
return `export const ${anyCaseToCamelCase(key)} = ${JSON.stringify(value, null, 2)} as const;`;
|
|
265
330
|
})
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const cachePath = this.getCachePath(cacheKey);
|
|
270
|
-
await fileSystem.removeAsync(cachePath);
|
|
271
|
-
logger.debug("Deleted cache file for image", { cachePath });
|
|
272
|
-
} catch (error) {
|
|
273
|
-
logger.error("Failed to delete cache file for image", { imagePath, error });
|
|
274
|
-
}
|
|
275
|
-
delete this.processedImages[path.basename(imagePath)];
|
|
276
|
-
this.generateTypes();
|
|
277
|
-
} catch (error) {
|
|
278
|
-
logger.error("Failed to delete processed images", { path: imagePath, error });
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Generate types for the virtual module.
|
|
283
|
-
*/
|
|
284
|
-
generateTypes() {
|
|
285
|
-
if (!this.options?.outputDir) {
|
|
286
|
-
throw new Error("Output directory not set");
|
|
331
|
+
.join('\n\n');
|
|
332
|
+
this.writeGeneratedFile(runtimeVirtualModulePath, runtimeModuleContent);
|
|
333
|
+
logger.debug('Generated runtime virtual module for images', { runtimeVirtualModulePath });
|
|
287
334
|
}
|
|
288
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Writes the ambient declaration package for `ecopages:images`.
|
|
337
|
+
*/
|
|
338
|
+
writeVirtualModuleTypes(imageKeys) {
|
|
339
|
+
const content = `
|
|
289
340
|
/**
|
|
290
341
|
* Do not edit manually. This file is auto-generated.
|
|
291
342
|
* This file contains the type definitions for the virtual module "ecopages:images".
|
|
@@ -294,53 +345,46 @@ class ImageProcessorPlugin extends Processor {
|
|
|
294
345
|
${IMAGE_VIRTUAL_MODULE_TYPES}
|
|
295
346
|
|
|
296
347
|
declare module "ecopages:images" {
|
|
297
|
-
${
|
|
348
|
+
${imageKeys.map((key) => `export const ${anyCaseToCamelCase(key)}: ImageSpecifications;`).join('\n ')}
|
|
298
349
|
}`;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
getProcessor() {
|
|
337
|
-
return this.processor;
|
|
338
|
-
}
|
|
350
|
+
if (!this.context)
|
|
351
|
+
throw new Error('Processor is not configured correctly');
|
|
352
|
+
const typesDir = resolveGeneratedPath('types', {
|
|
353
|
+
root: this.context.rootDir,
|
|
354
|
+
module: this.name,
|
|
355
|
+
subPath: 'virtual-module.d.ts',
|
|
356
|
+
});
|
|
357
|
+
this.writeGeneratedFile(typesDir, content);
|
|
358
|
+
logger.debug('Generated types for virtual module', { typesDir });
|
|
359
|
+
const indexTypesDir = resolveGeneratedPath('types', {
|
|
360
|
+
root: this.context.rootDir,
|
|
361
|
+
module: this.name,
|
|
362
|
+
subPath: 'index.d.ts',
|
|
363
|
+
});
|
|
364
|
+
const indexContent = 'import "./virtual-module.d.js";\n';
|
|
365
|
+
this.writeGeneratedFile(indexTypesDir, indexContent);
|
|
366
|
+
logger.debug('Generated index types for virtual module', { indexTypesDir });
|
|
367
|
+
writeGeneratedTypesPackage({
|
|
368
|
+
root: this.context.rootDir,
|
|
369
|
+
module: this.name,
|
|
370
|
+
packageName: '@types/ecopages-image-processor',
|
|
371
|
+
writeFile: (filePath, content) => this.writeGeneratedFile(filePath, content),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Teardown the image processor.
|
|
376
|
+
*/
|
|
377
|
+
async teardown() {
|
|
378
|
+
logger.debug('Tearing down image processor');
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get the image processor instance.
|
|
382
|
+
* @returns The image processor instance.
|
|
383
|
+
*/
|
|
384
|
+
getProcessor() {
|
|
385
|
+
return this.processor;
|
|
386
|
+
}
|
|
339
387
|
}
|
|
340
|
-
const imageProcessorPlugin = (config) => {
|
|
341
|
-
|
|
342
|
-
};
|
|
343
|
-
export {
|
|
344
|
-
ImageProcessorPlugin,
|
|
345
|
-
imageProcessorPlugin
|
|
388
|
+
export const imageProcessorPlugin = (config) => {
|
|
389
|
+
return new ImageProcessorPlugin(config);
|
|
346
390
|
};
|
package/src/types.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|