sharp 0.32.6 → 0.33.0-alpha.10

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
@@ -2,10 +2,14 @@
2
2
 
3
3
  <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
4
4
 
5
- The typical use case for this high speed Node.js module
5
+ The typical use case for this high speed Node-API module
6
6
  is to convert large images in common formats to
7
7
  smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
8
8
 
9
+ It can be used with all JavaScript runtimes
10
+ that provide support for Node-API v9, including
11
+ Node.js >= 18.17.0, Deno and Bun.
12
+
9
13
  Resizing an image is typically 4x-5x faster than using the
10
14
  quickest ImageMagick and GraphicsMagick settings
11
15
  due to its use of [libvips](https://github.com/libvips/libvips).
@@ -16,7 +20,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
16
20
  As well as image resizing, operations such as
17
21
  rotation, extraction, compositing and gamma correction are available.
18
22
 
19
- Most modern macOS, Windows and Linux systems running Node.js >= 14.15.0
23
+ Most modern macOS, Windows and Linux systems
20
24
  do not require any additional install or runtime dependencies.
21
25
 
22
26
  ## Documentation
@@ -0,0 +1,36 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ 'use strict';
5
+
6
+ const { useGlobalLibvips, globalLibvipsVersion, log, spawnRebuild } = require('../lib/libvips');
7
+
8
+ const buildFromSource = (msg) => {
9
+ log(msg);
10
+ log('Attempting to build from source via node-gyp');
11
+ try {
12
+ require('node-addon-api');
13
+ log('Found node-addon-api');
14
+ } catch (err) {
15
+ log('Please add node-addon-api to your dependencies');
16
+ return;
17
+ }
18
+ try {
19
+ const gyp = require('node-gyp');
20
+ log(`Found node-gyp version ${gyp().version}`);
21
+ } catch (err) {
22
+ log('Please add node-gyp to your dependencies');
23
+ return;
24
+ }
25
+ log('See https://sharp.pixelplumbing.com/install#building-from-source');
26
+ const status = spawnRebuild();
27
+ if (status !== 0) {
28
+ process.exit(status);
29
+ }
30
+ };
31
+
32
+ if (useGlobalLibvips()) {
33
+ buildFromSource(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
34
+ } else if (process.env.npm_config_build_from_source) {
35
+ buildFromSource('Detected --build-from-source flag');
36
+ }
@@ -3,11 +3,10 @@
3
3
 
4
4
  'use strict';
5
5
 
6
- const util = require('util');
7
- const stream = require('stream');
6
+ const util = require('node:util');
7
+ const stream = require('node:stream');
8
8
  const is = require('./is');
9
9
 
10
- require('./libvips').hasVendoredLibvips();
11
10
  require('./sharp');
12
11
 
13
12
  // Use NODE_DEBUG=sharp to enable libvips warnings
@@ -122,7 +121,7 @@ const debuglog = util.debuglog('sharp');
122
121
  * a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
123
122
  * JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
124
123
  * @param {Object} [options] - if present, is an Object with optional attributes.
125
- * @param {string} [options.failOn='warning'] - when to abort processing of invalid pixel data, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), higher levels imply lower levels, invalid metadata will always abort.
124
+ * @param {string} [options.failOn='warning'] - When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort.
126
125
  * @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
127
126
  * (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
128
127
  * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
@@ -231,7 +230,8 @@ const Sharp = function (input, options) {
231
230
  threshold: 0,
232
231
  thresholdGrayscale: true,
233
232
  trimBackground: [],
234
- trimThreshold: 0,
233
+ trimThreshold: -1,
234
+ trimLineArt: false,
235
235
  gamma: 0,
236
236
  gammaOut: 0,
237
237
  greyscale: false,
@@ -306,6 +306,7 @@ const Sharp = function (input, options) {
306
306
  tiffCompression: 'jpeg',
307
307
  tiffPredictor: 'horizontal',
308
308
  tiffPyramid: false,
309
+ tiffMiniswhite: false,
309
310
  tiffBitdepth: 8,
310
311
  tiffTile: false,
311
312
  tiffTileHeight: 256,
package/lib/index.d.ts CHANGED
@@ -91,12 +91,6 @@ declare namespace sharp {
91
91
  zlib?: string | undefined;
92
92
  };
93
93
 
94
- /** An Object containing the platform and architecture of the current and installed vendored binaries. */
95
- const vendor: {
96
- current: string;
97
- installed: string[];
98
- };
99
-
100
94
  /** An Object containing the available interpolators and their proper values */
101
95
  const interpolators: Interpolators;
102
96
 
@@ -646,7 +640,7 @@ declare namespace sharp {
646
640
  * @param withMetadata
647
641
  * @throws {Error} Invalid parameters.
648
642
  */
649
- withMetadata(withMetadata?: WriteableMetadata): Sharp;
643
+ withMetadata(withMetadata?: boolean | WriteableMetadata): Sharp;
650
644
 
651
645
  /**
652
646
  * Use these JPEG options for output image.
@@ -705,7 +699,6 @@ declare namespace sharp {
705
699
 
706
700
  /**
707
701
  * Use these AVIF options for output image.
708
- * Whilst it is possible to create AVIF images smaller than 16x16 pixels, most web browsers do not display these properly.
709
702
  * @param options Output options.
710
703
  * @throws {Error} Invalid options
711
704
  * @returns A sharp instance that can be used to chain operations
@@ -854,11 +847,11 @@ declare namespace sharp {
854
847
  * Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
855
848
  * Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
856
849
  * The info response Object will contain trimOffsetLeft and trimOffsetTop properties.
857
- * @param trim The specific background colour to trim, the threshold for doing so or an Object with both.
850
+ * @param options trim options
858
851
  * @throws {Error} Invalid parameters
859
852
  * @returns A sharp instance that can be used to chain operations
860
853
  */
861
- trim(trim?: string | number | TrimOptions): Sharp;
854
+ trim(options?: TrimOptions): Sharp;
862
855
 
863
856
  //#endregion
864
857
  }
@@ -1241,6 +1234,8 @@ declare namespace sharp {
1241
1234
  yres?: number | undefined;
1242
1235
  /** Reduce bitdepth to 1, 2 or 4 bit (optional, default 8) */
1243
1236
  bitdepth?: 1 | 2 | 4 | 8 | undefined;
1237
+ /** Write 1-bit images as miniswhite (optional, default false) */
1238
+ miniswhite?: boolean | undefined;
1244
1239
  /** Resolution unit options: inch, cm (optional, default 'inch') */
1245
1240
  resolutionUnit?: 'inch' | 'cm' | undefined;
1246
1241
  }
@@ -1282,10 +1277,10 @@ declare namespace sharp {
1282
1277
  }
1283
1278
 
1284
1279
  interface NormaliseOptions {
1285
- /** Percentile below which luminance values will be underexposed. */
1286
- lower?: number | undefined;
1287
- /** Percentile above which luminance values will be overexposed. */
1288
- upper?: number | undefined;
1280
+ /** Percentile below which luminance values will be underexposed. */
1281
+ lower?: number | undefined;
1282
+ /** Percentile above which luminance values will be overexposed. */
1283
+ upper?: number | undefined;
1289
1284
  }
1290
1285
 
1291
1286
  interface ResizeOptions {
@@ -1347,10 +1342,12 @@ declare namespace sharp {
1347
1342
  }
1348
1343
 
1349
1344
  interface TrimOptions {
1350
- /** background colour, parsed by the color module, defaults to that of the top-left pixel. (optional) */
1345
+ /** Background colour, parsed by the color module, defaults to that of the top-left pixel. (optional) */
1351
1346
  background?: Color | undefined;
1352
- /** the allowed difference from the above colour, a positive number. (optional, default `10`) */
1347
+ /** Allowed difference from the above colour, a positive number. (optional, default 10) */
1353
1348
  threshold?: number | undefined;
1349
+ /** Does the input more closely resemble line art (e.g. vector) rather than being photographic? (optional, default false) */
1350
+ lineArt?: boolean | undefined;
1354
1351
  }
1355
1352
 
1356
1353
  interface RawOptions {
package/lib/input.js CHANGED
@@ -483,14 +483,27 @@ function _isStreamInput () {
483
483
  * @returns {Promise<Object>|Sharp}
484
484
  */
485
485
  function metadata (callback) {
486
+ const stack = Error();
486
487
  if (is.fn(callback)) {
487
488
  if (this._isStreamInput()) {
488
489
  this.on('finish', () => {
489
490
  this._flattenBufferIn();
490
- sharp.metadata(this.options, callback);
491
+ sharp.metadata(this.options, (err, metadata) => {
492
+ if (err) {
493
+ callback(is.nativeError(err, stack));
494
+ } else {
495
+ callback(null, metadata);
496
+ }
497
+ });
491
498
  });
492
499
  } else {
493
- sharp.metadata(this.options, callback);
500
+ sharp.metadata(this.options, (err, metadata) => {
501
+ if (err) {
502
+ callback(is.nativeError(err, stack));
503
+ } else {
504
+ callback(null, metadata);
505
+ }
506
+ });
494
507
  }
495
508
  return this;
496
509
  } else {
@@ -500,7 +513,7 @@ function metadata (callback) {
500
513
  this._flattenBufferIn();
501
514
  sharp.metadata(this.options, (err, metadata) => {
502
515
  if (err) {
503
- reject(err);
516
+ reject(is.nativeError(err, stack));
504
517
  } else {
505
518
  resolve(metadata);
506
519
  }
@@ -516,7 +529,7 @@ function metadata (callback) {
516
529
  return new Promise((resolve, reject) => {
517
530
  sharp.metadata(this.options, (err, metadata) => {
518
531
  if (err) {
519
- reject(err);
532
+ reject(is.nativeError(err, stack));
520
533
  } else {
521
534
  resolve(metadata);
522
535
  }
@@ -572,14 +585,27 @@ function metadata (callback) {
572
585
  * @returns {Promise<Object>}
573
586
  */
574
587
  function stats (callback) {
588
+ const stack = Error();
575
589
  if (is.fn(callback)) {
576
590
  if (this._isStreamInput()) {
577
591
  this.on('finish', () => {
578
592
  this._flattenBufferIn();
579
- sharp.stats(this.options, callback);
593
+ sharp.stats(this.options, (err, stats) => {
594
+ if (err) {
595
+ callback(is.nativeError(err, stack));
596
+ } else {
597
+ callback(null, stats);
598
+ }
599
+ });
580
600
  });
581
601
  } else {
582
- sharp.stats(this.options, callback);
602
+ sharp.stats(this.options, (err, stats) => {
603
+ if (err) {
604
+ callback(is.nativeError(err, stack));
605
+ } else {
606
+ callback(null, stats);
607
+ }
608
+ });
583
609
  }
584
610
  return this;
585
611
  } else {
@@ -589,7 +615,7 @@ function stats (callback) {
589
615
  this._flattenBufferIn();
590
616
  sharp.stats(this.options, (err, stats) => {
591
617
  if (err) {
592
- reject(err);
618
+ reject(is.nativeError(err, stack));
593
619
  } else {
594
620
  resolve(stats);
595
621
  }
@@ -600,7 +626,7 @@ function stats (callback) {
600
626
  return new Promise((resolve, reject) => {
601
627
  sharp.stats(this.options, (err, stats) => {
602
628
  if (err) {
603
- reject(err);
629
+ reject(is.nativeError(err, stack));
604
630
  } else {
605
631
  resolve(stats);
606
632
  }
package/lib/is.js CHANGED
@@ -137,19 +137,33 @@ const invalidParameterError = function (name, expected, actual) {
137
137
  );
138
138
  };
139
139
 
140
+ /**
141
+ * Ensures an Error from C++ contains a JS stack.
142
+ *
143
+ * @param {Error} native - Error with message from C++.
144
+ * @param {Error} context - Error with stack from JS.
145
+ * @returns {Error} Error with message and stack.
146
+ * @private
147
+ */
148
+ const nativeError = function (native, context) {
149
+ context.message = native.message;
150
+ return context;
151
+ };
152
+
140
153
  module.exports = {
141
- defined: defined,
142
- object: object,
143
- plainObject: plainObject,
144
- fn: fn,
145
- bool: bool,
146
- buffer: buffer,
147
- typedArray: typedArray,
148
- arrayBuffer: arrayBuffer,
149
- string: string,
150
- number: number,
151
- integer: integer,
152
- inRange: inRange,
153
- inArray: inArray,
154
- invalidParameterError: invalidParameterError
154
+ defined,
155
+ object,
156
+ plainObject,
157
+ fn,
158
+ bool,
159
+ buffer,
160
+ typedArray,
161
+ arrayBuffer,
162
+ string,
163
+ number,
164
+ integer,
165
+ inRange,
166
+ inArray,
167
+ invalidParameterError,
168
+ nativeError
155
169
  };
package/lib/libvips.js CHANGED
@@ -3,61 +3,74 @@
3
3
 
4
4
  'use strict';
5
5
 
6
- const fs = require('fs');
7
- const os = require('os');
8
- const path = require('path');
9
- const spawnSync = require('child_process').spawnSync;
6
+ const { spawnSync } = require('node:child_process');
10
7
  const semverCoerce = require('semver/functions/coerce');
11
8
  const semverGreaterThanOrEqualTo = require('semver/functions/gte');
9
+ const detectLibc = require('detect-libc');
12
10
 
13
- const platform = require('./platform');
14
- const { config } = require('../package.json');
11
+ const { engines } = require('../package.json');
15
12
 
16
- const env = process.env;
17
- const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
18
- config.libvips;
13
+ const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
14
+ engines.libvips;
19
15
  const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
20
16
 
17
+ const prebuiltPlatforms = [
18
+ 'darwin-arm64', 'darwin-x64',
19
+ 'linux-arm', 'linux-arm64', 'linux-x64',
20
+ 'linuxmusl-arm64', 'linuxmusl-x64',
21
+ 'win32-ia32', 'win32-x64'
22
+ ];
23
+
21
24
  const spawnSyncOptions = {
22
25
  encoding: 'utf8',
23
26
  shell: true
24
27
  };
25
28
 
26
- const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
27
-
28
- const mkdirSync = function (dirPath) {
29
- try {
30
- fs.mkdirSync(dirPath, { recursive: true });
31
- } catch (err) {
32
- /* istanbul ignore next */
33
- if (err.code !== 'EEXIST') {
34
- throw err;
35
- }
29
+ const log = (item) => {
30
+ if (item instanceof Error) {
31
+ console.error(`sharp: Installation error: ${item.message}`);
32
+ } else {
33
+ console.log(`sharp: ${item}`);
36
34
  }
37
35
  };
38
36
 
39
- const cachePath = function () {
40
- const npmCachePath = env.npm_config_cache || /* istanbul ignore next */
41
- (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
42
- mkdirSync(npmCachePath);
43
- const libvipsCachePath = path.join(npmCachePath, '_libvips');
44
- mkdirSync(libvipsCachePath);
45
- return libvipsCachePath;
37
+ /* istanbul ignore next */
38
+ const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
39
+
40
+ const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
41
+
42
+ /* istanbul ignore next */
43
+ const buildPlatformArch = () => {
44
+ /* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
45
+ const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
46
+ return `${npm_config_platform || process.platform}${npm_config_libc || runtimeLibc()}-${npm_config_arch || process.arch}`;
46
47
  };
47
48
 
48
- const integrity = function (platformAndArch) {
49
- return env[`npm_package_config_integrity_${platformAndArch.replace('-', '_')}`] || config.integrity[platformAndArch];
49
+ const buildSharpLibvipsIncludeDir = () => {
50
+ try {
51
+ return require('@img/sharp-libvips-dev/include');
52
+ } catch {}
53
+ /* istanbul ignore next */
54
+ return '';
50
55
  };
51
56
 
52
- const log = function (item) {
53
- if (item instanceof Error) {
54
- console.error(`sharp: Installation error: ${item.message}`);
55
- } else {
56
- console.log(`sharp: ${item}`);
57
- }
57
+ const buildSharpLibvipsCPlusPlusDir = () => {
58
+ try {
59
+ return require('@img/sharp-libvips-dev/cplusplus');
60
+ } catch {}
61
+ /* istanbul ignore next */
62
+ return '';
63
+ };
64
+
65
+ const buildSharpLibvipsLibDir = () => {
66
+ try {
67
+ return require(`@img/sharp-libvips-${buildPlatformArch()}/lib`);
68
+ } catch {}
69
+ /* istanbul ignore next */
70
+ return '';
58
71
  };
59
72
 
60
- const isRosetta = function () {
73
+ const isRosetta = () => {
61
74
  /* istanbul ignore next */
62
75
  if (process.platform === 'darwin' && process.arch === 'x64') {
63
76
  const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
@@ -66,12 +79,19 @@ const isRosetta = function () {
66
79
  return false;
67
80
  };
68
81
 
69
- const globalLibvipsVersion = function () {
82
+ /* istanbul ignore next */
83
+ const spawnRebuild = () =>
84
+ spawnSync('node-gyp rebuild --directory=src', {
85
+ ...spawnSyncOptions,
86
+ stdio: 'inherit'
87
+ }).status;
88
+
89
+ const globalLibvipsVersion = () => {
70
90
  if (process.platform !== 'win32') {
71
91
  const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
72
92
  ...spawnSyncOptions,
73
93
  env: {
74
- ...env,
94
+ ...process.env,
75
95
  PKG_CONFIG_PATH: pkgConfigPath()
76
96
  }
77
97
  }).stdout;
@@ -82,17 +102,8 @@ const globalLibvipsVersion = function () {
82
102
  }
83
103
  };
84
104
 
85
- const hasVendoredLibvips = function () {
86
- return fs.existsSync(vendorPath);
87
- };
88
-
89
- /* istanbul ignore next */
90
- const removeVendoredLibvips = function () {
91
- fs.rmSync(vendorPath, { recursive: true, maxRetries: 3, force: true });
92
- };
93
-
94
105
  /* istanbul ignore next */
95
- const pkgConfigPath = function () {
106
+ const pkgConfigPath = () => {
96
107
  if (process.platform !== 'win32') {
97
108
  const brewPkgConfigPath = spawnSync(
98
109
  'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
@@ -100,7 +111,7 @@ const pkgConfigPath = function () {
100
111
  ).stdout || '';
101
112
  return [
102
113
  brewPkgConfigPath.trim(),
103
- env.PKG_CONFIG_PATH,
114
+ process.env.PKG_CONFIG_PATH,
104
115
  '/usr/local/lib/pkgconfig',
105
116
  '/usr/lib/pkgconfig',
106
117
  '/usr/local/libdata/pkgconfig',
@@ -111,8 +122,9 @@ const pkgConfigPath = function () {
111
122
  }
112
123
  };
113
124
 
114
- const useGlobalLibvips = function () {
115
- if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
125
+ const useGlobalLibvips = () => {
126
+ if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
127
+ log('Detected SHARP_IGNORE_GLOBAL_LIBVIPS, skipping search for globally-installed libvips');
116
128
  return false;
117
129
  }
118
130
  /* istanbul ignore next */
@@ -127,14 +139,15 @@ const useGlobalLibvips = function () {
127
139
 
128
140
  module.exports = {
129
141
  minimumLibvipsVersion,
130
- minimumLibvipsVersionLabelled,
131
- cachePath,
132
- integrity,
142
+ prebuiltPlatforms,
143
+ buildPlatformArch,
144
+ buildSharpLibvipsIncludeDir,
145
+ buildSharpLibvipsCPlusPlusDir,
146
+ buildSharpLibvipsLibDir,
147
+ runtimePlatformArch,
133
148
  log,
149
+ spawnRebuild,
134
150
  globalLibvipsVersion,
135
- hasVendoredLibvips,
136
- removeVendoredLibvips,
137
151
  pkgConfigPath,
138
- useGlobalLibvips,
139
- mkdirSync
152
+ useGlobalLibvips
140
153
  };