sharp 0.27.1 → 0.30.5

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/platform.js CHANGED
@@ -7,10 +7,12 @@ const env = process.env;
7
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
- /* istanbul ignore next */
11
- const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
10
+ const libc = process.env.npm_config_libc ||
11
+ /* istanbul ignore next */
12
+ (detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
13
+ const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
12
14
 
13
- const platformId = [`${platform}${libc}`];
15
+ const platformId = [`${platform}${libcId}`];
14
16
 
15
17
  if (arch === 'arm') {
16
18
  const fallback = process.versions.electron ? '7' : '6';
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);
@@ -302,11 +321,20 @@ function resize (width, height, options) {
302
321
  * })
303
322
  * ...
304
323
  *
324
+ * @example
325
+ * // Add a row of 10 red pixels to the bottom
326
+ * sharp(input)
327
+ * .extend({
328
+ * bottom: 10,
329
+ * background: 'red'
330
+ * })
331
+ * ...
332
+ *
305
333
  * @param {(number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
306
- * @param {number} [extend.top]
307
- * @param {number} [extend.left]
308
- * @param {number} [extend.bottom]
309
- * @param {number} [extend.right]
334
+ * @param {number} [extend.top=0]
335
+ * @param {number} [extend.left=0]
336
+ * @param {number} [extend.bottom=0]
337
+ * @param {number} [extend.right=0]
310
338
  * @param {String|Object} [extend.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
311
339
  * @returns {Sharp}
312
340
  * @throws {Error} Invalid parameters
@@ -317,17 +345,35 @@ function extend (extend) {
317
345
  this.options.extendBottom = extend;
318
346
  this.options.extendLeft = extend;
319
347
  this.options.extendRight = extend;
320
- } else if (
321
- is.object(extend) &&
322
- is.integer(extend.top) && extend.top >= 0 &&
323
- is.integer(extend.bottom) && extend.bottom >= 0 &&
324
- is.integer(extend.left) && extend.left >= 0 &&
325
- is.integer(extend.right) && extend.right >= 0
326
- ) {
327
- this.options.extendTop = extend.top;
328
- this.options.extendBottom = extend.bottom;
329
- this.options.extendLeft = extend.left;
330
- this.options.extendRight = extend.right;
348
+ } else if (is.object(extend)) {
349
+ if (is.defined(extend.top)) {
350
+ if (is.integer(extend.top) && extend.top >= 0) {
351
+ this.options.extendTop = extend.top;
352
+ } else {
353
+ throw is.invalidParameterError('top', 'positive integer', extend.top);
354
+ }
355
+ }
356
+ if (is.defined(extend.bottom)) {
357
+ if (is.integer(extend.bottom) && extend.bottom >= 0) {
358
+ this.options.extendBottom = extend.bottom;
359
+ } else {
360
+ throw is.invalidParameterError('bottom', 'positive integer', extend.bottom);
361
+ }
362
+ }
363
+ if (is.defined(extend.left)) {
364
+ if (is.integer(extend.left) && extend.left >= 0) {
365
+ this.options.extendLeft = extend.left;
366
+ } else {
367
+ throw is.invalidParameterError('left', 'positive integer', extend.left);
368
+ }
369
+ }
370
+ if (is.defined(extend.right)) {
371
+ if (is.integer(extend.right) && extend.right >= 0) {
372
+ this.options.extendRight = extend.right;
373
+ } else {
374
+ throw is.invalidParameterError('right', 'positive integer', extend.right);
375
+ }
376
+ }
331
377
  this._setBackgroundColourOption('extendBackground', extend.background);
332
378
  } else {
333
379
  throw is.invalidParameterError('extend', 'integer or object', extend);
package/lib/sharp.js ADDED
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ const platformAndArch = require('./platform')();
4
+
5
+ /* istanbul ignore next */
6
+ try {
7
+ module.exports = require(`../build/Release/sharp-${platformAndArch}.node`);
8
+ } catch (err) {
9
+ // Bail early if bindings aren't available
10
+ const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, '', 'Possible solutions:'];
11
+ if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
12
+ help.push('- Update Homebrew: "brew update && brew upgrade vips"');
13
+ } else {
14
+ const [platform, arch] = platformAndArch.split('-');
15
+ help.push(
16
+ '- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
17
+ `- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
18
+ );
19
+ }
20
+ help.push(
21
+ '- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
22
+ );
23
+ // Check loaded
24
+ if (process.platform === 'win32' || /symbol/.test(err.message)) {
25
+ const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
26
+ if (loadedModule) {
27
+ const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
28
+ help.push(`- Ensure the version of sharp aligns with the ${loadedPackage} package: "npm ls sharp"`);
29
+ }
30
+ }
31
+ throw new Error(help.join('\n'));
32
+ }
package/lib/utility.js CHANGED
@@ -1,8 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
4
+ const path = require('path');
3
5
  const events = require('events');
6
+ const detectLibc = require('detect-libc');
7
+
4
8
  const is = require('./is');
5
- const sharp = require('../build/Release/sharp.node');
9
+ const platformAndArch = require('./platform')();
10
+ const sharp = require('./sharp');
6
11
 
7
12
  /**
8
13
  * An Object containing nested boolean values representing the available input and output formats/methods.
@@ -25,11 +30,11 @@ const interpolators = {
25
30
  bilinear: 'bilinear',
26
31
  /** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
27
32
  bicubic: 'bicubic',
28
- /** [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
33
+ /** [LBB interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
29
34
  locallyBoundedBicubic: 'lbb',
30
35
  /** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
31
36
  nohalo: 'nohalo',
32
- /** [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
37
+ /** [VSQBS interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
33
38
  vertexSplitQuadraticBasisSpline: 'vsqbs'
34
39
  };
35
40
 
@@ -43,8 +48,23 @@ let versions = {
43
48
  vips: sharp.libvipsVersion()
44
49
  };
45
50
  try {
46
- versions = require(`../vendor/${versions.vips}/versions.json`);
47
- } catch (err) {}
51
+ versions = require(`../vendor/${versions.vips}/${platformAndArch}/versions.json`);
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 */ }
48
68
 
49
69
  /**
50
70
  * Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
@@ -83,15 +103,32 @@ cache(true);
83
103
 
84
104
  /**
85
105
  * Gets or, when a concurrency is provided, sets
86
- * the number of threads _libvips'_ should create to process each image.
87
- * The default value is the number of CPU cores.
88
- * A value of `0` will reset to this default.
89
- *
90
- * The maximum number of images that can be processed in parallel
91
- * is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
106
+ * the maximum number of threads _libvips_ should use to process _each image_.
107
+ * These are from a thread pool managed by glib,
108
+ * which helps avoid the overhead of creating new threads.
92
109
  *
93
110
  * This method always returns the current concurrency.
94
111
  *
112
+ * The default value is the number of CPU cores,
113
+ * except when using glibc-based Linux without jemalloc,
114
+ * where the default is `1` to help reduce memory fragmentation.
115
+ *
116
+ * A value of `0` will reset this to the number of CPU cores.
117
+ *
118
+ * Some image format libraries spawn additional threads,
119
+ * e.g. libaom manages its own 4 threads when encoding AVIF images,
120
+ * and these are independent of the value set here.
121
+ *
122
+ * The maximum number of images that sharp can process in parallel
123
+ * is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
124
+ * which defaults to 4.
125
+ *
126
+ * https://nodejs.org/api/cli.html#uv_threadpool_sizesize
127
+ *
128
+ * For example, by default, a machine with 8 CPU cores will process
129
+ * 4 images in parallel and use up to 8 threads per image,
130
+ * so there will be up to 32 concurrent threads.
131
+ *
95
132
  * @example
96
133
  * const threads = sharp.concurrency(); // 4
97
134
  * sharp.concurrency(2); // 2
@@ -103,6 +140,11 @@ cache(true);
103
140
  function concurrency (concurrency) {
104
141
  return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
105
142
  }
143
+ /* istanbul ignore next */
144
+ if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
145
+ // Reduce default concurrency to 1 when using glibc memory allocator
146
+ sharp.concurrency(1);
147
+ }
106
148
 
107
149
  /**
108
150
  * An EventEmitter that emits a `change` event when a task is either:
@@ -157,16 +199,13 @@ simd(true);
157
199
  * @private
158
200
  */
159
201
  module.exports = function (Sharp) {
160
- [
161
- cache,
162
- concurrency,
163
- counters,
164
- simd
165
- ].forEach(function (f) {
166
- Sharp[f.name] = f;
167
- });
202
+ Sharp.cache = cache;
203
+ Sharp.concurrency = concurrency;
204
+ Sharp.counters = counters;
205
+ Sharp.simd = simd;
168
206
  Sharp.format = format;
169
207
  Sharp.interpolators = interpolators;
170
208
  Sharp.versions = versions;
209
+ Sharp.vendor = vendor;
171
210
  Sharp.queue = queue;
172
211
  };
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.27.1",
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.5",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -74,13 +74,23 @@
74
74
  "Christian Flintrup <chr@gigahost.dk>",
75
75
  "Manan Jadhav <manan@motionden.com>",
76
76
  "Leon Radley <leon@radley.se>",
77
- "alza54 <alza54@thiocod.in>"
77
+ "alza54 <alza54@thiocod.in>",
78
+ "Jacob Smith <jacob@frende.me>",
79
+ "Michael Nutt <michael@nutt.im>",
80
+ "Brad Parham <baparham@gmail.com>",
81
+ "Taneli Vatanen <taneli.vatanen@gmail.com>",
82
+ "Joris Dugué <zaruike10@gmail.com>",
83
+ "Chris Banks <christopher.bradley.banks@gmail.com>",
84
+ "Ompal Singh <ompal.hitm09@gmail.com>",
85
+ "Brodan <christopher.hranj@gmail.com",
86
+ "Ankur Parihar <ankur.github@gmail.com>"
78
87
  ],
79
88
  "scripts": {
80
- "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
89
+ "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
81
90
  "clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
82
- "test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
83
- "test-unit": "nyc --reporter=lcov --branches=99 mocha --parallel --slow=5000 --timeout=60000 ./test/unit/*.js",
91
+ "test": "npm run test-lint && npm run test-unit && npm run test-licensing",
92
+ "test-lint": "semistandard && cpplint",
93
+ "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
84
94
  "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
85
95
  "test-coverage": "./test/coverage/report.sh",
86
96
  "test-leak": "./test/leak/leak.sh",
@@ -107,6 +117,7 @@
107
117
  "tiff",
108
118
  "gif",
109
119
  "svg",
120
+ "jp2",
110
121
  "dzi",
111
122
  "image",
112
123
  "resize",
@@ -117,47 +128,58 @@
117
128
  "vips"
118
129
  ],
119
130
  "dependencies": {
120
- "array-flatten": "^3.0.0",
121
- "color": "^3.1.3",
122
- "detect-libc": "^1.0.3",
123
- "node-addon-api": "^3.1.0",
124
- "npmlog": "^4.1.2",
125
- "prebuild-install": "^6.0.0",
126
- "semver": "^7.3.4",
127
- "simple-get": "^4.0.0",
131
+ "color": "^4.2.3",
132
+ "detect-libc": "^2.0.1",
133
+ "node-addon-api": "^5.0.0",
134
+ "prebuild-install": "^7.1.0",
135
+ "semver": "^7.3.7",
136
+ "simple-get": "^4.0.1",
128
137
  "tar-fs": "^2.1.1",
129
138
  "tunnel-agent": "^0.6.0"
130
139
  },
131
140
  "devDependencies": {
132
- "async": "^3.2.0",
141
+ "async": "^3.2.3",
133
142
  "cc": "^3.0.1",
134
143
  "decompress-zip": "^0.3.3",
135
- "documentation": "^13.1.1",
144
+ "documentation": "^13.2.5",
136
145
  "exif-reader": "^1.0.3",
137
146
  "icc": "^2.0.0",
138
147
  "license-checker": "^25.0.1",
139
- "mocha": "^8.2.1",
140
- "mock-fs": "^4.13.0",
148
+ "mocha": "^10.0.0",
149
+ "mock-fs": "^5.1.2",
141
150
  "nyc": "^15.1.0",
142
- "prebuild": "^10.0.1",
151
+ "prebuild": "^11.0.3",
143
152
  "rimraf": "^3.0.2",
144
- "semistandard": "^16.0.0"
153
+ "semistandard": "^16.0.1"
145
154
  },
146
155
  "license": "Apache-2.0",
147
156
  "config": {
148
- "libvips": "8.10.5",
157
+ "libvips": "8.12.2",
158
+ "integrity": {
159
+ "darwin-arm64v8": "sha512-p46s/bbJAjkOXzPISZt9HUpG9GWjwQkYnLLRLKzsBJHLtB3X6C6Y/zXI5Hd0DOojcFkks9a0kTN+uDQ/XJY19g==",
160
+ "darwin-x64": "sha512-6vOHVZnvXwe6EXRsy29jdkUzBE6ElNpXUwd+m8vV7qy32AnXu7B9YemHsZ44vWviIwPZeXF6Nhd9EFLM0wWohw==",
161
+ "linux-arm64v8": "sha512-XwZdS63yhqLtbFtx/0eoLF/Agf5qtTrI11FMnMRpuBJWd4jHB60RAH+uzYUgoChCmKIS+AeXYMLm4d8Ns2QX8w==",
162
+ "linux-armv6": "sha512-Rh0Q0kqwPG2MjXWOkPCuPEyiUKFgKJYWLgS835D4MrXgdKr8Tft/eVrc2iGIxt2re30VpDiZ1h0Rby1aCZt2zw==",
163
+ "linux-armv7": "sha512-heTS/MsmRvu4JljINxP+vDiS9ZZfuGhr3IStb5F7Gc0/QLRhllYAg4rcO8L1eTK9sIIzG5ARvI19+YUZe7WbzA==",
164
+ "linux-x64": "sha512-SSWAwBFi0hx8V/h/v82tTFGKWTFv9FiCK3Timz5OExuI+sX1Ngrd0PVQaWXOThGNdel/fcD3Bz9YjSt4feBR1g==",
165
+ "linuxmusl-arm64v8": "sha512-Rhks+5C7p7aO6AucLT1uvzo8ohlqcqCUPgZmN+LZjsPWob/Iix3MfiDYtv/+gTvdeEfXxbCU6/YuPBwHQ7/crA==",
166
+ "linuxmusl-x64": "sha512-IOyjSQqpWVntqOUpCHVWuQwACwmmjdi15H8Pc+Ma1JkhPogTfVsFQWyL7DuOTD3Yr23EuYGzovUX00duOtfy/g==",
167
+ "win32-arm64v8": "sha512-A+Qe8Ipewtvw9ldvF6nWed2J8kphzrUE04nFeKCtNx6pfGQ/MAlCKMjt/U8VgUKNjB01zJDUW9XE0+FhGZ/UpQ==",
168
+ "win32-ia32": "sha512-cMrAvwFdDeAVnLJt0IPMPRKaIFhyXYGTprsM0DND9VUHE8F7dJMR44tS5YkXsGh1QNDtjKT6YuxAVUglmiXtpA==",
169
+ "win32-x64": "sha512-vLFIfw6aW2zABa8jpgzWDhljnE6glktrddErVyazAIoHl6BFFe/Da+LK1DbXvIYHz7fyOoKhSfCJHCiJG1Vg6w=="
170
+ },
149
171
  "runtime": "napi",
150
- "target": 3
172
+ "target": 5
151
173
  },
152
174
  "engines": {
153
- "node": ">=10"
175
+ "node": ">=12.13.0"
154
176
  },
155
177
  "funding": {
156
178
  "url": "https://opencollective.com/libvips"
157
179
  },
158
180
  "binary": {
159
181
  "napi_versions": [
160
- 3
182
+ 5
161
183
  ]
162
184
  },
163
185
  "semistandard": {