sharp 0.32.6 → 0.33.0-alpha.11

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
@@ -3,7 +3,7 @@
3
3
 
4
4
  'use strict';
5
5
 
6
- const path = require('path');
6
+ const path = require('node:path');
7
7
  const is = require('./is');
8
8
  const sharp = require('./sharp');
9
9
 
@@ -86,7 +86,8 @@ function toFile (fileOut, callback) {
86
86
  }
87
87
  } else {
88
88
  this.options.fileOut = fileOut;
89
- return this._pipeline(callback);
89
+ const stack = Error();
90
+ return this._pipeline(callback, stack);
90
91
  }
91
92
  return this;
92
93
  }
@@ -157,7 +158,8 @@ function toBuffer (options, callback) {
157
158
  this.options.resolveWithObject = false;
158
159
  }
159
160
  this.options.fileOut = '';
160
- return this._pipeline(is.fn(options) ? options : callback);
161
+ const stack = Error();
162
+ return this._pipeline(is.fn(options) ? options : callback, stack);
161
163
  }
162
164
 
163
165
  /**
@@ -780,6 +782,7 @@ function trySetAnimationOptions (source, target) {
780
782
  * @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
781
783
  * @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
782
784
  * @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
785
+ * @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
783
786
  * @returns {Sharp}
784
787
  * @throws {Error} Invalid options
785
788
  */
@@ -817,6 +820,10 @@ function tiff (options) {
817
820
  throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
818
821
  }
819
822
  }
823
+ // miniswhite
824
+ if (is.defined(options.miniswhite)) {
825
+ this._setBooleanOption('tiffMiniswhite', options.miniswhite);
826
+ }
820
827
  // pyramid
821
828
  if (is.defined(options.pyramid)) {
822
829
  this._setBooleanOption('tiffPyramid', options.pyramid);
@@ -867,9 +874,6 @@ function tiff (options) {
867
874
  /**
868
875
  * Use these AVIF options for output image.
869
876
  *
870
- * Whilst it is possible to create AVIF images smaller than 16x16 pixels,
871
- * most web browsers do not display these properly.
872
- *
873
877
  * AVIF image sequences are not supported.
874
878
  *
875
879
  * @example
@@ -909,9 +913,9 @@ function avif (options) {
909
913
  *
910
914
  * @since 0.23.0
911
915
  *
912
- * @param {Object} [options] - output options
916
+ * @param {Object} options - output options
917
+ * @param {string} options.compression - compression format: av1, hevc
913
918
  * @param {number} [options.quality=50] - quality, integer 1-100
914
- * @param {string} [options.compression='av1'] - compression format: av1, hevc
915
919
  * @param {boolean} [options.lossless=false] - use lossless compression
916
920
  * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
917
921
  * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
@@ -920,6 +924,11 @@ function avif (options) {
920
924
  */
921
925
  function heif (options) {
922
926
  if (is.object(options)) {
927
+ if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
928
+ this.options.heifCompression = options.compression;
929
+ } else {
930
+ throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
931
+ }
923
932
  if (is.defined(options.quality)) {
924
933
  if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
925
934
  this.options.heifQuality = options.quality;
@@ -934,13 +943,6 @@ function heif (options) {
934
943
  throw is.invalidParameterError('lossless', 'boolean', options.lossless);
935
944
  }
936
945
  }
937
- if (is.defined(options.compression)) {
938
- if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
939
- this.options.heifCompression = options.compression;
940
- } else {
941
- throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
942
- }
943
- }
944
946
  if (is.defined(options.effort)) {
945
947
  if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
946
948
  this.options.heifEffort = options.effort;
@@ -955,6 +957,8 @@ function heif (options) {
955
957
  throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
956
958
  }
957
959
  }
960
+ } else {
961
+ throw is.invalidParameterError('options', 'Object', options);
958
962
  }
959
963
  return this._updateFormatOut('heif', options);
960
964
  }
@@ -1285,7 +1289,8 @@ function _read () {
1285
1289
  /* istanbul ignore else */
1286
1290
  if (!this.options.streamOut) {
1287
1291
  this.options.streamOut = true;
1288
- this._pipeline();
1292
+ const stack = Error();
1293
+ this._pipeline(undefined, stack);
1289
1294
  }
1290
1295
  }
