@ecopages/image-processor 0.2.0-alpha.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/package.json +63 -0
- package/src/bun-plugins.d.ts +22 -0
- package/src/bun-plugins.js +33 -0
- package/src/bun-plugins.ts +59 -0
- package/src/component/html.d.ts +11 -0
- package/src/component/html.js +7 -0
- package/src/component/html.ts +15 -0
- package/src/component/react.d.ts +13 -0
- package/src/component/react.js +11 -0
- package/src/component/react.ts +20 -0
- package/src/constants.d.ts +6 -0
- package/src/constants.js +4 -0
- package/src/constants.ts +7 -0
- package/src/image-plugins.d.ts +22 -0
- package/src/image-plugins.js +33 -0
- package/src/image-plugins.ts +59 -0
- package/src/image-processor.d.ts +19 -0
- package/src/image-processor.js +141 -0
- package/src/image-processor.ts +201 -0
- package/src/image-renderer.d.ts +152 -0
- package/src/image-renderer.js +222 -0
- package/src/image-renderer.ts +427 -0
- package/src/image-utils.d.ts +45 -0
- package/src/image-utils.js +103 -0
- package/src/image-utils.ts +128 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +3 -0
- package/src/index.ts +3 -0
- package/src/plugin.d.ts +81 -0
- package/src/plugin.js +223 -0
- package/src/plugin.ts +336 -0
- package/src/types.d.ts +42 -0
- package/src/types.js +0 -0
- package/src/types.ts +45 -0
- package/src/utils.d.ts +1 -0
- package/src/utils.js +27 -0
- package/src/utils.ts +30 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ImageMap, ImageProcessorConfig } from './plugin';
|
|
2
|
+
import type { ImageSpecifications } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* ImageProcessor
|
|
5
|
+
* This is the core class for processing images.
|
|
6
|
+
* It uses the sharp library to resize and optimize images.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ImageProcessor {
|
|
9
|
+
private readonly config;
|
|
10
|
+
private readonly cacheManager;
|
|
11
|
+
constructor(config: ImageProcessorConfig, cacheManager: {
|
|
12
|
+
readCache: <T>(key: string) => Promise<T | null>;
|
|
13
|
+
writeCache: <T>(key: string, data: T) => Promise<void>;
|
|
14
|
+
});
|
|
15
|
+
private calculateDimensions;
|
|
16
|
+
private getOutputPath;
|
|
17
|
+
processImage(imagePath: string): Promise<ImageSpecifications | null>;
|
|
18
|
+
processDirectory(): Promise<ImageMap>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { deepMerge } from "@ecopages/core/utils/deep-merge";
|
|
3
|
+
import { fileSystem } from "@ecopages/file-system";
|
|
4
|
+
import { Logger } from "@ecopages/logger";
|
|
5
|
+
import sharp from "sharp";
|
|
6
|
+
import { ImageUtils } from "./image-utils";
|
|
7
|
+
const appLogger = new Logger("[@ecopages/image-processor]", {
|
|
8
|
+
debug: process.env.ECOPAGES_LOGGER_DEBUG === "true"
|
|
9
|
+
});
|
|
10
|
+
class ImageProcessor {
|
|
11
|
+
config;
|
|
12
|
+
cacheManager;
|
|
13
|
+
constructor(config, cacheManager) {
|
|
14
|
+
this.config = deepMerge({ cacheEnabled: true }, config);
|
|
15
|
+
this.cacheManager = cacheManager;
|
|
16
|
+
fileSystem.ensureDir(this.config.outputDir);
|
|
17
|
+
}
|
|
18
|
+
async calculateDimensions(metadata, targetWidth) {
|
|
19
|
+
const originalWidth = metadata.width || 0;
|
|
20
|
+
const originalHeight = metadata.height || 0;
|
|
21
|
+
const aspectRatio = originalHeight / originalWidth;
|
|
22
|
+
const width = Math.min(targetWidth, originalWidth);
|
|
23
|
+
const height = Math.round(width * aspectRatio);
|
|
24
|
+
return { width, height };
|
|
25
|
+
}
|
|
26
|
+
getOutputPath(imagePath, width) {
|
|
27
|
+
const hash = fileSystem.hash(imagePath);
|
|
28
|
+
const ext = path.extname(imagePath);
|
|
29
|
+
const base = path.basename(imagePath, ext);
|
|
30
|
+
const filename = `${base}-${hash}-${width}.${this.config.format}`;
|
|
31
|
+
return path.join(this.config.outputDir, filename);
|
|
32
|
+
}
|
|
33
|
+
async processImage(imagePath) {
|
|
34
|
+
try {
|
|
35
|
+
const fileHash = fileSystem.hash(imagePath);
|
|
36
|
+
const cacheKey = `${path.basename(imagePath)}:${fileHash}`;
|
|
37
|
+
if (this.config.cacheEnabled) {
|
|
38
|
+
const cached = await this.cacheManager.readCache(cacheKey);
|
|
39
|
+
if (cached) {
|
|
40
|
+
const mainFilePath = path.join(process.cwd(), cached.attributes.src);
|
|
41
|
+
const mainFileExists = fileSystem.exists(mainFilePath);
|
|
42
|
+
const variantsExist = cached.variants.every(
|
|
43
|
+
(v) => fileSystem.exists(path.join(process.cwd(), v.src))
|
|
44
|
+
);
|
|
45
|
+
if (mainFileExists && variantsExist) {
|
|
46
|
+
appLogger.debug(`Cache hit for ${imagePath}`);
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
appLogger.debug(`Cache invalid for ${imagePath}, reprocessing`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
fileSystem.ensureDir(this.config.outputDir);
|
|
53
|
+
const metadata = await sharp(imagePath).metadata();
|
|
54
|
+
const originalWidth = metadata.width || 0;
|
|
55
|
+
const originalHeight = metadata.height || 0;
|
|
56
|
+
if (this.config.sizes.length === 0) {
|
|
57
|
+
const outputPath = this.getOutputPath(imagePath, originalWidth);
|
|
58
|
+
if (fileSystem.exists(outputPath)) {
|
|
59
|
+
appLogger.debug(`Using existing file for ${imagePath}`);
|
|
60
|
+
} else {
|
|
61
|
+
await sharp(imagePath).toFormat(this.config.format, { quality: this.config.quality }).toFile(outputPath);
|
|
62
|
+
}
|
|
63
|
+
const src = path.join(this.config.publicPath, path.basename(outputPath));
|
|
64
|
+
const imageSpecifications2 = {
|
|
65
|
+
attributes: {
|
|
66
|
+
src,
|
|
67
|
+
width: originalWidth,
|
|
68
|
+
height: originalHeight,
|
|
69
|
+
sizes: ""
|
|
70
|
+
},
|
|
71
|
+
variants: [],
|
|
72
|
+
cacheKey
|
|
73
|
+
};
|
|
74
|
+
if (this.config.cacheEnabled) {
|
|
75
|
+
await this.cacheManager.writeCache(cacheKey, imageSpecifications2);
|
|
76
|
+
}
|
|
77
|
+
return imageSpecifications2;
|
|
78
|
+
}
|
|
79
|
+
let applicableSizes = this.config.sizes.filter((size) => size.width <= originalWidth).sort((a, b) => b.width - a.width);
|
|
80
|
+
if (applicableSizes.length === 0) {
|
|
81
|
+
applicableSizes = this.config.sizes.sort((a, b) => b.width - a.width).slice(0, 1);
|
|
82
|
+
}
|
|
83
|
+
const variants = await Promise.all(
|
|
84
|
+
applicableSizes.map(async ({ width: targetWidth, label }) => {
|
|
85
|
+
const { width, height } = await this.calculateDimensions(metadata, targetWidth);
|
|
86
|
+
const outputPath = this.getOutputPath(imagePath, width);
|
|
87
|
+
if (fileSystem.exists(outputPath)) {
|
|
88
|
+
appLogger.debug(`Variant ${width}px already exists for ${imagePath}`);
|
|
89
|
+
} else {
|
|
90
|
+
await sharp(imagePath).resize(width, height).toFormat(this.config.format, { quality: this.config.quality }).toFile(outputPath);
|
|
91
|
+
}
|
|
92
|
+
const src = path.join(this.config.publicPath, path.basename(outputPath));
|
|
93
|
+
return {
|
|
94
|
+
width,
|
|
95
|
+
height,
|
|
96
|
+
src,
|
|
97
|
+
label
|
|
98
|
+
};
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
const mainVariant = variants[0];
|
|
102
|
+
const attributes = {
|
|
103
|
+
src: mainVariant.src,
|
|
104
|
+
width: mainVariant.width,
|
|
105
|
+
height: mainVariant.height,
|
|
106
|
+
sizes: ImageUtils.generateSizes(variants),
|
|
107
|
+
srcset: ImageUtils.generateSrcset(variants)
|
|
108
|
+
};
|
|
109
|
+
const imageSpecifications = {
|
|
110
|
+
attributes,
|
|
111
|
+
variants,
|
|
112
|
+
cacheKey
|
|
113
|
+
};
|
|
114
|
+
if (this.config.cacheEnabled) {
|
|
115
|
+
await this.cacheManager.writeCache(cacheKey, imageSpecifications);
|
|
116
|
+
}
|
|
117
|
+
return imageSpecifications;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
appLogger.error(`Failed to process image ${imagePath}:`, error);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async processDirectory() {
|
|
124
|
+
const acceptedFormats = this.config.acceptedFormats || ["jpg", "jpeg", "png", "webp"];
|
|
125
|
+
const images = await fileSystem.glob([`${this.config.sourceDir}/**/*.{${acceptedFormats.join(",")}}`]);
|
|
126
|
+
appLogger.debugTime("Processing images");
|
|
127
|
+
const results = (await Promise.all(
|
|
128
|
+
images.map(async (file) => {
|
|
129
|
+
const processed = await this.processImage(file);
|
|
130
|
+
if (!processed) return null;
|
|
131
|
+
return [path.basename(file), processed];
|
|
132
|
+
})
|
|
133
|
+
)).filter(Boolean);
|
|
134
|
+
appLogger.debugTimeEnd("Processing images");
|
|
135
|
+
appLogger.info(`Processed ${results.length} images`);
|
|
136
|
+
return Object.fromEntries(results);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
ImageProcessor
|
|
141
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { deepMerge } from '@ecopages/core/utils/deep-merge';
|
|
3
|
+
import { fileSystem } from '@ecopages/file-system';
|
|
4
|
+
import { Logger } from '@ecopages/logger';
|
|
5
|
+
import sharp from 'sharp';
|
|
6
|
+
import { ImageUtils } from './image-utils';
|
|
7
|
+
import type { ImageMap, ImageProcessorConfig } from './plugin';
|
|
8
|
+
import type { ImageAttributes, ImageSpecifications, ImageVariant } from './types';
|
|
9
|
+
|
|
10
|
+
const appLogger = new Logger('[@ecopages/image-processor]', {
|
|
11
|
+
debug: process.env.ECOPAGES_LOGGER_DEBUG === 'true',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ImageProcessor
|
|
16
|
+
* This is the core class for processing images.
|
|
17
|
+
* It uses the sharp library to resize and optimize images.
|
|
18
|
+
*/
|
|
19
|
+
export class ImageProcessor {
|
|
20
|
+
private readonly config: ImageProcessorConfig;
|
|
21
|
+
private readonly cacheManager: {
|
|
22
|
+
readCache: <T>(key: string) => Promise<T | null>;
|
|
23
|
+
writeCache: <T>(key: string, data: T) => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
config: ImageProcessorConfig,
|
|
28
|
+
cacheManager: {
|
|
29
|
+
readCache: <T>(key: string) => Promise<T | null>;
|
|
30
|
+
writeCache: <T>(key: string, data: T) => Promise<void>;
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
this.config = deepMerge({ cacheEnabled: true }, config);
|
|
34
|
+
this.cacheManager = cacheManager;
|
|
35
|
+
fileSystem.ensureDir(this.config.outputDir);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private async calculateDimensions(metadata: sharp.Metadata, targetWidth: number) {
|
|
39
|
+
const originalWidth = metadata.width || 0;
|
|
40
|
+
const originalHeight = metadata.height || 0;
|
|
41
|
+
const aspectRatio = originalHeight / originalWidth;
|
|
42
|
+
const width = Math.min(targetWidth, originalWidth);
|
|
43
|
+
const height = Math.round(width * aspectRatio);
|
|
44
|
+
return { width, height };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private getOutputPath(imagePath: string, width: number) {
|
|
48
|
+
const hash = fileSystem.hash(imagePath);
|
|
49
|
+
const ext = path.extname(imagePath);
|
|
50
|
+
const base = path.basename(imagePath, ext);
|
|
51
|
+
const filename = `${base}-${hash}-${width}.${this.config.format}`;
|
|
52
|
+
return path.join(this.config.outputDir, filename);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async processImage(imagePath: string): Promise<ImageSpecifications | null> {
|
|
56
|
+
try {
|
|
57
|
+
const fileHash = fileSystem.hash(imagePath);
|
|
58
|
+
const cacheKey = `${path.basename(imagePath)}:${fileHash}`;
|
|
59
|
+
|
|
60
|
+
if (this.config.cacheEnabled) {
|
|
61
|
+
const cached = await this.cacheManager.readCache<ImageSpecifications>(cacheKey);
|
|
62
|
+
if (cached) {
|
|
63
|
+
/**
|
|
64
|
+
* Verify that the files actually exist
|
|
65
|
+
* We construct the absolute path relative to the process current working directory
|
|
66
|
+
* since the src in attributes is relative from the root
|
|
67
|
+
*/
|
|
68
|
+
const mainFilePath = path.join(process.cwd(), cached.attributes.src);
|
|
69
|
+
const mainFileExists = fileSystem.exists(mainFilePath);
|
|
70
|
+
const variantsExist = cached.variants.every((v) =>
|
|
71
|
+
fileSystem.exists(path.join(process.cwd(), v.src)),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (mainFileExists && variantsExist) {
|
|
75
|
+
appLogger.debug(`Cache hit for ${imagePath}`);
|
|
76
|
+
return cached;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
appLogger.debug(`Cache invalid for ${imagePath}, reprocessing`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fileSystem.ensureDir(this.config.outputDir);
|
|
84
|
+
|
|
85
|
+
const metadata = await sharp(imagePath).metadata();
|
|
86
|
+
const originalWidth = metadata.width || 0;
|
|
87
|
+
const originalHeight = metadata.height || 0;
|
|
88
|
+
|
|
89
|
+
if (this.config.sizes.length === 0) {
|
|
90
|
+
const outputPath = this.getOutputPath(imagePath, originalWidth);
|
|
91
|
+
|
|
92
|
+
if (fileSystem.exists(outputPath)) {
|
|
93
|
+
appLogger.debug(`Using existing file for ${imagePath}`);
|
|
94
|
+
} else {
|
|
95
|
+
await sharp(imagePath)
|
|
96
|
+
.toFormat(this.config.format, { quality: this.config.quality })
|
|
97
|
+
.toFile(outputPath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const src = path.join(this.config.publicPath, path.basename(outputPath));
|
|
101
|
+
|
|
102
|
+
const imageSpecifications: ImageSpecifications = {
|
|
103
|
+
attributes: {
|
|
104
|
+
src,
|
|
105
|
+
width: originalWidth,
|
|
106
|
+
height: originalHeight,
|
|
107
|
+
sizes: '',
|
|
108
|
+
},
|
|
109
|
+
variants: [],
|
|
110
|
+
cacheKey,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (this.config.cacheEnabled) {
|
|
114
|
+
await this.cacheManager.writeCache(cacheKey, imageSpecifications);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return imageSpecifications;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let applicableSizes = this.config.sizes
|
|
121
|
+
.filter((size) => size.width <= originalWidth)
|
|
122
|
+
.sort((a, b) => b.width - a.width);
|
|
123
|
+
|
|
124
|
+
if (applicableSizes.length === 0) {
|
|
125
|
+
applicableSizes = this.config.sizes.sort((a, b) => b.width - a.width).slice(0, 1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const variants: ImageVariant[] = await Promise.all(
|
|
129
|
+
applicableSizes.map(async ({ width: targetWidth, label }) => {
|
|
130
|
+
const { width, height } = await this.calculateDimensions(metadata, targetWidth);
|
|
131
|
+
const outputPath = this.getOutputPath(imagePath, width);
|
|
132
|
+
|
|
133
|
+
if (fileSystem.exists(outputPath)) {
|
|
134
|
+
appLogger.debug(`Variant ${width}px already exists for ${imagePath}`);
|
|
135
|
+
} else {
|
|
136
|
+
await sharp(imagePath)
|
|
137
|
+
.resize(width, height)
|
|
138
|
+
.toFormat(this.config.format, { quality: this.config.quality })
|
|
139
|
+
.toFile(outputPath);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const src = path.join(this.config.publicPath, path.basename(outputPath));
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
width,
|
|
146
|
+
height,
|
|
147
|
+
src,
|
|
148
|
+
label,
|
|
149
|
+
};
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const mainVariant = variants[0];
|
|
154
|
+
const attributes: ImageAttributes = {
|
|
155
|
+
src: mainVariant.src,
|
|
156
|
+
width: mainVariant.width,
|
|
157
|
+
height: mainVariant.height,
|
|
158
|
+
sizes: ImageUtils.generateSizes(variants),
|
|
159
|
+
srcset: ImageUtils.generateSrcset(variants),
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const imageSpecifications: ImageSpecifications = {
|
|
163
|
+
attributes,
|
|
164
|
+
variants,
|
|
165
|
+
cacheKey,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (this.config.cacheEnabled) {
|
|
169
|
+
await this.cacheManager.writeCache(cacheKey, imageSpecifications);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return imageSpecifications;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
appLogger.error(`Failed to process image ${imagePath}:`, error as Error);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async processDirectory(): Promise<ImageMap> {
|
|
180
|
+
const acceptedFormats = this.config.acceptedFormats || ['jpg', 'jpeg', 'png', 'webp'];
|
|
181
|
+
|
|
182
|
+
const images = await fileSystem.glob([`${this.config.sourceDir}/**/*.{${acceptedFormats.join(',')}}`]);
|
|
183
|
+
|
|
184
|
+
appLogger.debugTime('Processing images');
|
|
185
|
+
|
|
186
|
+
const results = (
|
|
187
|
+
await Promise.all(
|
|
188
|
+
images.map(async (file) => {
|
|
189
|
+
const processed = await this.processImage(file);
|
|
190
|
+
if (!processed) return null;
|
|
191
|
+
return [path.basename(file), processed] as [string, ImageSpecifications];
|
|
192
|
+
}),
|
|
193
|
+
)
|
|
194
|
+
).filter(Boolean) as [string, ImageSpecifications][];
|
|
195
|
+
|
|
196
|
+
appLogger.debugTimeEnd('Processing images');
|
|
197
|
+
appLogger.info(`Processed ${results.length} images`);
|
|
198
|
+
|
|
199
|
+
return Object.fromEntries(results);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImageRenderer
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import type { ImageSpecifications } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Image layout options
|
|
8
|
+
*/
|
|
9
|
+
export type ImageLayout = 'fixed' | 'constrained' | 'full-width';
|
|
10
|
+
/**
|
|
11
|
+
* ImageProps
|
|
12
|
+
* This interface represents the properties that can be passed to the image element
|
|
13
|
+
*/
|
|
14
|
+
export type EcoImageProps = ImageSpecifications & {
|
|
15
|
+
/**
|
|
16
|
+
* width of the image as defined in the component
|
|
17
|
+
*/
|
|
18
|
+
width?: number;
|
|
19
|
+
/**
|
|
20
|
+
* height of the image as defined in the component
|
|
21
|
+
*/
|
|
22
|
+
height?: number;
|
|
23
|
+
/**
|
|
24
|
+
* The alt text for the image
|
|
25
|
+
*/
|
|
26
|
+
aspectRatio?: string;
|
|
27
|
+
/**
|
|
28
|
+
* If true, the image will be loaded eagerly
|
|
29
|
+
*/
|
|
30
|
+
priority?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The layout of the image
|
|
33
|
+
* @default "constrained"
|
|
34
|
+
*/
|
|
35
|
+
layout?: ImageLayout;
|
|
36
|
+
/**
|
|
37
|
+
* If true, the image will not have any styles applied to it
|
|
38
|
+
*/
|
|
39
|
+
unstyled?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Specifies a fixed image variant from the configuration.
|
|
42
|
+
* This should match one of the variant labels defined in your image optimization config.
|
|
43
|
+
* When set, the image will use this specific variant instead of responsive sizes.
|
|
44
|
+
*/
|
|
45
|
+
staticVariant?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Optional additional attributes to be added to the image element
|
|
48
|
+
*/
|
|
49
|
+
[additionalAttributes: string]: any;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* CollectedAttributes
|
|
53
|
+
* This interface represents the attributes that are collected by the image renderer
|
|
54
|
+
* These attributes are used to generate the attributes for the image element
|
|
55
|
+
*/
|
|
56
|
+
export interface CollectedAttributes {
|
|
57
|
+
width?: number;
|
|
58
|
+
height?: number;
|
|
59
|
+
loading: HTMLImageElement['loading'];
|
|
60
|
+
fetchpriority: HTMLImageElement['fetchPriority'];
|
|
61
|
+
decoding: HTMLImageElement['decoding'];
|
|
62
|
+
src: string;
|
|
63
|
+
srcset?: string;
|
|
64
|
+
sizes?: string;
|
|
65
|
+
styles: [string, string][];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* GenerateAttributesResult
|
|
69
|
+
* This interface represents the result of the generateAttributes method
|
|
70
|
+
*/
|
|
71
|
+
export interface GenerateAttributesResult {
|
|
72
|
+
fetchpriority: HTMLImageElement['fetchPriority'];
|
|
73
|
+
loading: HTMLImageElement['loading'];
|
|
74
|
+
decoding: HTMLImageElement['decoding'];
|
|
75
|
+
src: string;
|
|
76
|
+
srcset?: string;
|
|
77
|
+
sizes?: string;
|
|
78
|
+
width?: number;
|
|
79
|
+
height?: number;
|
|
80
|
+
style?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* GenerateAttributesResultJsx
|
|
84
|
+
* This interface represents the result of the generateAttributes method as JSX
|
|
85
|
+
*/
|
|
86
|
+
export interface GenerateAttributesResultJsx {
|
|
87
|
+
fetchPriority: HTMLImageElement['fetchPriority'];
|
|
88
|
+
loading: HTMLImageElement['loading'];
|
|
89
|
+
decoding: HTMLImageElement['decoding'];
|
|
90
|
+
src: string;
|
|
91
|
+
srcSet?: string;
|
|
92
|
+
sizes?: string;
|
|
93
|
+
width?: number;
|
|
94
|
+
height?: number;
|
|
95
|
+
style?: React.CSSProperties;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* IImageRenderer
|
|
99
|
+
* This interface represents the image renderer in charge of generating the attributes for the image element
|
|
100
|
+
*/
|
|
101
|
+
export interface IImageRenderer {
|
|
102
|
+
/**
|
|
103
|
+
* This method generates the attributes for the image element based on the provided props
|
|
104
|
+
* This is the main method that should be used to generate the attributes for the image element
|
|
105
|
+
* @param props
|
|
106
|
+
*/
|
|
107
|
+
generateAttributes(props: EcoImageProps): GenerateAttributesResult | null;
|
|
108
|
+
/**
|
|
109
|
+
* This method generates the attributes for the image element based on the provided props as JSX
|
|
110
|
+
* @param props
|
|
111
|
+
*/
|
|
112
|
+
generateAttributesJsx(props: EcoImageProps): GenerateAttributesResultJsx | null;
|
|
113
|
+
/**
|
|
114
|
+
* This method generates the image element based on the provided props as a string
|
|
115
|
+
* @param props
|
|
116
|
+
*/
|
|
117
|
+
renderToString(props: EcoImageProps): string;
|
|
118
|
+
}
|
|
119
|
+
export declare class LayoutAttributesManager {
|
|
120
|
+
static shouldIncludeWidthHeight(layout: ImageLayout): boolean;
|
|
121
|
+
static filterDimensionAttributes(props: Pick<EcoImageProps, 'width' | 'height' | 'layout'>): Pick<EcoImageProps, 'width' | 'height'>;
|
|
122
|
+
static getEffectiveDimensions(props: EcoImageProps, variants?: Array<{
|
|
123
|
+
width: number;
|
|
124
|
+
height: number;
|
|
125
|
+
}>): {
|
|
126
|
+
width?: number;
|
|
127
|
+
height?: number;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* ImageRenderer
|
|
132
|
+
* This class is responsible for generating the attributes for the image element
|
|
133
|
+
*/
|
|
134
|
+
export declare class ImageRenderer implements IImageRenderer {
|
|
135
|
+
private originalProps?;
|
|
136
|
+
private readonly internalProps;
|
|
137
|
+
generateAttributes(props: EcoImageProps): GenerateAttributesResult | null;
|
|
138
|
+
private getHtmlAttributes;
|
|
139
|
+
generateAttributesJsx(props: EcoImageProps): GenerateAttributesResultJsx | null;
|
|
140
|
+
renderToString({ attributes, variants, ...rest }: EcoImageProps): string;
|
|
141
|
+
private collectAttributes;
|
|
142
|
+
private getPriorityAttributes;
|
|
143
|
+
private getMainVariant;
|
|
144
|
+
private calculateEffectiveDimensions;
|
|
145
|
+
private calculateStyles;
|
|
146
|
+
private buildImageAttributes;
|
|
147
|
+
private handleNoVariants;
|
|
148
|
+
private stringifyAttributes;
|
|
149
|
+
private generateStyleString;
|
|
150
|
+
private createCamelCaseKeysOnStyle;
|
|
151
|
+
}
|
|
152
|
+
export declare const renderer: ImageRenderer;
|