auto-image-converter 2.1.1 → 2.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/README.md CHANGED
@@ -1,69 +1,195 @@
1
1
  # 🖼️ Auto Image Converter
2
2
 
3
- Automatically convert PNG, JPG, and JPEG images to WebP or AVIF format with real-time file watching.
3
+ Automatically convert and resize images to modern formats (WebP, AVIF, PNG, JPEG, TIFF) with real-time file watching and flexible resize options.
4
4
 
5
- ### 🔗 GitHub repository: https://github.com/StoneZol/auto-image-converter
5
+ ## 🔗 GitHub repository
6
+
7
+ <https://github.com/StoneZol/auto-image-converter>
6
8
 
7
9
  ## 🚀 Features
8
10
 
9
- 🔄 Convert images to .webp or .avif
11
+ 🔄 **Convert images** to WebP, AVIF, PNG, JPEG, or TIFF
12
+
13
+ 📐 **Resize images** by width, height, or to specific aspect ratios
14
+
15
+ 🔍 **Recursive folder support** - process entire directory trees
10
16
 
11
- 🔍 Recursive folder support
17
+ 🗑️ **Optional removal** of original files after conversion
12
18
 
13
- 🗑️ Optional removal of original files
19
+ 👀 **Watch mode** automatically converts newly added images in real-time
14
20
 
15
- 👀 Watch mode automatically converts newly added images
21
+ **Parallel processing** with configurable concurrency
16
22
 
17
- ⚙️ Easy configuration via image-converter.config.mjs
23
+ 🎯 **One-time resize** resize already converted images without re-conversion
24
+
25
+ ⚙️ **Easy configuration** via `image-converter.config.mjs`
18
26
 
19
27
  ## 📦 Installation
20
28
 
21
- `npm install auto-image-converter --save-dev`
29
+ ```bash
30
+ npm install auto-image-converter --save-dev
31
+ ```
32
+
33
+ Or use locally via `npm link`:
34
+
35
+ ```bash
36
+ cd auto-image-converter
37
+ npm link
38
+ cd ../your-project
39
+ npm link auto-image-converter
40
+ ```
22
41
 
23
42
  ## ⚙️ Configuration
24
43
 
25
44
  Create a `image-converter.config.mjs` file in the root of your project:
26
45
 
27
- ```// Default configuration example
46
+ ```javascript
28
47
  export default {
29
- dir: "public", // Directory to scan for images
30
- converted: "*.{png,jpg,jpeg}", // Glob pattern for source image files to convert
31
- format: "webp", // Output image format: 'webp' or 'avif'
32
- quality: 80, // Quality of output images (0–100)
33
- recursive: true, // Whether to search subdirectories recursively
34
- removeOriginal: true, // Delete original files after successful conversion
35
- ignoreOnStart: false, // If true, ignore existing files on watcher startup
48
+ // Main settings
49
+ dir: "./public", // Directory to scan for images
50
+ removeOriginal: true, // Delete original files after conversion
51
+ recursive: true, // Recursive search in subdirectories
52
+ ignoreOnStart: true, // Ignore existing files on watcher startup
53
+ concurrency: 4, // Number of parallel workers
54
+
55
+ // Conversion settings
56
+ convertation: {
57
+ converted: "*.{png,jpg,jpeg,tiff}", // Source file pattern
58
+ format: "webp", // Target format: webp, avif, png, jpg, tiff
59
+ quality: 80, // Quality (0-100)
60
+ outputDir: null, // null = same folder, or path for output
61
+ },
62
+
63
+ // Resize settings (optional)
64
+ needResize: true, // Enable resize
65
+ resize: {
66
+ width: 1920, // Width (or null)
67
+ height: null, // Height (or null)
68
+ fit: "cover", // cover, contain, fill, inside, outside
69
+ position: "center", // Cropping position
70
+ withoutEnlargement: true, // Don't enlarge small images
71
+ },
36
72
  };
