sharp 0.29.3 → 0.30.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/lib/output.js CHANGED
@@ -22,14 +22,15 @@ const formats = new Map([
22
22
  ['j2c', 'jp2']
23
23
  ]);
24
24
 
25
- const errMagickSave = new Error('GIF output requires libvips with support for ImageMagick');
26
25
  const errJp2Save = new Error('JP2 output requires libvips with support for OpenJPEG');
27
26
 
27
+ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
28
+
28
29
  /**
29
30
  * Write output image data to a file.
30
31
  *
31
32
  * If an explicit output format is not selected, it will be inferred from the extension,
32
- * with JPEG, PNG, WebP, AVIF, TIFF, DZI, and libvips' V format supported.
33
+ * with JPEG, PNG, WebP, AVIF, TIFF, GIF, DZI, and libvips' V format supported.
33
34
  * Note that raw pixel data is only supported for buffer output.
34
35
  *
35
36
  * By default all metadata will be removed, which includes EXIF-based orientation.
@@ -63,8 +64,6 @@ function toFile (fileOut, callback) {
63
64
  err = new Error('Missing output file path');
64
65
  } else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
65
66
  err = new Error('Cannot use same file for input and output');
66
- } else if (this.options.formatOut === 'input' && fileOut.toLowerCase().endsWith('.gif') && !this.constructor.format.magick.output.file) {
67
- err = errMagickSave;
68
67
  }
69
68
  if (err) {
70
69
  if (is.fn(callback)) {
@@ -81,9 +80,9 @@ function toFile (fileOut, callback) {
81
80
 
82
81
  /**
83
82
  * Write output to a Buffer.
84
- * JPEG, PNG, WebP, AVIF, TIFF and raw pixel data output are supported.
83
+ * JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
85
84
  *
86
- * If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
85
+ * If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
87
86
  *
88
87
  * By default all metadata will be removed, which includes EXIF-based orientation.
89
88
  * See {@link withMetadata} for control over this.
@@ -169,7 +168,7 @@ function toBuffer (options, callback) {
169
168
  * })
170
169
  * .toBuffer();
171
170
  *
172
- * * @example
171
+ * @example
173
172
  * // Set output metadata to 96 DPI
174
173
  * const data = await sharp(input)
175
174
  * .withMetadata({ density: 96 })
@@ -374,6 +373,7 @@ function jpeg (options) {
374
373
  * @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
375
374
  * @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support
376
375
  * @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`
376
+ * @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true`
377
377
  * @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`
378
378
  * @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`
379
379
  * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`
@@ -398,7 +398,7 @@ function png (options) {
398
398
  }
399
399
  if (is.defined(options.palette)) {
400
400
  this._setBooleanOption('pngPalette', options.palette);
401
- } else if (is.defined(options.quality) || is.defined(options.colours || options.colors) || is.defined(options.dither)) {
401
+ } else if ([options.quality, options.effort, options.colours, options.colors, options.dither].some(is.defined)) {
402
402
  this._setBooleanOption('pngPalette', true);
403
403
  }
404
404
  if (this.options.pngPalette) {
@@ -409,10 +409,17 @@ function png (options) {
409
409
  throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
410
410
  }
411
411
  }
412
+ if (is.defined(options.effort)) {
413
+ if (is.integer(options.effort) && is.inRange(options.effort, 1, 10)) {
414
+ this.options.pngEffort = options.effort;
415
+ } else {
416
+ throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
417
+ }
418
+ }
412
419
  const colours = options.colours || options.colors;
413
420
  if (is.defined(colours)) {
414
421
  if (is.integer(colours) && is.inRange(colours, 2, 256)) {
415
- this.options.pngBitdepth = 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
422
+ this.options.pngBitdepth = bitdepthFromColourCount(colours);
416
423
  } else {
417
424
  throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
418
425
  }
@@ -441,7 +448,7 @@ function png (options) {
441
448
  * @example
442
449
  * // Optimise the file size of an animated WebP
443
450
  * const outputWebp = await sharp(inputWebp, { animated: true })
444
- * .webp({ reductionEffort: 6 })
451
+ * .webp({ effort: 6 })
445
452
  * .toBuffer();
446
453
  *
447
454
  * @param {Object} [options] - output options
@@ -450,69 +457,111 @@ function png (options) {
450
457
  * @param {boolean} [options.lossless=false] - use lossless compression mode
451
458
  * @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
452
459
  * @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
453
- * @param {number} [options.reductionEffort=4] - level of CPU effort to reduce file size, integer 0-6
454
- * @param {number} [options.pageHeight] - page height for animated output
460
+ * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
455
461
  * @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
456
- * @param {number[]} [options.delay] - list of delays between animation frames (in milliseconds)
462
+ * @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
457
463
  * @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
458
464
  * @returns {Sharp}
459
465
  * @throws {Error} Invalid options
460
466
  */
