metalsmith-optimize-images 0.9.0 → 0.10.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,7 +1,5 @@
1
1
  # metalsmith-optimize-images
2
2
 
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
-
5
3
  Metalsmith plugin for generating responsive images with optimal formats
6
4
 
7
5
  [![metalsmith:plugin][metalsmith-badge]][metalsmith-url]
@@ -11,16 +9,19 @@ Metalsmith plugin for generating responsive images with optimal formats
11
9
  [![ESM/CommonJS][modules-badge]][npm-url]
12
10
  [![Known Vulnerabilities](https://snyk.io/test/npm/metalsmith-optimize-images/badge.svg)](https://snyk.io/test/npm/metalsmith-optimize-images)
13
11
 
12
+ > This Metalsmith plugin is under active development. The API is stable, but breaking changes may occur before reaching 1.0.0.
13
+
14
14
  ## Features
15
15
 
16
16
  - **Multiple image formats**: Generates AVIF and WebP variants with JPEG/PNG fallbacks
17
17
  - **Responsive sizes**: Creates different image sizes for various device widths
18
+ - **Background image support**: Automatically processes unused images for CSS `image-set()` backgrounds
18
19
  - **Progressive loading**: Optional progressive image loading with low-quality placeholders
19
- - **Lazy loading**: Uses native browser lazy loading for better performance
20
+ - **Lazy loading**: Uses native browser lazy loading
20
21
  - **Content-based hashing**: Adds hash to filenames for optimal caching
21
22
  - **Layout shift prevention**: Adds width/height attributes
22
- - **Parallel processing**: Processes images in parallel for faster builds
23
- - **Metadata generation**: Creates a JSON file with image information
23
+ - **Parallel processing**: Processes images in parallel
24
+ - **Metadata generation**: Creates a JSON manifest with image information and variants
24
25
  - **Configurable compression**: Customize compression settings per format
25
26
  - **ESM and CommonJS support**:
26
27
  - ESM: `import optimizeImages from 'metalsmith-optimize-images'`
@@ -32,20 +33,6 @@ Metalsmith plugin for generating responsive images with optimal formats
32
33
  npm install metalsmith-optimize-images
33
34
  ```
34
35
 
35
- ## Requirements
36
-
37
- - Node.js >=18.0.0
38
- - Metalsmith >=2.5.0
39
-
40
- ## Platform Testing Status
41
-
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
46
-
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)!
48
-
49
36
  ## Usage
50
37
 
51
38
  > This plugin **must** be run after assets are copied but before any final HTML processing.
@@ -69,23 +56,24 @@ metalsmith
69
56
 
70
57
  ## Options
71
58
 
72
- | Option | Type | Default | Description |
73
- | --------------------- | ---------- | ------------------------------------- | ---------------------------------------- |
74
- | `widths` | `number[]` | `[320, 640, 960, 1280, 1920]` | Image sizes to generate |
75
- | `formats` | `string[]` | `['avif', 'webp', 'original']` | Image formats in order of preference |
76
- | `formatOptions` | `object` | See below | Format-specific compression settings |
77
- | `htmlPattern` | `string` | `**/*.html` | Glob pattern to match HTML files |
78
- | `imgSelector` | `string` | `img:not([data-no-responsive])` | CSS selector for images to process |
79
- | `outputDir` | `string` | `assets/images/responsive` | Where to store the responsive images |
80
- | `outputPattern` | `string` | `[filename]-[width]w-[hash].[format]` | Filename pattern with tokens |
81
- | `skipLarger` | `boolean` | `true` | Don't upscale images |
82
- | `lazy` | `boolean` | `true` | Use native lazy loading |
83
- | `dimensionAttributes` | `boolean` | `true` | Add width/height to prevent layout shift |
84
- | `sizes` | `string` | `(max-width: 768px) 100vw, 75vw` | Default sizes attribute |
85
- | `concurrency` | `number` | `5` | Process N images at a time |
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 |
59
+ | Option | Type | Default | Description |
60
+ | --------------------- | ---------- | ------------------------------------- | ------------------------------------------------------------------------------ |
61
+ | `widths` | `number[]` | `[320, 640, 960, 1280, 1920]` | Image sizes to generate |
62
+ | `formats` | `string[]` | `['avif', 'webp', 'original']` | Image formats in order of preference |
63
+ | `formatOptions` | `object` | See below | Format-specific compression settings |
64
+ | `htmlPattern` | `string` | `**/*.html` | Glob pattern to match HTML files |
65
+ | `imgSelector` | `string` | `img:not([data-no-responsive])` | CSS selector for images to process |
66
+ | `outputDir` | `string` | `assets/images/responsive` | Where to store the responsive images |
67
+ | `outputPattern` | `string` | `[filename]-[width]w-[hash].[format]` | Filename pattern with tokens |
68
+ | `skipLarger` | `boolean` | `true` | Don't upscale images |
69
+ | `lazy` | `boolean` | `true` | Use native lazy loading |
70
+ | `dimensionAttributes` | `boolean` | `true` | Add width/height to prevent layout shift |
71
+ | `sizes` | `string` | `(max-width: 768px) 100vw, 75vw` | Default sizes attribute |
72
+ | `concurrency` | `number` | `5` | Process N images at a time |
73
+ | `generateMetadata` | `boolean` | `false` | Generate a metadata JSON file at `{outputDir}/responsive-images-manifest.json` |
74
+ | `isProgressive` | `boolean` | `false` | Enable progressive image loading |
75
+ | `placeholder` | `object` | See below | Placeholder image settings |
76
+ | `processUnusedImages` | `boolean` | `true` | Process unused images for background use |
89
77
 
90
78
  ### Default Format Options
91
79
 
@@ -112,7 +100,9 @@ metalsmith
112
100
 
113
101
  ### Standard Mode (default)
114
102
 
115
- The plugin:
103
+ The plugin operates in two phases:
104
+
105
+ **Phase 1: HTML-Referenced Images**
116
106
 
117
107
  1. Scans HTML files for image tags
118
108
  2. Processes each image to create multiple sizes and formats
@@ -121,6 +111,13 @@ The plugin:
121
111
  5. Adds width/height attributes to prevent layout shifts
122
112
  6. Implements native lazy loading for better performance
123
113
 
114
+ **Phase 2: Background Images (when `processUnusedImages: true`)**
115
+
116
+ 1. Finds images that weren't processed in Phase 1
117
+ 2. Generates 1x/2x variants (half size and original size) for retina displays
118
+ 3. Creates all configured formats (AVIF, WebP, original)
119
+ 4. Suitable for use with CSS `image-set()` for background images
120
+
124
121
  ### Progressive Mode (experimental)
125
122
 
126
123
  When `isProgressive: true` is enabled:
@@ -164,6 +161,9 @@ metalsmith.use(
164
161
  // Custom output directory
165
162
  outputDir: 'images/processed',
166
163
 
164
+ // Generate metadata manifest
165
+ generateMetadata: true, // Creates images/processed/responsive-images-manifest.json
166
+
167
167
  // Don't add lazy loading
168
168
  lazy: false
169
169
  })
@@ -199,6 +199,79 @@ Add the `data-no-responsive` attribute to any image you don't want processed:
199
199
  <img src="image.jpg" data-no-responsive alt="This image won't be processed" />
200
200
  ```
201
201
 
202
+ ## Background Images
203
+
204
+ The plugin automatically processes images that aren't referenced in HTML for use as CSS background images. This feature is enabled by default (`processUnusedImages: true`).
205
+
206
+ ### How Background Processing Works
207
+
208
+ After processing HTML-referenced images, the plugin:
209
+
210
+ 1. **Scans the Metalsmith files object** for all images
211
+ 2. **Excludes already-processed images** (those found during HTML scanning)
212
+ 3. **Excludes responsive variants** (generated images in the outputDir)
213
+ 4. **Generates 1x/2x variants** using actual image dimensions:
214
+ - **1x variant**: Half the original size for regular displays
215
+ - **2x variant**: Original image size for retina displays (sharper on high-DPI screens)
216
+ 5. **Creates all formats** (AVIF, WebP, original) for optimal browser support
217
+
218
+ ### Using Background Images with CSS
219
+
220
+ For an image like `images/hero.jpg` (1920x1080 pixels), the plugin generates variants like:
221
+
222
+ ```
223
+ assets/images/responsive/hero-960w.avif (1x - half 960px width for regular displays)
224
+ assets/images/responsive/hero-1920w.avif (2x - original 1920px width, sharper on retina)
225
+ assets/images/responsive/hero-960w.webp (1x - half 960px width for regular displays)
226
+ assets/images/responsive/hero-1920w.webp (2x - original 1920px width, sharper on retina)
227
+ assets/images/responsive/hero-960w.jpg (1x - half 960px width for regular displays)
228
+ assets/images/responsive/hero-1920w.jpg (2x - original 1920px width, sharper on retina)
229
+ ```
230
+
231
+ **Note**: Background images are generated **without hashes** for easier CSS authoring. HTML images still include hashes for cache-busting.
232
+
233
+ Use them in CSS with `image-set()`:
234
+
235
+ ```css
236
+ .hero {
237
+ background-image: image-set(
238
+ url('/assets/images/responsive/hero-960w.avif') 1x,
239
+ url('/assets/images/responsive/hero-1920w.avif') 2x,
240
+ url('/assets/images/responsive/hero-960w.webp') 1x,
241
+ url('/assets/images/responsive/hero-1920w.webp') 2x,
242
+ url('/assets/images/responsive/hero-960w.jpg') 1x,
243
+ url('/assets/images/responsive/hero-1920w.jpg') 2x
244
+ );
245
+ background-size: cover;
246
+ background-position: center;
247
+ }
248
+ ```
249
+
250
+ ### Background Image Configuration
251
+
252
+ ```javascript
253
+ metalsmith.use(
254
+ optimizeImages({
255
+ // Standard HTML image processing
256
+ widths: [320, 640, 960, 1280, 1920],
257
+ formats: ['avif', 'webp', 'original'],
258
+
259
+ // Background image processing
260
+ processUnusedImages: true, // Enable background processing
261
+
262
+ // Generate metadata to see all variants
263
+ generateMetadata: true
264
+ })
265
+ );
266
+ ```
267
+
268
+ ### Benefits of Background Image Processing
269
+
270
+ - **Automatic format optimization** - Browser selects best supported format
271
+ - **Retina display support** - 2x variants provide crisp images on high-DPI screens
272
+ - **No manual work** - Plugin automatically finds and processes unused images in Metalsmith files object
273
+ - **Consistent workflow** - Same formats and quality settings as HTML images
274
+
202
275
  ## Progressive Loading
203
276
 
204
277
  ### Overview
@@ -295,6 +368,37 @@ The plugin provides CSS for progressive loading, but you can customize it:
295
368
  }
296
369
  ```
297
370
 
371
+ ## Metadata Manifest
372
+
373
+ When `generateMetadata: true` is enabled, the plugin creates a JSON file at `{outputDir}/responsive-images-manifest.json` containing detailed information about all processed images:
374
+
375
+ ```json
376
+ {
377
+ "images/hero.jpg": [
378
+ {
379
+ "path": "assets/images/responsive/hero-320w-a1b2c3d4.avif",
380
+ "width": 320,
381
+ "height": 180,
382
+ "format": "avif",
383
+ "size": 8432
384
+ },
385
+ {
386
+ "path": "assets/images/responsive/hero-320w-a1b2c3d4.webp",
387
+ "width": 320,
388
+ "height": 180,
389
+ "format": "webp",
390
+ "size": 12658
391
+ }
392
+ ]
393
+ }
394
+ ```
395
+
396
+ This manifest is useful for:
397
+
398
+ - **Debugging**: Verify which variants were generated
399
+ - **Integration**: Use variant information in other tools
400
+ - **Performance analysis**: Compare file sizes across formats
401
+
298
402
  ## Debug
299
403
 
300
404
  To enable debug logs, set the DEBUG environment variable to metalsmith-optimize-images\*:
@@ -318,30 +422,19 @@ metalsmith.env('DEBUG', 'metalsmith-optimize-images*');
318
422
  }
319
423
  ```
320
424
 
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
425
+ ## License
332
426
 
333
- ### Current Status
427
+ MIT
334
428
 
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
429
+ ## Development transparency
339
430
 
340
- **Report issues or success stories**: [GitHub Issues](https://github.com/wernerglinka/metalsmith-optimize-images/issues)
431
+ Portions of this project were developed with the assistance of AI tools including Claude and Claude Code. These tools were used to:
341
432
 
342
- ## License
433
+ - Generate or refactor code
434
+ - Assist with documentation
435
+ - Troubleshoot bugs and explore alternative approaches
343
436
 
344
- MIT
437
+ All AI-assisted code has been reviewed and tested to ensure it meets project standards. See the included [CLAUDE.md](CLAUDE.md) file for more details.
345
438
 
346
439
  [npm-badge]: https://img.shields.io/npm/v/metalsmith-optimize-images.svg
347
440
  [npm-url]: https://www.npmjs.com/package/metalsmith-optimize-images
@@ -349,6 +442,6 @@ MIT
349
442
  [metalsmith-url]: https://metalsmith.io
350
443
  [license-badge]: https://img.shields.io/github/license/wernerglinka/metalsmith-optimize-images
351
444
  [license-url]: LICENSE
352
- [coverage-badge]: https://img.shields.io/badge/test%20coverage-96%25-brightgreen
445
+ [coverage-badge]: https://img.shields.io/badge/test%20coverage-95%25-brightgreen
353
446
  [coverage-url]: https://github.com/wernerglinka/metalsmith-optimize-images/actions/workflows/test.yml
354
447
  [modules-badge]: https://img.shields.io/badge/modules-ESM%2FCJS-blue