@@ -7,13 +7,13 @@ const {default: PQueue} = require("p-queue");
7
7
const getImageSize = require ( "image-size" ) ;
8
8
const sharp = require ( "sharp" ) ;
9
9
const brotliSize = require ( "brotli-size" ) ;
10
- const debug = require ( "debug" ) ( "EleventyImg ") ;
10
+ const { RemoteAssetCache , queue } = require ( "@11ty/eleventy-fetch " ) ;
11
11
12
12
const svgHook = require ( "./format-hooks/svg" ) ;
13
-
14
- const { RemoteAssetCache, queue} = require ( "@11ty/eleventy-fetch" ) ;
15
13
const MemoryCache = require ( "./memory-cache" ) ;
16
14
15
+ const debug = require ( "debug" ) ( "EleventyImg" ) ;
16
+
17
17
const globalOptions = {
18
18
widths : [ null ] ,
19
19
formats : [ "webp" , "jpeg" ] , // "png", "svg", "avif"
@@ -68,6 +68,13 @@ const globalOptions = {
68
68
// Advanced
69
69
useCacheValidityInHash : true ,
70
70
71
+ // When the original width is smaller than the desired output width, this is the minimum size difference
72
+ // between the next smallest image width that will generate one extra width in the output.
73
+ // e.g. when using `widths: [400, 800]`, the source image would need to be at least (400 * 1.2 =) 500px wide
74
+ // to generate two outputs (400px, 500px). If the source image is less than 500px, only one output will
75
+ // be generated (400px).
76
+ // Read more at https://github.com/11ty/eleventy-img/issues/184 and https://github.com/11ty/eleventy-img/pull/190
77
+ minimumThreshold : 1.25 ,
71
78
} ;
72
79
73
80
const MIME_TYPES = {
@@ -184,7 +191,7 @@ class Image {
184
191
return this . _contents ;
185
192
}
186
193
187
- static getValidWidths ( originalWidth , widths = [ ] , allowUpscale = false ) {
194
+ static getValidWidths ( originalWidth , widths = [ ] , allowUpscale = false , minimumThreshold = 1 ) {
188
195
// replace any falsy values with the original width
189
196
let valid = widths . map ( width => ! width || width === 'auto' ? originalWidth : width ) ;
190
197
@@ -195,7 +202,19 @@ class Image {
195
202
// This ensures that if a larger width has been requested, we're at least providing the closest
196
203
// non-upscaled image that we can.
197
204
if ( ! allowUpscale ) {
198
- valid = valid . map ( width => width > originalWidth ? originalWidth : width ) ;
205
+ let lastWidthWasBigEnough = true ; // first one is always valid
206
+ valid = valid . sort ( ( a , b ) => a - b ) . map ( width => {
207
+ if ( width > originalWidth ) {
208
+ if ( lastWidthWasBigEnough ) {
209
+ return originalWidth ;
210
+ }
211
+ return - 1 ;
212
+ }
213
+
214
+ lastWidthWasBigEnough = originalWidth > Math . floor ( width * minimumThreshold ) ;
215
+
216
+ return width ;
217
+ } ) . filter ( width => width > 0 ) ;
199
218
}
200
219
201
220
// Remove duplicates (e.g., if null happens to coincide with an explicit width
@@ -489,7 +508,7 @@ class Image {
489
508
continue ;
490
509
}
491
510
} else { // not outputting SVG (might still be SVG input though!)
492
- let widths = Image . getValidWidths ( metadata . width , this . options . widths , metadata . format === "svg" && this . options . svgAllowUpscale ) ;
511
+ let widths = Image . getValidWidths ( metadata . width , this . options . widths , metadata . format === "svg" && this . options . svgAllowUpscale , this . options . minimumThreshold ) ;
493
512
for ( let width of widths ) {
494
513
// Warning: if this is a guess via statsByDimensionsSync and that guess is wrong
495
514
// The aspect ratio will be wrong and any height/widths returned will be wrong!
@@ -599,7 +618,7 @@ class Image {
599
618
. then ( info => {
600
619
stat . size = info . size ;
601
620
return stat ;
602
- } )
621
+ } )
603
622
) ;
604
623
} else {
605
624
outputFilePromises . push ( sharpInstance . toBuffer ( { resolveWithObject : true } ) . then ( ( { data, info } ) => {
0 commit comments