461
467
  function webp (options) {
462
- if (is.object(options) && is.defined(options.quality)) {
463
- if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
464
- this.options.webpQuality = options.quality;
465
- } else {
466
- throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
468
+ if (is.object(options)) {
469
+ if (is.defined(options.quality)) {
470
+ if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
471
+ this.options.webpQuality = options.quality;
472
+ } else {
473
+ throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
474
+ }
467
475
  }
468
- }
469
- if (is.object(options) && is.defined(options.alphaQuality)) {
470
- if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
471
- this.options.webpAlphaQuality = options.alphaQuality;
472
- } else {
473
- throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
476
+ if (is.defined(options.alphaQuality)) {
477
+ if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
478
+ this.options.webpAlphaQuality = options.alphaQuality;
479
+ } else {
480
+ throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
481
+ }
474
482
  }
475
- }
476
- if (is.object(options) && is.defined(options.lossless)) {
477
- this._setBooleanOption('webpLossless', options.lossless);
478
- }
479
- if (is.object(options) && is.defined(options.nearLossless)) {
480
- this._setBooleanOption('webpNearLossless', options.nearLossless);
481
- }
482
- if (is.object(options) && is.defined(options.smartSubsample)) {
483
- this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
484
- }
485
- if (is.object(options) && is.defined(options.reductionEffort)) {
486
- if (is.integer(options.reductionEffort) && is.inRange(options.reductionEffort, 0, 6)) {
487
- this.options.webpReductionEffort = options.reductionEffort;
488
- } else {
489
- throw is.invalidParameterError('reductionEffort', 'integer between 0 and 6', options.reductionEffort);
483
+ if (is.defined(options.lossless)) {
484
+ this._setBooleanOption('webpLossless', options.lossless);
485
+ }
486
+ if (is.defined(options.nearLossless)) {
487
+ this._setBooleanOption('webpNearLossless', options.nearLossless);
488
+ }
489
+ if (is.defined(options.smartSubsample)) {
490
+ this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
491
+ }
492
+ const effort = options.effort || options.reductionEffort;
493
+ if (is.defined(effort)) {
494
+ if (is.integer(effort) && is.inRange(effort, 0, 6)) {
495
+ this.options.webpEffort = effort;
496
+ } else {
497
+ throw is.invalidParameterError('effort', 'integer between 0 and 6', effort);
498
+ }
490
499
  }
491
500
  }
492
-
493
501
  trySetAnimationOptions(options, this.options);
494
502
  return this._updateFormatOut('webp', options);
495
503
  }
496
504
 
