metalsmith-optimize-images 0.1.1 → 0.9.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,25 +1,30 @@
1
1
  # metalsmith-optimize-images
2
2
 
3
- > **⚠️: This plugin is a fully functional proof-of-concept. However, it is not yet fully tested and may contain bugs. Use with caution.**
3
+ > **🚀 Release Candidate**: This plugin has achieved comprehensive test coverage (96.06%) and is ready for production testing. Feedback welcome before 1.0.0 release!
4
4
 
5
5
  Metalsmith plugin for generating responsive images with optimal formats
6
6
 
7
7
  [![metalsmith:plugin][metalsmith-badge]][metalsmith-url]
8
8
  [![npm: version][npm-badge]][npm-url]
9
9
  [![license: MIT][license-badge]][license-url]
10
- [![coverage][coverage-badge]][coverage-url]
10
+ [![test coverage][coverage-badge]][coverage-url]
11
11
  [![ESM/CommonJS][modules-badge]][npm-url]
12
+ [![Known Vulnerabilities](https://snyk.io/test/npm/metalsmith-optimize-images/badge.svg)](https://snyk.io/test/npm/metalsmith-optimize-images)
12
13
 
13
14
  ## Features
14
15
 
15
16
  - **Multiple image formats**: Generates AVIF and WebP variants with JPEG/PNG fallbacks
16
17
  - **Responsive sizes**: Creates different image sizes for various device widths
18
+ - **Progressive loading**: Optional progressive image loading with low-quality placeholders
17
19
  - **Lazy loading**: Uses native browser lazy loading for better performance
18
20
  - **Content-based hashing**: Adds hash to filenames for optimal caching
19
21
  - **Layout shift prevention**: Adds width/height attributes
20
22
  - **Parallel processing**: Processes images in parallel for faster builds
21
23
  - **Metadata generation**: Creates a JSON file with image information
22
24
  - **Configurable compression**: Customize compression settings per format
25
+ - **ESM and CommonJS support**:
26
+ - ESM: `import optimizeImages from 'metalsmith-optimize-images'`
27
+ - CommonJS: `const optimizeImages = require('metalsmith-optimize-images')`
23
28
 
24
29
  ## Installation
25
30
 
@@ -27,41 +32,39 @@ Metalsmith plugin for generating responsive images with optimal formats
27
32
  npm install metalsmith-optimize-images
28
33
  ```
29
34
 
30
- ### Requirements
35
+ ## Requirements
31
36
 
32
- - Node.js 18.0.0 or newer
33
- - Metalsmith 2.5.0 or newer
37
+ - Node.js >=18.0.0
38
+ - Metalsmith >=2.5.0
34
39
 
35
- ## Usage
40
+ ## Platform Testing Status
36
41
 
37
- ### ESM
42
+ - ✅ **macOS**: Fully tested and working
43
+ - 🔄 **Windows**: Seeking community feedback on Sharp.js compatibility
44
+ - 🔄 **Linux**: Seeking validation across different distributions
45
+ - 🔄 **CI/CD**: Testing in various containerized environments
38
46
 
39
- ```javascript
40
- import metalsmith from 'metalsmith';
41
- import optimizeImages from 'metalsmith-optimize-images';
47
+ **Help us reach 1.0.0**: If you test this plugin on Windows, Linux, or in production environments, please [share your experience](https://github.com/wernerglinka/metalsmith-optimize-images/issues)!
42
48
 
43
- metalsmith.use(
44
- optimizeImages({
45
- // configuration options
46
- widths: [320, 640, 960, 1280, 1920],
47
- formats: ['avif', 'webp', 'original']
48
- })
49
- );
50
- ```
49
+ ## Usage
51
50
 
52
- ### CommonJS
51
+ > This plugin **must** be run after assets are copied but before any final HTML processing.
53
52
 
54
53
  ```javascript
55
- const metalsmith = require('metalsmith');
56
- const optimizeImages = require('metalsmith-optimize-images');
57
-
58
- metalsmith.use(
59
- optimizeImages({
60
- // configuration options
61
- widths: [320, 640, 960, 1280, 1920],
62
- formats: ['avif', 'webp', 'original']
63
- })
64
- );
54
+ metalsmith
55
+ .use(
56
+ assets({
57
+ source: 'lib/assets/', // Where to find assets
58
+ destination: 'assets/' // Where to copy assets
59
+ })
60
+ )
61
+ .use(
62
+ optimizeImages({
63
+ // configuration options
64
+ widths: [320, 640, 960, 1280, 1920],
65
+ formats: ['avif', 'webp', 'original']
66
+ })
67
+ );
65
68
  ```
66
69
 
67
70
  ## Options
@@ -81,6 +84,8 @@ metalsmith.use(
81
84
  | `sizes` | `string` | `(max-width: 768px) 100vw, 75vw` | Default sizes attribute |
82
85
  | `concurrency` | `number` | `5` | Process N images at a time |
83
86
  | `generateMetadata` | `boolean` | `false` | Generate a metadata JSON file |
87
+ | `isProgressive` | `boolean` | `false` | Enable progressive image loading |
88
+ | `placeholder` | `object` | See below | Placeholder image settings |
84
89
 
85
90
  ### Default Format Options
86
91
 
@@ -93,8 +98,20 @@ metalsmith.use(
93
98
  }
94
99
  ```
95
100
 
101
+ ### Default Placeholder Options
102
+
103
+ ```javascript
104
+ {
105
+ width: 50, // Width of placeholder image
106
+ quality: 30, // Quality of placeholder image
107
+ blur: 10 // Blur amount for placeholder
108
+ }
109
+ ```
110
+
96
111
  ## How It Works
97
112
 
113
+ ### Standard Mode (default)
114
+
98
115
  The plugin:
99
116
 
100
117
  1. Scans HTML files for image tags
@@ -104,6 +121,18 @@ The plugin:
104
121
  5. Adds width/height attributes to prevent layout shifts
105
122
  6. Implements native lazy loading for better performance
106
123
 
124
+ ### Progressive Mode (experimental)
125
+
126
+ When `isProgressive: true` is enabled:
127
+
128
+ 1. Generates low-quality placeholder images (small, blurred)
129
+ 2. Creates wrapper elements with both placeholder and high-resolution images
130
+ 3. Uses Intersection Observer to load high-resolution images on demand
131
+ 4. Implements smooth transitions between placeholder and final image
132
+ 5. Uses modern `createImageBitmap()` for reliable format detection (AVIF/WebP support)
133
+ 6. Maintains proper aspect ratios using original image dimensions
134
+ 7. Provides CSS and JavaScript for progressive loading behavior
135
+
107
136
  ## Examples
108
137
 
109
138
  ### Basic usage with defaults
@@ -141,6 +170,27 @@ metalsmith.use(
141
170
  );
142
171
  ```
143
172
 
173
+ ### Progressive loading configuration
174
+
175
+ ```javascript
176
+ metalsmith.use(
177
+ optimizeImages({
178
+ // Enable progressive loading
179
+ isProgressive: true,
180
+
181
+ // Customize placeholder settings
182
+ placeholder: {
183
+ width: 40, // Smaller placeholder
184
+ quality: 20, // Lower quality for faster loading
185
+ blur: 15 // More blur for artistic effect
186
+ },
187
+
188
+ // Progressive mode works best with original format only
189
+ formats: ['original']
190
+ })
191
+ );
192
+ ```
193
+
144
194
  ### Excluding specific images
145
195
 
146
196
  Add the `data-no-responsive` attribute to any image you don't want processed:
@@ -149,46 +199,110 @@ Add the `data-no-responsive` attribute to any image you don't want processed:
149
199
  <img src="image.jpg" data-no-responsive alt="This image won't be processed" />
150
200
  ```
151
201
 
152
- ## Test Coverage
202
+ ## Progressive Loading
153
203
 
154
- This project maintains a high level of test coverage to ensure reliability.
204
+ ### Overview
155
205
 
156
- ## Debug
206
+ Progressive loading provides a smooth user experience by:
157
207
 
158
- For debugging, use Metalsmith's debug mode:
208
+ 1. **Immediate display**: Shows a low-quality placeholder instantly
209
+ 2. **Smooth transitions**: Fades from placeholder to high-quality image
210
+ 3. **Lazy loading**: Only loads high-resolution images when they enter the viewport
211
+ 4. **Format optimization**: Automatically serves the best supported format
159
212
 
160
- ```javascript
161
- // Enable debug output by setting the DEBUG environment variable
162
- // DEBUG=metalsmith-responsive-images node build.js
213
+ ### Implementation
163
214
 
164
- const metalsmith = Metalsmith(__dirname).use(optimizeImages());
165
- // other plugins...
166
- ```
215
+ When progressive mode is enabled, the plugin:
167
216
 
168
- You can also use the [metalsmith-debug](https://github.com/metalsmith/metalsmith-debug) plugin:
217
+ - Generates small, blurred placeholder images
218
+ - Creates wrapper elements with proper aspect ratios
219
+ - Includes JavaScript for intersection observer-based loading
220
+ - Provides CSS for smooth transitions
169
221
 
170
- ```javascript
171
- const debug = require('metalsmith-debug');
222
+ ### HTML Output
223
+
224
+ **Standard mode:**
172
225
 
173
- const metalsmith = Metalsmith(__dirname)
174
- .use(debug()) // Add the metalsmith-debug plugin
175
- .use(optimizeImages());
176
- // other plugins...
226
+ ```html
227
+ <picture>
228
+ <source
229
+ type="image/avif"
230
+ srcset="image-320w.avif 320w, image-640w.avif 640w"
231
+ sizes="(max-width: 768px) 100vw, 75vw"
232
+ />
233
+ <source
234
+ type="image/webp"
235
+ srcset="image-320w.webp 320w, image-640w.webp 640w"
236
+ sizes="(max-width: 768px) 100vw, 75vw"
237
+ />
238
+ <img
239
+ src="image-640w.jpg"
240
+ srcset="image-320w.jpg 320w, image-640w.jpg 640w"
241
+ sizes="(max-width: 768px) 100vw, 75vw"
242
+ alt="Description"
243
+ loading="lazy"
244
+ />
245
+ </picture>
177
246
  ```
178
247
 
179
- Or with the CLI:
248
+ **Progressive mode:**
180
249
 
181
- ```json
182
- {
183
- "plugins": {
184
- "metalsmith-debug": true,
185
- "metalsmith-optimize-images": {
186
- "widths": [320, 640, 960, 1280, 1920]
187
- }
188
- }
250
+ ```html
251
+ <div class="responsive-wrapper js-progressive-image-wrapper" style="aspect-ratio: 1280/720">
252
+ <img class="low-res" src="/assets/images/responsive/image-placeholder.jpg" alt="Description" />
253
+ <img class="high-res" src="" alt="Description" data-source="/assets/images/responsive/image-960w.jpg" />
254
+ </div>
255
+ ```
256
+
257
+ ### CSS Requirements
258
+
259
+ The plugin provides CSS for progressive loading, but you can customize it:
260
+
261
+ ```css
262
+ .responsive-wrapper {
263
+ position: relative;
264
+ overflow: hidden;
265
+ background-color: #f0f0f0;
266
+ }
267
+
268
+ .responsive-wrapper .low-res {
269
+ position: absolute;
270
+ top: 0;
271
+ left: 0;
272
+ width: 100%;
273
+ height: 100%;
274
+ object-fit: cover;
275
+ transition: opacity 0.4s ease;
276
+ }
277
+
278
+ .responsive-wrapper .high-res {
279
+ position: absolute;
280
+ top: 0;
281
+ left: 0;
282
+ width: 100%;
283
+ height: 100%;
284
+ object-fit: cover;
285
+ opacity: 0;
286
+ transition: opacity 0.4s ease;
287
+ }
288
+
289
+ .responsive-wrapper.done .high-res {
290
+ opacity: 1;
291
+ }
292
+
293
+ .responsive-wrapper.done .low-res {
294
+ opacity: 0;
189
295
  }
190
296
  ```
191
297
 
298
+ ## Debug
299
+
300
+ To enable debug logs, set the DEBUG environment variable to metalsmith-optimize-images\*:
301
+
302
+ ```javascript
303
+ metalsmith.env('DEBUG', 'metalsmith-optimize-images*');
304
+ ```
305
+
192
306
  ## CLI Usage
193
307
 
194
308
  ### Metalsmith CLI
@@ -204,6 +318,27 @@ Or with the CLI:
204
318
  }
205
319
  ```
206
320
 
321
+ ## Feedback & Testing
322
+
323
+ This plugin is approaching 1.0.0 and we'd love your feedback! Please test and report:
324
+
325
+ ### Especially Needed
326
+
327
+ - **Windows compatibility**: Sharp.js native compilation and image processing
328
+ - **Large image batches**: Performance with 50+ images
329
+ - **Memory usage**: Resource consumption in your environment
330
+ - **Cross-platform consistency**: Image quality and file sizes across platforms
331
+ - **Progressive loading**: Behavior across different browsers
332
+
333
+ ### Current Status
334
+
335
+ - ✅ 96.06% test coverage with comprehensive edge case handling
336
+ - ✅ Real Metalsmith integration tests (no mocks)
337
+ - ✅ Tested on macOS with Node.js 18+
338
+ - 🔄 Seeking broader platform validation
339
+
340
+ **Report issues or success stories**: [GitHub Issues](https://github.com/wernerglinka/metalsmith-optimize-images/issues)
341
+
207
342
  ## License
208
343
 
209
344
  MIT
@@ -214,6 +349,6 @@ MIT
214
349
  [metalsmith-url]: https://metalsmith.io
215
350
  [license-badge]: https://img.shields.io/github/license/wernerglinka/metalsmith-optimize-images
216
351
  [license-url]: LICENSE
217
- [coverage-badge]: https://img.shields.io/badge/test%20coverage-95%25-brightgreen
218
- [coverage-url]: #test-coverage
352
+ [coverage-badge]: https://img.shields.io/badge/test%20coverage-96%25-brightgreen
353
+ [coverage-url]: https://github.com/wernerglinka/metalsmith-optimize-images/actions/workflows/test.yml
219
354
  [modules-badge]: https://img.shields.io/badge/modules-ESM%2FCJS-blue