37
73
  ```
38
74
 
75
+ ### Resize Options
76
+
77
+ - **By width only**: `width: 1920, height: null` - reduces to width, height scales proportionally
78
+ - **By height only**: `width: null, height: 1080` - reduces to height, width scales proportionally
79
+ - **To aspect ratio**: `width: 1920, height: 1080` - fits/crops to specific aspect ratio
80
+
81
+ ### Fit Modes
82
+
83
+ - `cover` - fills entire size, cropping excess (default)
84
+ - `contain` - fits fully, may add padding
85
+ - `fill` - stretches without preserving aspect ratio
86
+ - `inside` - reduces to fit, doesn't enlarge
87
+ - `outside` - covers entire size, may enlarge
88
+
39
89
  ## 🛠️ Usage
40
90
 
41
- Add the following to your package.json:
91
+ ### Commands
92
+
93
+ ```bash
94
+ # One-time conversion of all files
95
+ npx auto-convert-images
96
+
97
+ # Watch mode - processes new files as they're added
98
+ npx auto-convert-images-watch
99
+
100
+ # One-time resize of already converted files
101
+ npx auto-convert-images-resize
102
+ ```
103
+
104
+ ### Package.json Scripts
105
+
106
+ Add to your `package.json`:
107
+
108
+ ```json
109
+ {
110
+ "scripts": {
111
+ "convert": "auto-convert-images",
112
+ "watch": "auto-convert-images-watch",
113
+ "resize": "auto-convert-images-resize"
114
+ }
115
+ }
116
+ ```
117
+
118
+ Then run:
119
+
120
+ - `npm run convert` - one-time conversion
121
+ - `npm run watch` - watch mode
122
+ - `npm run resize` - resize already converted files
123
+
124
+ ### With Next.js
125
+
126
+ To run the watcher alongside the development server:
42
127
 
128
+ ```bash
129
+ npm install concurrently --save-dev
43
130
  ```
44
- "scripts": {
45
- "aciw": "auto-convert-images-watch"
131
+
132
+ ```json
133
+ {
134
+ "scripts": {
135
+ "dev": "concurrently \"npm run watch\" \"next dev\""
136
+ }
46
137
  }
47
138
  ```
48
139
 
49
- To start the image watcher: `npm run aciw`
140
+ ## 📋 Use Cases
50
141
 
51
- Or run a one-time conversion: `npx auto-convert-images`
142
+ ### 1. Initial Conversion
52
143
 
53
- ## With Next.js
144
+ Convert all PNG/JPG files to WebP:
54
145
 
55
- To run the watcher alongside the development server, install concurrently:
146
+ ```bash
147
+ npx auto-convert-images
148
+ ```
56
149
 
57
- `npm install concurrently --save-dev`
150
+ ### 2. Watch Mode
58
151
 
59
- Then update your dev script like so:
152
+ Automatically convert new images as they're added:
60
153
 
154
+ ```bash
155
+ npx auto-convert-images-watch
61
156
  ```
62
- "scripts": {
63
- "dev": "concurrently \"npm run aciw\" \"next dev\""
64
- }
157
+
158
+ ### 3. Resize Already Converted Files
159
+
160
+ If you converted files without resize, then decided you need resize:
161
+
162
+ ```bash
163
+ npx auto-convert-images-resize
65
164
  ```
66
165
 
166
+ - If `removeOriginal: false` → creates new files with size suffix: `image.webp` → `image-1920x1080.webp`
167
+ - If `removeOriginal: true` → overwrites original files
168
+
169
+ ## 🏗️ Architecture
170
+
171
+ The tool uses a modular architecture:
172
+
173
+ - **Pipeline** - main processing engine with queue and workers
174
+ - **ResizeImages** - resize operations wrapper
175
+ - **ConvertImages** - format conversion wrapper
176
+ - **FileManager** - file path resolution and saving
177
+
178
+ All operations work with Sharp instances in a chainable pipeline pattern.
179
+
180
+ ## 🐛 Bug Reports
181
+
182
+ Found a bug? Please report it in the [GitHub Issues](https://github.com/StoneZol/auto-image-converter/issues) section of the repository. Include:
183
+
184
+ - Description of the issue
185
+ - Steps to reproduce
186
+ - Expected behavior
187
+ - Actual behavior
188
+ - Configuration file (if relevant)
189
+ - Node.js version and OS
190
+
191
+ This helps improve the tool for everyone!
192
+
67
193
  ## 📄 License
68
194
 
69
195
  This project is open source and available under the MIT License.
package/bin/index.js CHANGED
@@ -1,28 +1,59 @@
1
1
  #!/usr/bin/env node
2
2
  import path from "path";
3
- import { fileURLToPath, pathToFileURL } from "url";
4
- import { convertImages } from "../lib/converter.js";
3
+ import { pathToFileURL } from "url";
4
+ import fs from "fs";
5
+ import { Pipeline } from "../lib/Pipeline.js";
5
6
 
6
- const CONFIG_PATH = path.resolve(process.cwd(), "image-converter.config.mjs");
7
+ const CONFIG_PATH = path.resolve(
8
+ process.cwd(),
9
+ "image-converter.config.mjs"
10
+ );
7
11
 
8
12
  try {
9
- const configModule = await import(pathToFileURL(CONFIG_PATH).href);
10
- const config = configModule.default;
13
+ // Проверяем существование конфига
14
+ if (!fs.existsSync(CONFIG_PATH)) {
15
+ console.error(
16
+ `❌ Config file not found: ${CONFIG_PATH}\n` +
17
+ ` Please create 'image-converter.config.mjs' in the current directory.`
18
+ );
19
+ process.exit(1);
20
+ }
11
21
 
12
- const absDir = path.resolve(
13
- process.cwd(),
14
- config.source || config.dir || "."
15
- );
22
+ const configModule = await import(
23
+ pathToFileURL(CONFIG_PATH).href
24
+ );
25
+ const config = configModule.default;
26
+
27
+ if (!config) {
28
+ console.error(
29
+ `❌ Config file is empty or doesn't export default config.\n` +
30
+ ` File: ${CONFIG_PATH}`
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ const pipeline = new Pipeline(config);
36
+ const stats = await pipeline.run();
16
37
 