497
505
  /**
498
- * Use these GIF options for output image.
506
+ * Use these GIF options for the output image.
499
507
  *
500
- * Requires libvips compiled with support for ImageMagick or GraphicsMagick.
501
- * The prebuilt binaries do not include this - see
502
- * {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
508
+ * The first entry in the palette is reserved for transparency.
509
+ *
510
+ * @since 0.30.0
511
+ *
512
+ * @example
513
+ * // Convert PNG to GIF
514
+ * await sharp(pngBuffer)
515
+ * .gif()
516
+ * .toBuffer());
517
+ *
518
+ * @example
519
+ * // Convert animated WebP to animated GIF
520
+ * await sharp('animated.webp', { animated: true })
521
+ * .toFile('animated.gif');
522
+ *
523
+ * @example
524
+ * // Create a 128x128, cropped, non-dithered, animated thumbnail of an animated GIF
525
+ * const out = await sharp('in.gif', { animated: true })
526
+ * .resize({ width: 128, height: 128 })
527
+ * .gif({ dither: 0 })
528
+ * .toBuffer();
503
529
  *
504
530
  * @param {Object} [options] - output options
505
- * @param {number} [options.pageHeight] - page height for animated output
531
+ * @param {number} [options.colours=256] - maximum number of palette entries, including transparency, between 2 and 256
532
+ * @param {number} [options.colors=256] - alternative spelling of `options.colours`
533
+ * @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest)
534
+ * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
506
535
  * @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
507
- * @param {number[]} [options.delay] - list of delays between animation frames (in milliseconds)
536
+ * @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
508
537
  * @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
509
538
  * @returns {Sharp}
510
539
  * @throws {Error} Invalid options
511
540
  */