1291
1296
 
@@ -1294,18 +1299,30 @@ function _read () {
1294
1299
  * Supports callback, stream and promise variants
1295
1300
  * @private
1296
1301
  */
1297
- function _pipeline (callback) {
1302
+ function _pipeline (callback, stack) {
1298
1303
  if (typeof callback === 'function') {
1299
1304
  // output=file/buffer
1300
1305
  if (this._isStreamInput()) {
1301
1306
  // output=file/buffer, input=stream
1302
1307
  this.on('finish', () => {
1303
1308
  this._flattenBufferIn();
1304
- sharp.pipeline(this.options, callback);
1309
+ sharp.pipeline(this.options, (err, data, info) => {
1310
+ if (err) {
1311
+ callback(is.nativeError(err, stack));
1312
+ } else {
1313
+ callback(null, data, info);
1314
+ }
1315
+ });
1305
1316
  });
1306
1317
  } else {
1307
1318
  // output=file/buffer, input=file/buffer
1308
- sharp.pipeline(this.options, callback);
1319
+ sharp.pipeline(this.options, (err, data, info) => {
1320
+ if (err) {
1321
+ callback(is.nativeError(err, stack));
1322
+ } else {
1323
+ callback(null, data, info);
1324
+ }
1325
+ });
1309
1326
  }
1310
1327
  return this;
1311
1328
  } else if (this.options.streamOut) {
@@ -1316,7 +1333,7 @@ function _pipeline (callback) {
1316
1333
  this._flattenBufferIn();
1317
1334
  sharp.pipeline(this.options, (err, data, info) => {
1318
1335
  if (err) {
1319
- this.emit('error', err);
1336
+ this.emit('error', is.nativeError(err, stack));
1320
1337
  } else {
1321
1338
  this.emit('info', info);
1322
1339
  this.push(data);
@@ -1332,7 +1349,7 @@ function _pipeline (callback) {
1332
1349
  // output=stream, input=file/buffer
1333
1350
  sharp.pipeline(this.options, (err, data, info) => {
1334
1351
  if (err) {
1335
- this.emit('error', err);
1352
+ this.emit('error', is.nativeError(err, stack));
1336
1353
  } else {
1337
1354
  this.emit('info', info);
1338
1355
  this.push(data);
@@ -1351,7 +1368,7 @@ function _pipeline (callback) {
1351
1368
  this._flattenBufferIn();
1352
1369
  sharp.pipeline(this.options, (err, data, info) => {
1353
1370
  if (err) {
1354
- reject(err);
1371
+ reject(is.nativeError(err, stack));
1355
1372
  } else {
1356
1373
  if (this.options.resolveWithObject) {
1357
1374
  resolve({ data, info });
@@ -1367,10 +1384,10 @@ function _pipeline (callback) {
1367
1384
  return new Promise((resolve, reject) => {
1368
1385
  sharp.pipeline(this.options, (err, data, info) => {
1369
1386
  if (err) {
1370
- reject(err);
1387
+ reject(is.nativeError(err, stack));
1371
1388
  } else {
1372
1389
  if (this.options.resolveWithObject) {
1373
- resolve({ data: data, info: info });
1390
+ resolve({ data, info });
1374
1391
  } else {
1375
1392
  resolve(data);
1376
1393
  }
package/lib/resize.js CHANGED
@@ -250,6 +250,9 @@ function resize (widthOrOptions, height, options) {
250
250
  if (isResizeExpected(this.options)) {
251
251
  this.options.debuglog('ignoring previous resize options');
252
252
  }
253
+ if (this.options.widthPost !== -1) {
254
+ this.options.debuglog('operation order will be: extract, resize, extract');
255
+ }
253
256
  if (is.defined(widthOrOptions)) {
254
257
  if (is.object(widthOrOptions) && !is.defined(options)) {
255
258
  options = widthOrOptions;
@@ -437,7 +440,7 @@ function extend (extend) {
437
440
  *
438
441
  * - Use `extract` before `resize` for pre-resize extraction.
439
442
  * - Use `extract` after `resize` for post-resize extraction.
440
- * - Use `extract` before and after for both.
443
+ * - Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
441
444
  *
442
445
  * @example
443
446
  * sharp(input)
@@ -491,70 +494,67 @@ function extract (options) {
491
494
  *
492
495
  * If the result of this operation would trim an image to nothing then no change is made.
493
496
  *
494
- * The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
495
- * will contain `trimOffsetLeft` and `trimOffsetTop` properties.
497
+ * The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
496
498
  *
497
499
  * @example
498
500
  * // Trim pixels with a colour similar to that of the top-left pixel.
499
- * sharp(input)
501
+ * await sharp(input)
500
502
  * .trim()
501
- * .toFile(output, function(err, info) {
502
- * ...
503
- * });
503
+ * .toFile(output);
504
+ *
504
505
  * @example
505
506
  * // Trim pixels with the exact same colour as that of the top-left pixel.
506
- * sharp(input)
507
- * .trim(0)
508
- * .toFile(output, function(err, info) {
509
- * ...
510
- * });
507
+ * await sharp(input)
508
+ * .trim({
509
+ * threshold: 0
510
+ * })
511
+ * .toFile(output);
512
+ *
511
513
  * @example
512
- * // Trim only pixels with a similar colour to red.
513
- * sharp(input)
514
- * .trim("#FF0000")
515
- * .toFile(output, function(err, info) {
516
- * ...
517
- * });
514
+ * // Assume input is line art and trim only pixels with a similar colour to red.
515
+ * const output = await sharp(input)
516
+ * .trim({
517
+ * background: "#FF0000",
518
+ * lineArt: true
519
+ * })
520
+ * .toBuffer();
521
+ *
518
522
  * @example
519
523
  * // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
520
- * sharp(input)
524
+ * const output = await sharp(input)
521
525
  * .trim({
522
526
  * background: "yellow",
523
527
  * threshold: 42,
524
528
  * })
525
- * .toFile(output, function(err, info) {
526
- * ...
527
- * });
529
+ * .toBuffer();
528
530
  *
529
- * @param {string|number|Object} trim - the specific background colour to trim, the threshold for doing so or an Object with both.
530
- * @param {string|Object} [trim.background='top-left pixel'] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
531
- * @param {number} [trim.threshold=10] - the allowed difference from the above colour, a positive number.
531
+ * @param {Object} [options]
532
+ * @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
533
+ * @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
534
+ * @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
532
535
  * @returns {Sharp}
533
536
  * @throws {Error} Invalid parameters
534
537
  */
535
- function trim (trim) {
536
- if (!is.defined(trim)) {
537
- this.options.trimThreshold = 10;
538
- } else if (is.string(trim)) {
539
- this._setBackgroundColourOption('trimBackground', trim);
540
- this.options.trimThreshold = 10;
541
- } else if (is.number(trim)) {
542
- if (trim >= 0) {
543
- this.options.trimThreshold = trim;
544
- } else {
545
- throw is.invalidParameterError('threshold', 'positive number', trim);
546
- }
547
- } else if (is.object(trim)) {
548
- this._setBackgroundColourOption('trimBackground', trim.background);
549
- if (!is.defined(trim.threshold)) {
550
- this.options.trimThreshold = 10;
551
- } else if (is.number(trim.threshold) && trim.threshold >= 0) {
552
- this.options.trimThreshold = trim.threshold;
538
+ function trim (options) {
539
+ this.options.trimThreshold = 10;
540
+ if (is.defined(options)) {
541
+ if (is.object(options)) {
542
+ if (is.defined(options.background)) {
543
+ this._setBackgroundColourOption('trimBackground', options.background);
544
+ }
545
+ if (is.defined(options.threshold)) {
546
+ if (is.number(options.threshold) && options.threshold >= 0) {
547
+ this.options.trimThreshold = options.threshold;
548
+ } else {
549
+ throw is.invalidParameterError('threshold', 'positive number', options.threshold);
550
+ }
551
+ }
552
+ if (is.defined(options.lineArt)) {
553
+ this._setBooleanOption('trimLineArt', options.lineArt);
554
+ }
553
555
  } else {
554
- throw is.invalidParameterError('threshold', 'positive number', trim);
556
+ throw is.invalidParameterError('trim', 'object', options);
555
557
  }
556
- } else {
557
- throw is.invalidParameterError('trim', 'string, number or object', trim);
558
558
  }
559
559
  if (isRotationExpected(this.options)) {
560
560
  this.options.rotateBeforePreExtract = true;
package/lib/sharp.js CHANGED
@@ -3,36 +3,81 @@
3
3
 
4
4
  'use strict';
5
5
 
6
- const platformAndArch = require('./platform')();
6
+ // Inspects the runtime environment and exports the relevant sharp.node binary
7
+
8
+ const { familySync, versionSync } = require('detect-libc');
9
+
10
+ const { runtimePlatformArch, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
11
+ const runtimePlatform = runtimePlatformArch();
12
+
13
+ const paths = [
14
+ `../src/build/Release/sharp-${runtimePlatform}.node`,
15
+ '../src/build/Release/sharp-wasm32.node',
16
+ `@img/sharp-${runtimePlatform}/sharp.node`,
17
+ '@img/sharp-wasm32/sharp.node'
18
+ ];
19
+
20
+ const errors = [];
21
+ for (const path of paths) {
22
+ try {
23
+ module.exports = require(path);
24
+ break;
25
+ } catch (err) {
26
+ /* istanbul ignore next */
27
+ errors.push(err);
28
+ }
29
+ }
7
30
 
8
31
  /* istanbul ignore next */
9
- try {
10
- module.exports = require(`../build/Release/sharp-${platformAndArch}.node`);
11
- } catch (err) {
12
- // Bail early if bindings aren't available
13
- const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, '', 'Possible solutions:'];
14
- if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
15
- help.push('- Update Homebrew: "brew update && brew upgrade vips"');
16
- } else {
17
- const [platform, arch] = platformAndArch.split('-');
18
- if (platform === 'linux' && /Module did not self-register/.test(err.message)) {
19
- help.push('- Using worker threads? See https://sharp.pixelplumbing.com/install#worker-threads');
32
+ if (!module.exports) {
33
+ const [isLinux, isMacOs, isWindows] = ['linux', 'darwin', 'win32'].map(os => runtimePlatform.startsWith(os));
34
+
35
+ const help = [`Could not load the "sharp" module using the ${runtimePlatform} runtime`];
36
+ errors.forEach(err => {
37
+ if (err.code !== 'MODULE_NOT_FOUND') {
38
+ help.push(`${err.code}: ${err.message}`);
20
39
  }
21
- help.push(
22
- '- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
23
- `- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
24
- );
40
+ });
41
+ const messages = errors.map(err => err.message).join(' ');
42
+ help.push('Possible solutions:');
43
+ // Common error messages
44
+ if (prebuiltPlatforms.includes(runtimePlatform)) {
45
+ const [os, cpu] = runtimePlatform.split('-');
46
+ help.push('- Add platform-specific dependencies:');
47
+ help.push(` npm install --os=${os} --cpu=${cpu} sharp`);
48
+ help.push(' or');
49
+ help.push(` npm install --force @img/sharp-${runtimePlatform}`);
50
+ } else {
51
+ help.push(`- Manually install libvips >= ${minimumLibvipsVersion}`);
52
+ help.push('- Add experimental WebAssembly-based dependencies:');
53
+ help.push(' npm install --cpu=wasm32 sharp');
25
54
  }
26
- help.push(
27
- '- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
28
- );
29
- // Check loaded
30
- if (process.platform === 'win32' || /symbol/.test(err.message)) {
31
- const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
32
- if (loadedModule) {
33
- const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
34
- help.push(`- Ensure the version of sharp aligns with the ${loadedPackage} package: "npm ls sharp"`);
35
- }
55
+ if (isLinux && /symbol not found/i.test(messages)) {
56
+ try {
57
+ const { engines } = require(`@img/sharp-libvips-${runtimePlatform}/package`);
58
+ const libcFound = `${familySync()} ${versionSync()}`;
59
+ const libcRequires = `${engines.musl ? 'musl' : 'glibc'} ${engines.musl || engines.glibc}`;
60
+ help.push('- Update your OS:');
61
+ help.push(` Found ${libcFound}`);
62
+ help.push(` Requires ${libcRequires}`);
63
+ } catch (errEngines) {}
64
+ }
65
+ if (isMacOs && /Incompatible library version/.test(messages)) {
66
+ help.push('- Update Homebrew:');
67
+ help.push(' brew update && brew upgrade vips');
68
+ }
69
+ if (errors.some(err => err.code === 'ERR_DLOPEN_DISABLED')) {
70
+ help.push('- Run Node.js without using the --no-addons flag');
71
+ }
72
+ if (process.versions.pnp) {
73
+ help.push('- Use a supported yarn linker, either pnpm or node-modules:');
74
+ help.push(' yarn config set nodeLinker node-modules');
75
+ }
76
+ // Link to installation docs
77
+ if (isWindows && /The specified procedure could not be found/.test(messages)) {
78
+ help.push('- Using the canvas package on Windows? See https://sharp.pixelplumbing.com/install#canvas-and-windows');
79
+ } else {
80
+ help.push('- Consult the installation documentation: https://sharp.pixelplumbing.com/install');
36
81
  }
37
82
  throw new Error(help.join('\n'));
38
83
  }
package/lib/utility.js CHANGED
@@ -3,15 +3,16 @@
3
3
 
4
4
  'use strict';
5
5
 
6
- const fs = require('fs');
7
- const path = require('path');
8
- const events = require('events');
6
+ const events = require('node:events');
9
7
  const detectLibc = require('detect-libc');
10
8
 
11
9
  const is = require('./is');
12
- const platformAndArch = require('./platform')();
10
+ const { runtimePlatformArch } = require('./libvips');
13
11
  const sharp = require('./sharp');
14
12
 
13
+ const runtimePlatform = runtimePlatformArch();
14
+ const libvipsVersion = sharp.libvipsVersion();
15
+
15
16
  /**
16
17
  * An Object containing nested boolean values representing the available input and output formats/methods.
17
18
  * @member
@@ -46,34 +47,34 @@ const interpolators = {
46
47
  };
47
48
 
48
49
  /**
49
- * An Object containing the version numbers of sharp, libvips and its dependencies.
50
+ * An Object containing the version numbers of sharp, libvips
51
+ * and (when using prebuilt binaries) its dependencies.
52
+ *
50
53
  * @member
51
54
  * @example
52
55
  * console.log(sharp.versions);
53
56
  */
54
57
  let versions = {
55
- vips: sharp.libvipsVersion()
58
+ vips: libvipsVersion.semver
56
59
  };
57
- try {
58
- versions = require(`../vendor/${versions.vips}/${platformAndArch}/versions.json`);
59
- } catch (_err) { /* ignore */ }
60
+ /* istanbul ignore next */
61
+ if (!libvipsVersion.isGlobal) {
62
+ if (!libvipsVersion.isWasm) {
63
+ try {
64
+ versions = require(`@img/sharp-${runtimePlatform}/versions`);
65
+ } catch (_) {
66
+ try {
67
+ versions = require(`@img/sharp-libvips-${runtimePlatform}/versions`);
68
+ } catch (_) {}
69
+ }
70
+ } else {
71
+ try {
72
+ versions = require('@img/sharp-wasm32/versions');
73
+ } catch (_) {}
74
+ }
75
+ }
60
76
  versions.sharp = require('../package.json').version;
61
77
 
62
- /**
63
- * An Object containing the platform and architecture
64
- * of the current and installed vendored binaries.
65
- * @member
66
- * @example
67
- * console.log(sharp.vendor);
68
- */
69
- const vendor = {
70
- current: platformAndArch,
71
- installed: []
72
- };
73
- try {
74
- vendor.installed = fs.readdirSync(path.join(__dirname, `../vendor/${versions.vips}`));
75
- } catch (_err) { /* ignore */ }
76
-
77
78
  /**
78
79
  * Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
79
80
  * Existing entries in the cache will be trimmed after any change in limits.
@@ -280,7 +281,6 @@ module.exports = function (Sharp) {
280
281
  Sharp.format = format;
281
282
  Sharp.interpolators = interpolators;
282
283
  Sharp.versions = versions;
283
- Sharp.vendor = vendor;
284
284
  Sharp.queue = queue;
285
285
  Sharp.block = block;
286
286
  Sharp.unblock = unblock;