17
- await convertImages({
18
- dir: absDir,
19
- converted: config.converted ?? "*.{png,jpg,jpeg}",
20
- targetFormat: config.targetFormat ?? "webp",
21
- quality: config.quality ?? 80,
22
- recursive: config.recursive ?? true,
23
- removeOriginal: config.removeOriginal ?? false,
24
- });
38
+ console.log(`\n✅ Processing complete:`);
39
+ console.log(` Total: ${stats.total}`);
40
+ console.log(` Converted: ${stats.converted}`);
41
+ console.log(` Skipped: ${stats.skipped}`);
42
+ console.log(` Failed: ${stats.failed}`);
25
43
  } catch (e) {
26
- console.error("❌ Ошибка загрузки конфигурации:", e.message);
27
- process.exit(1);
44
+ if (e.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("image-converter.config.mjs")) {
45
+ console.error(
46
+ `❌ Config file not found: ${CONFIG_PATH}\n` +
47
+ ` Please create 'image-converter.config.mjs' in the current directory.`
48
+ );
49
+ } else {
50
+ console.error(
51
+ "❌ Error:",
52
+ e.message
53
+ );
54
+ if (e.stack) {
55
+ console.error(e.stack);
56
+ }
57
+ }
58
+ process.exit(1);
28
59
  }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import { pathToFileURL } from "url";