512
- /* istanbul ignore next */
513
541
  function gif (options) {
514
- if (!this.constructor.format.magick.output.buffer) {
515
- throw errMagickSave;
542
+ if (is.object(options)) {
543
+ const colours = options.colours || options.colors;
544
+ if (is.defined(colours)) {
545
+ if (is.integer(colours) && is.inRange(colours, 2, 256)) {
546
+ this.options.gifBitdepth = bitdepthFromColourCount(colours);
547
+ } else {
548
+ throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
549
+ }
550
+ }
551
+ if (is.defined(options.effort)) {
552
+ if (is.number(options.effort) && is.inRange(options.effort, 1, 10)) {
553
+ this.options.gifEffort = options.effort;
554
+ } else {
555
+ throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
556
+ }
557
+ }
558
+ if (is.defined(options.dither)) {
559
+ if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
560
+ this.options.gifDither = options.dither;
561
+ } else {
562
+ throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
563
+ }
564
+ }
516
565
  }
517
566
  trySetAnimationOptions(options, this.options);
518
567
  return this._updateFormatOut('gif', options);
@@ -601,20 +650,12 @@ function jp2 (options) {
601
650
  * @private
602
651
  *
603
652
  * @param {Object} [source] - output options
604
- * @param {number} [source.pageHeight] - page height for animated output
605
653
  * @param {number} [source.loop=0] - number of animation iterations, use 0 for infinite animation
606
654
  * @param {number[]} [source.delay] - list of delays between animation frames (in milliseconds)
607
655
  * @param {Object} [target] - target object for valid options
608
656
  * @throws {Error} Invalid options
609
657
  */
610
658
  function trySetAnimationOptions (source, target) {
611
- if (is.object(source) && is.defined(source.pageHeight)) {
612
- if (is.integer(source.pageHeight) && source.pageHeight > 0) {
613
- target.pageHeight = source.pageHeight;
614
- } else {
615
- throw is.invalidParameterError('pageHeight', 'integer larger than 0', source.pageHeight);
616
- }
617
- }
618
659
  if (is.object(source) && is.defined(source.loop)) {
619
660
  if (is.integer(source.loop) && is.inRange(source.loop, 0, 65535)) {
620
661
  target.loop = source.loop;
@@ -623,13 +664,16 @@ function trySetAnimationOptions (source, target) {
623
664
  }
624
665
  }
625
666
  if (is.object(source) && is.defined(source.delay)) {
626
- if (
667
+ // We allow singular values as well
668
+ if (is.integer(source.delay) && is.inRange(source.delay, 0, 65535)) {
669
+ target.delay = [source.delay];
670
+ } else if (
627
671
  Array.isArray(source.delay) &&
628
672
  source.delay.every(is.integer) &&
629
673
  source.delay.every(v => is.inRange(v, 0, 65535))) {
630
674
  target.delay = source.delay;
631
675
  } else {
632
- throw is.invalidParameterError('delay', 'array of integers between 0 and 65535', source.delay);
676
+ throw is.invalidParameterError('delay', 'integer or an array of integers between 0 and 65535', source.delay);
633
677
  }
634
678
  }
635
679
  }
@@ -660,6 +704,7 @@ function trySetAnimationOptions (source, target) {
660
704
  * @param {number} [options.tileHeight=256] - vertical tile size
661
705
  * @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
662
706
  * @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
707
+ * @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
663
708
  * @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
664
709
  * @returns {Sharp}
665
710
  * @throws {Error} Invalid options
@@ -733,6 +778,14 @@ function tiff (options) {
733
778
  throw is.invalidParameterError('predictor', 'one of: none, horizontal, float', options.predictor);
734
779
  }
735
780
  }
781
+ // resolutionUnit
782
+ if (is.defined(options.resolutionUnit)) {
783
+ if (is.string(options.resolutionUnit) && is.inArray(options.resolutionUnit, ['inch', 'cm'])) {
784
+ this.options.tiffResolutionUnit = options.resolutionUnit;
785
+ } else {
786
+ throw is.invalidParameterError('resolutionUnit', 'one of: inch, cm', options.resolutionUnit);
787
+ }
788
+ }
736
789
  }
737
790
  return this._updateFormatOut('tiff', options);
738
791
  }
@@ -750,7 +803,7 @@ function tiff (options) {
750
803
  * @param {Object} [options] - output options
751
804
  * @param {number} [options.quality=50] - quality, integer 1-100
752
805
  * @param {boolean} [options.lossless=false] - use lossless compression
753
- * @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 9 (fastest/largest)
806
+ * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
754
807
  * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
755
808
  * @returns {Sharp}
756
809
  * @throws {Error} Invalid options
@@ -771,7 +824,7 @@ function avif (options) {
771
824
  * @param {number} [options.quality=50] - quality, integer 1-100
772
825
  * @param {string} [options.compression='av1'] - compression format: av1, hevc
773
826
  * @param {boolean} [options.lossless=false] - use lossless compression
774
- * @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 9 (fastest/largest)
827
+ * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
775
828
  * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
776
829
  * @returns {Sharp}
777
830
  * @throws {Error} Invalid options
@@ -799,9 +852,15 @@ function heif (options) {
799
852
  throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
800
853
  }
801
854
  }
802
- if (is.defined(options.speed)) {
855
+ if (is.defined(options.effort)) {
856
+ if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
857
+ this.options.heifEffort = options.effort;
858
+ } else {
859
+ throw is.invalidParameterError('effort', 'integer between 0 and 9', options.effort);
860
+ }
861
+ } else if (is.defined(options.speed)) {
803
862
  if (is.integer(options.speed) && is.inRange(options.speed, 0, 9)) {
804
- this.options.heifSpeed = options.speed;
863
+ this.options.heifEffort = 9 - options.speed;
805
864
  } else {
806
865
  throw is.invalidParameterError('speed', 'integer between 0 and 9', options.speed);
807
866
  }
@@ -861,8 +920,6 @@ function raw (options) {
861
920
  * Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
862
921
  * Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
863
922
  *
864
- * Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
865
- *
866
923
  * @example
867
924
  * sharp('input.tiff')
868
925
  * .png()
@@ -882,10 +939,10 @@ function raw (options) {
882
939
  * @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
883
940
  * @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
884
941
  * @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
885
- * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`.
942
+ * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`.
886
943
  * @param {boolean} [options.centre=false] centre image in tile.
887
944
  * @param {boolean} [options.center=false] alternative spelling of centre.
888
- * @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`, sets the `@id` attribute of `info.json`
945
+ * @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json`
889
946
  * @returns {Sharp}
890
947
  * @throws {Error} Invalid parameters
891
948
  */
@@ -920,10 +977,10 @@ function tile (options) {
920
977
  }
921
978
  // Layout
922
979
  if (is.defined(options.layout)) {
923
- if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'iiif', 'zoomify'])) {
980
+ if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'iiif', 'iiif3', 'zoomify'])) {
924
981
  this.options.tileLayout = options.layout;
925
982
  } else {
926
- throw is.invalidParameterError('layout', 'one of: dz, google, iiif, zoomify', options.layout);
983
+ throw is.invalidParameterError('layout', 'one of: dz, google, iiif, iiif3, zoomify', options.layout);
927
984
  }
928
985
  }
929
986
  // Angle of rotation,
@@ -1078,6 +1135,7 @@ function _pipeline (callback) {
1078
1135
  this.push(data);
1079
1136
  }
1080
1137
  this.push(null);
1138
+ this.emit('close');
1081
1139
  });