4
+ import fs from "fs";
5
+ import { Pipeline } from "../lib/Pipeline.js";
6
+
7
+ const CONFIG_PATH = path.resolve(
8
+ process.cwd(),
9
+ "image-converter.config.mjs"
10
+ );
11
+
12
+ try {
13
+ // Проверяем существование конфига
14
+ if (!fs.existsSync(CONFIG_PATH)) {
15
+ console.error(
16
+ `❌ Config file not found: ${CONFIG_PATH}\n` +
17
+ ` Please create 'image-converter.config.mjs' in the current directory.`
18
+ );
19
+ process.exit(1);
20
+ }
21
+
22
+ const configModule = await import(
23
+ pathToFileURL(CONFIG_PATH).href
24
+ );
25
+ const originalConfig = configModule.default;
26
+
27
+ if (!originalConfig) {
28
+ console.error(
29
+ `❌ Config file is empty or doesn't export default config.\n` +
30
+ ` File: ${CONFIG_PATH}`
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ // Проверяем, что в конфиге есть resize настройки
36
+ if (!originalConfig.resize) {
37
+ console.error(
38
+ `❌ Resize config is missing.\n` +
39
+ ` Please add 'resize' section to your config.`
40
+ );
41
+ process.exit(1);
42
+ }
43
+
44
+ // Создаём модифицированный конфиг для ресайза уже сконвертированных файлов
45
+ const targetFormat = (
46
+ originalConfig.convertation?.format ?? originalConfig.format ?? "webp"
47
+ ).toLowerCase();
48
+
49
+ const resizeConfig = {
50
+ ...originalConfig,
51
+ // Ищем файлы в целевом формате (уже сконвертированные)
52
+ convertation: {
53
+ ...originalConfig.convertation,
54
+ converted: `*.${targetFormat}`, // только файлы в целевом формате (без фигурных скобок для одного формата)
55
+ format: targetFormat, // оставляем тот же формат (не конвертируем)
56
+ },
57
+ // Принудительно включаем ресайз
58
+ needResize: true,
59
+ resize: originalConfig.resize,
60
+ // Используем removeOriginal из конфига
61
+ removeOriginal: originalConfig.removeOriginal ?? false,
62
+ // Флаг для режима ресайза (чтобы Pipeline знал, что нужно добавлять размер в имя)
63
+ isResizeMode: true,
64
+ };
65
+
66
+ console.log(`🔄 Resizing already converted ${targetFormat.toUpperCase()} files...`);
67
+ console.log(` Resize config: ${JSON.stringify(originalConfig.resize, null, 2)}\n`);
68
+
69
+ const pipeline = new Pipeline(resizeConfig);
70
+ const stats = await pipeline.run();
71
+
72
+ console.log(`\n✅ Resize complete:`);
73
+ console.log(` Total: ${stats.total}`);
74
+ console.log(` Resized: ${stats.converted}`);
75
+ console.log(` Skipped: ${stats.skipped}`);
76
+ console.log(` Failed: ${stats.failed}`);
77
+ } catch (e) {
78
+ if (e.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("image-converter.config.mjs")) {
79
+ console.error(
80
+ `❌ Config file not found: ${CONFIG_PATH}\n` +
81
+ ` Please create 'image-converter.config.mjs' in the current directory.`
82
+ );
83
+ } else {
84
+ console.error(
85
+ "❌ Error:",
86
+ e.message
87
+ );
88
+ if (e.stack) {
89
+ console.error(e.stack);
90
+ }
91
+ }
92
+ process.exit(1);
93
+ }
package/bin/watcher.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import chokidar from "chokidar";
3
3
  import path from "path";
4
- import { convertImages } from "../lib/converter.js";
4
+ import { Pipeline } from "../lib/Pipeline.js";
5
5
  import { pathToFileURL } from "url";
6
6
 
7
7
  const configPath = path.resolve(process.cwd(), "image-converter.config.mjs");
@@ -9,48 +9,65 @@ const config = (await import(pathToFileURL(configPath).href)).default;
9
9
 
10
10
  const watchDir = config.dir || "public";
11
11
  const absWatchDir = path.resolve(process.cwd(), watchDir);
12
- const extensions = extractExtensions(config.converted ?? "*.{png,jpg,jpeg}");
12
+ const extensions = extractExtensions(
13
+ config.convertation?.converted ?? config.converted ?? "*.{png,jpg,jpeg}"
14
+ );
15
+ const targetFormat = (
16
+ config.convertation?.format ?? config.format ?? "webp"
17
+ ).toLowerCase();
13
18
 
14
19
  const watchPath = absWatchDir;
15
20
 
16
21
  console.log(`👀 Watching for image changes on directory: ${watchPath}`);
17
22
 
23
+ // Создаём Pipeline и запускаем воркеров в постоянном режиме
24
+ const pipeline = new Pipeline(config);
25
+ pipeline.startWorkers();
26
+
18
27
  let debounceTimeout;
28
+ let pendingFiles = new Set();
19
29
 
20
30
  chokidar
21
- .watch(absWatchDir, {
22
- ignored: /(^|[\/\\])\../,
23
- persistent: true,
24
- ignoreInitial: config.ignoreOnStart ?? false,
25
- awaitWriteFinish: {
26
- stabilityThreshold: 1500,
27
- pollInterval: 500,
28
- },
29
- })
30
- .on("add", async (filePath) => {
31
- const ext = path.extname(filePath).slice(1).toLowerCase(); // без точки
32
- if (!extensions.includes(ext)) return;
33
- if (ext === (config.targetFormat ?? "webp").toLowerCase()) return;
34
-
35
- console.log(`➕ New image: ${filePath}`);
36
- clearTimeout(debounceTimeout);
37
- debounceTimeout = setTimeout(async () => {
38
- try {
39
- await convertImages({
40
- dir: watchPath,
41
- converted: config.converted ?? "*.{png,jpg,jpeg}",
42
- format: config.format ?? "webp",
43
- quality: config.quality ?? 80,
44
- recursive: config.recursive ?? true,
45
- removeOriginal: config.removeOriginal ?? false,
46
- });
47
- } catch (err) {
48
- console.error("Error covertation:", err.message);
49
- }
50
- }, 1000);
51
- });
31
+ .watch(absWatchDir, {
32
+ ignored: /(^|[\/\\])\../,
33
+ persistent: true,
34
+ ignoreInitial: config.ignoreOnStart ?? false,
35
+ awaitWriteFinish: {
36
+ stabilityThreshold: 1500,
37
+ pollInterval: 500,
38
+ },
39
+ })
40
+ .on("add", async (filePath) => {
41
+ const ext = path.extname(filePath).slice(1).toLowerCase();
42
+ if (!extensions.includes(ext)) return;
43
+ if (ext === targetFormat) return;
44
+
45
+ console.log(`➕ New image: ${filePath}`);
46
+ pendingFiles.add(filePath);
47
+
48
+ clearTimeout(debounceTimeout);
49
+ debounceTimeout = setTimeout(async () => {
50
+ // Обрабатываем все накопленные файлы
51
+ const filesToProcess = Array.from(pendingFiles);
52
+ pendingFiles.clear();
53
+
54
+ for (const file of filesToProcess) {
55
+ pipeline.enqueueFile(file);
56
+ }
57
+ // Воркеры уже работают, просто добавляем задачи в очередь
58
+ }, 1000);
59
+ });
60
+
61
+ // Graceful shutdown
62
+ process.on("SIGINT", async () => {
63
+ console.log("\n🛑 Stopping watcher...");
64
+ await pipeline.stop();
65
+ process.exit(0);
66
+ });
52
67
 
53
68
  function extractExtensions(pattern) {
54
- const match = pattern.match(/\*\.\{(.+?)\}/);
55
- return match ? match[1].split(",").map((s) => s.trim().toLowerCase()) : [];
69
+ const match = pattern.match(/\*\.\{(.+?)\}/);
70
+ return match
71
+ ? match[1].split(",").map((s) => s.trim().toLowerCase())
72
+ : [];
56
73
  }
@@ -1,9 +1,21 @@
1
1
  export default {
2
- dir: "./public",
3
- converted: "*.{png,jpg,jpeg}",
4
- format: "webp",
5
- quality: 80,
6
- removeOriginal: true,
7
- recursive: true,
8
- ignoreOnStart: true,
2
+ // Основные настройки
3
+ dir: "./public",
4
+ removeOriginal: true,
5
+ recursive: true,
6
+ ignoreOnStart: true,
7
+ concurrency: 4,
8
+ convertation: {
9
+ converted: "*.{png,jpg,jpeg,tiff}", // png, jpg, jpeg, tiff, webp, avif
10
+ format: "webp", // png, jpeg, tiff, webp, avif
11
+ quality: 80, // 0-100
12
+ },
13
+ needResize: true, // true or false
14
+ resize: {
15
+ width: 1920, // 100-1000 or null
16
+ height: null, // 100-1000 or null
17
+ fit: "cover", // cover, contain, fill, inside, outside
18
+ position: "center", // center, top, bottom, left, right, top-left, top-right, bottom-left, bottom-right
19
+ withoutEnlargement: true, // true or false
20
+ },
9
21
  };
@@ -0,0 +1,68 @@
1
+ import sharp from "sharp";
2
+
3
+ /**
4
+ * Абстракция для конвертации форматов.
5
+ *
6
+ * Главное правило:
7
+ * - на вход можно дать путь/Buffer ИЛИ уже готовый sharp‑инстанс;
8
+ * - методы конверсии возвращают sharp‑инстанс;
9
+ * - .toBuffer() / .toFile() вызываются уже снаружи (в пайплайне).
10
+ */
11
+ export class ConvertImages {
12
+ /**
13
+ * @param {string|Buffer|import("sharp").Sharp} input
14
+ * @param {Object} options
15
+ * @param {number} options.quality
16
+ */
17
+ constructor(input, options) {
18
+ this.quality = options.quality;
19
+
20
+ // Если нам уже дали sharp‑инстанс — просто переиспользуем его.
21
+ if (input && typeof input === "object" && typeof input.resize === "function") {
22
+ this.instance = input;
23
+ } else {
24
+ // Иначе считаем, что это путь или Buffer.
25
+ this.instance = sharp(input);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Конвертировать в WebP.
31
+ * @returns {import("sharp").Sharp}
32
+ */
33
+ toWebp() {
34
+ return this.instance.webp({ quality: this.quality });
35
+ }
36
+
37
+ /**
38
+ * Конвертировать в AVIF.
39
+ * @returns {import("sharp").Sharp}
40
+ */
41
+ toAvif() {
42
+ return this.instance.avif({ quality: this.quality });
43
+ }
44
+
45
+ /**
46
+ * Конвертировать в PNG.
47
+ * @returns {import("sharp").Sharp}
48
+ */
49
+ toPng() {
50
+ return this.instance.png({ quality: this.quality });
51
+ }
52
+
53
+ /**
54
+ * Конвертировать в JPEG.
55
+ * @returns {import("sharp").Sharp}
56
+ */
57
+ toJpg() {
58
+ return this.instance.jpeg({ quality: this.quality });
59
+ }
60
+
61
+ /**
62
+ * Конвертировать в TIFF.
63
+ * @returns {import("sharp").Sharp}
64
+ */
65
+ toTiff() {
66
+ return this.instance.tiff({ quality: this.quality });
67
+ }
68
+ }
@@ -0,0 +1,60 @@
1
+ export class CreateSrcSet {
2
+ // Resize modes
3
+ static RESIZE_MODES = {
4
+ COVER: "cover", // Fill the entire size, cropping excess
5
+ CONTAIN: "contain", // Contain fully, preserving proportions
6
+ FILL: "fill", // Stretch to size (distortion)
7
+ INSIDE: "inside", // Contain inside size (like contain)
8
+ OUTSIDE: "outside", // Cover outside (like cover)
9
+ };
10
+ constructor(filePath, options = {}) {
11
+ this.filePath = filePath;
12
+ this.format = options.format;
13
+ this.quality = options.quality;
14
+ this.resizeMode = options.resizeMode; // [{w:32,h:32}, {w:64,h:64}, {w:128,h:128}]
15
+ this.w = options.w;
16
+ this.h = options.h;
17
+ }
18
+ getOutputFileName(w, h) {
19
+ return `${this.nameWithoutExt}-${w}x${h}.${this.format}`;
20
+ }
21
+ createCover(w, h) {
22
+ return sharp(this.filePath).resize(w, h).toBuffer();
23
+ }
24
+ createContain(w, h) {
25
+ return sharp(this.filePath).resize(w, h).toBuffer();
26
+ }
27
+ createFill(w, h) {
28
+ return sharp(this.filePath).resize(w, h).toBuffer();
29
+ }
30
+ createInside(w, h) {
31
+ return sharp(this.filePath).resize(w, h).toBuffer();
32
+ }
33
+ createOutside(w, h) {
34
+ return sharp(this.filePath).resize(w, h).toBuffer();
35
+ }
36
+
37
+ create(w, h) {
38
+ switch (this.resizeMode) {
39
+ case this.RESIZE_MODES.COVER:
40
+ return this.createCover(w, h);
41
+ case this.RESIZE_MODES.CONTAIN:
42
+ return this.createContain(w, h);
43
+ case this.RESIZE_MODES.FILL:
44
+ return this.createFill(w, h);
45
+ case this.RESIZE_MODES.INSIDE:
46
+ return this.createInside(w, h);
47
+ case this.RESIZE_MODES.OUTSIDE:
48
+ return this.createOutside(w, h);
49
+ default:
50
+ throw new Error(
51
+ `Invalid resize mode: ${this.resizeMode}`
52
+ );
53
+ }
54
+ }
55
+ createSrcSet() {
56
+ return this.resizeModes.map((mode) => {
57
+ return this.create(mode.w, mode.h);
58
+ });
59
+ }
60
+ }