1082
1140
  });
1083
1141
  if (this.streamInFinished) {
@@ -1093,6 +1151,7 @@ function _pipeline (callback) {
1093
1151
  this.push(data);
1094
1152
  }
1095
1153
  this.push(null);
1154
+ this.emit('close');
1096
1155
  });
1097
1156
  }
1098
1157
  return this;
package/lib/platform.js CHANGED
@@ -8,7 +8,7 @@ module.exports = function () {
8
8
  const arch = env.npm_config_arch || process.arch;
9
9
  const platform = env.npm_config_platform || process.platform;
10
10
  /* istanbul ignore next */
11
- const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
11
+ const libc = (platform === 'linux' && detectLibc.isNonGlibcLinuxSync()) ? detectLibc.familySync() : '';
12
12
 
13
13
  const platformId = [`${platform}${libc}`];
14
14
 
package/lib/resize.js CHANGED
@@ -183,6 +183,20 @@ function isRotationExpected (options) {
183
183
  * });
184
184
  *
185
185
  * @example
186
+ * sharp(input)
187
+ * .resize(200, 200, {
188
+ * fit: sharp.fit.outside,
189
+ * withoutReduction: true
190
+ * })
191
+ * .toFormat('jpeg')
192
+ * .toBuffer()
193
+ * .then(function(outputBuffer) {
194
+ * // outputBuffer contains JPEG image data
195
+ * // of at least 200 pixels wide and 200 pixels high while maintaining aspect ratio
196
+ * // and no smaller than the input image
197
+ * });
198
+ *
199
+ * @example
186
200
  * const scaleByHalf = await sharp(input)
187
201
  * .metadata()
188
202
  * .then(({ width }) => sharp(input)
@@ -200,6 +214,7 @@ function isRotationExpected (options) {
200
214
  * @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when using a `fit` of `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
201
215
  * @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
202
216
  * @param {Boolean} [options.withoutEnlargement=false] - do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option.
217
+ * @param {Boolean} [options.withoutReduction=false] - do not reduce if the width *or* height are already greater than the specified dimensions, equivalent to GraphicsMagick's `<` geometry option.
203
218
  * @param {Boolean} [options.fastShrinkOnLoad=true] - take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images.
204
219
  * @returns {Sharp}
205
220
  * @throws {Error} Invalid parameters
@@ -276,6 +291,10 @@ function resize (width, height, options) {
276
291
  if (is.defined(options.withoutEnlargement)) {
277
292
  this._setBooleanOption('withoutEnlargement', options.withoutEnlargement);
278
293
  }
294
+ // Without reduction
295
+ if (is.defined(options.withoutReduction)) {
296
+ this._setBooleanOption('withoutReduction', options.withoutReduction);
297
+ }
279
298
  // Shrink on load
280
299
  if (is.defined(options.fastShrinkOnLoad)) {
281
300
  this._setBooleanOption('fastShrinkOnLoad', options.fastShrinkOnLoad);
package/lib/sharp.js CHANGED
@@ -11,9 +11,10 @@ try {
11
11
  if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
12
12
  help.push('- Update Homebrew: "brew update && brew upgrade vips"');
13
13
  } else {
14
+ const [platform, arch] = platformAndArch.split('-');
14
15
  help.push(
15
16
  '- Install with the --verbose flag and look for errors: "npm install --ignore-scripts=false --verbose sharp"',
16
- `- Install for the current runtime: "npm install --platform=${process.platform} --arch=${process.arch} sharp"`
17
+ `- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
17
18
  );
18
19
  }
19
20
  help.push(
package/lib/utility.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
4
+ const path = require('path');
3
5
  const events = require('events');
4
6
  const detectLibc = require('detect-libc');
5
7
 
@@ -47,7 +49,22 @@ let versions = {
47
49
  };
48
50
  try {
49
51
  versions = require(`../vendor/${versions.vips}/${platformAndArch}/versions.json`);
50
- } catch (err) {}
52
+ } catch (_err) { /* ignore */ }
53
+
54
+ /**
55
+ * An Object containing the platform and architecture
56
+ * of the current and installed vendored binaries.
57
+ * @member
58
+ * @example
59
+ * console.log(sharp.vendor);
60
+ */
61
+ const vendor = {
62
+ current: platformAndArch,
63
+ installed: []
64
+ };
65
+ try {
66
+ vendor.installed = fs.readdirSync(path.join(__dirname, `../vendor/${versions.vips}`));
67
+ } catch (_err) { /* ignore */ }
51
68
 
52
69
  /**
53
70
  * Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
@@ -111,7 +128,7 @@ function concurrency (concurrency) {
111
128
  return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
112
129
  }
113
130
  /* istanbul ignore next */
114
- if (detectLibc.family === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
131
+ if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
115
132
  // Reduce default concurrency to 1 when using glibc memory allocator
116
133
  sharp.concurrency(1);
117
134
  }
@@ -176,5 +193,6 @@ module.exports = function (Sharp) {
176
193
  Sharp.format = format;
177
194
  Sharp.interpolators = interpolators;
178
195
  Sharp.versions = versions;
196
+ Sharp.vendor = vendor;
179
197
  Sharp.queue = queue;
180
198
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sharp",
3
- "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
4
- "version": "0.29.3",
3
+ "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
4
+ "version": "0.30.0",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -79,7 +79,9 @@
79
79
  "Michael Nutt <michael@nutt.im>",
80
80
  "Brad Parham <baparham@gmail.com>",
81
81
  "Taneli Vatanen <taneli.vatanen@gmail.com>",
82
- "Joris Dugué <zaruike10@gmail.com>"
82
+ "Joris Dugué <zaruike10@gmail.com>",
83
+ "Chris Banks <christopher.bradley.banks@gmail.com>",
84
+ "Ompal Singh <ompal.hitm09@gmail.com>"
83
85
  ],
84
86
  "scripts": {
85
87
  "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
@@ -124,33 +126,46 @@
124
126
  "vips"
125
127
  ],
126
128
  "dependencies": {
127
- "color": "^4.0.1",
128
- "detect-libc": "^1.0.3",
129
- "node-addon-api": "^4.2.0",
130
- "prebuild-install": "^7.0.0",
129
+ "color": "^4.2.0",
130
+ "detect-libc": "^2.0.0",
131
+ "node-addon-api": "^4.3.0",
132
+ "prebuild-install": "^7.0.1",
131
133
  "semver": "^7.3.5",
132
- "simple-get": "^4.0.0",
134
+ "simple-get": "^4.0.1",
133
135
  "tar-fs": "^2.1.1",
134
136
  "tunnel-agent": "^0.6.0"
135
137
  },
136
138
  "devDependencies": {
137
- "async": "^3.2.2",
139
+ "async": "^3.2.3",
138
140
  "cc": "^3.0.1",
139
141
  "decompress-zip": "^0.3.3",
140
142
  "documentation": "^13.2.5",
141
143
  "exif-reader": "^1.0.3",
142
144
  "icc": "^2.0.0",
143
145
  "license-checker": "^25.0.1",
144
- "mocha": "^9.1.3",
146
+ "mocha": "^9.2.0",
145
147
  "mock-fs": "^5.1.2",
146
148
  "nyc": "^15.1.0",
147
- "prebuild": "^11.0.0",
149
+ "prebuild": "^11.0.2",
148
150
  "rimraf": "^3.0.2",
149
151
  "semistandard": "^16.0.1"
150
152
  },
151
153
  "license": "Apache-2.0",
152
154
  "config": {
153
- "libvips": "8.11.3",
155
+ "libvips": "8.12.2",
156
+ "integrity": {
157
+ "darwin-arm64v8": "sha512-p46s/bbJAjkOXzPISZt9HUpG9GWjwQkYnLLRLKzsBJHLtB3X6C6Y/zXI5Hd0DOojcFkks9a0kTN+uDQ/XJY19g==",
158
+ "darwin-x64": "sha512-6vOHVZnvXwe6EXRsy29jdkUzBE6ElNpXUwd+m8vV7qy32AnXu7B9YemHsZ44vWviIwPZeXF6Nhd9EFLM0wWohw==",
159
+ "linux-arm64v8": "sha512-XwZdS63yhqLtbFtx/0eoLF/Agf5qtTrI11FMnMRpuBJWd4jHB60RAH+uzYUgoChCmKIS+AeXYMLm4d8Ns2QX8w==",
160
+ "linux-armv6": "sha512-Rh0Q0kqwPG2MjXWOkPCuPEyiUKFgKJYWLgS835D4MrXgdKr8Tft/eVrc2iGIxt2re30VpDiZ1h0Rby1aCZt2zw==",
161
+ "linux-armv7": "sha512-heTS/MsmRvu4JljINxP+vDiS9ZZfuGhr3IStb5F7Gc0/QLRhllYAg4rcO8L1eTK9sIIzG5ARvI19+YUZe7WbzA==",
162
+ "linux-x64": "sha512-SSWAwBFi0hx8V/h/v82tTFGKWTFv9FiCK3Timz5OExuI+sX1Ngrd0PVQaWXOThGNdel/fcD3Bz9YjSt4feBR1g==",
163
+ "linuxmusl-arm64v8": "sha512-Rhks+5C7p7aO6AucLT1uvzo8ohlqcqCUPgZmN+LZjsPWob/Iix3MfiDYtv/+gTvdeEfXxbCU6/YuPBwHQ7/crA==",
164
+ "linuxmusl-x64": "sha512-IOyjSQqpWVntqOUpCHVWuQwACwmmjdi15H8Pc+Ma1JkhPogTfVsFQWyL7DuOTD3Yr23EuYGzovUX00duOtfy/g==",
165
+ "win32-arm64v8": "sha512-A+Qe8Ipewtvw9ldvF6nWed2J8kphzrUE04nFeKCtNx6pfGQ/MAlCKMjt/U8VgUKNjB01zJDUW9XE0+FhGZ/UpQ==",
166
+ "win32-ia32": "sha512-cMrAvwFdDeAVnLJt0IPMPRKaIFhyXYGTprsM0DND9VUHE8F7dJMR44tS5YkXsGh1QNDtjKT6YuxAVUglmiXtpA==",
167
+ "win32-x64": "sha512-vLFIfw6aW2zABa8jpgzWDhljnE6glktrddErVyazAIoHl6BFFe/Da+LK1DbXvIYHz7fyOoKhSfCJHCiJG1Vg6w=="
168
+ },
154
169
  "runtime": "napi",
155
170
  "target": 5
156
171
  },