Drop support for versions of Node prior to v4.

Reduce production (sub)depedency count from 93 to 50.
Modernise dev tooling, e.g. use nyc, replace jshint with semistandard.
Make 'npm test' command consistent across platforms.
This commit is contained in:
Lovell Fuller 2016-10-26 10:07:42 +01:00
parent 3f5e38bb62
commit 36e636dca1
14 changed files with 348 additions and 387 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ packaging/libvips*
packaging/*.log packaging/*.log
!packaging/build !packaging/build
.DS_Store .DS_Store
.nyc_output

View File

@ -1,4 +0,0 @@
node_modules
test/bench/node_modules
test/saliency/humanae/node_modules
coverage

View File

@ -1,12 +0,0 @@
{
"strict": true,
"node": true,
"maxparams": 4,
"maxcomplexity": 14,
"globals": {
"beforeEach": true,
"afterEach": true,
"describe": true,
"it": true
}
}

View File

@ -14,3 +14,4 @@ lib
include include
packaging packaging
preinstall.sh preinstall.sh
.nyc_output

View File

@ -1,9 +1,8 @@
language: node_js language: node_js
node_js: node_js:
- "0.10"
- "0.12"
- "4" - "4"
- "6" - "6"
- "7"
os: os:
- linux - linux
- osx - osx
@ -18,4 +17,5 @@ osx_image: xcode8
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi
after_success: after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

View File

@ -3,14 +3,13 @@ version: "{build}"
build: off build: off
platform: x64 platform: x64
environment: environment:
VIPS_WARNING: 0
matrix: matrix:
- nodejs_version: "0.12"
- nodejs_version: "4" - nodejs_version: "4"
- nodejs_version: "6" - nodejs_version: "6"
- nodejs_version: "7"
install: install:
- ps: Install-Product node $env:nodejs_version x64 - ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@latest - npm install -g npm@latest
- npm install - npm install
test_script: test_script:
- npm run-script test-win - npm test

View File

@ -1,81 +1,58 @@
'use strict'; 'use strict';
var fs = require('fs'); const fs = require('fs');
var path = require('path'); const os = require('os');
var zlib = require('zlib'); const path = require('path');
const zlib = require('zlib');
var semver = require('semver'); const caw = require('caw');
var request = require('request'); const got = require('got');
var tar = require('tar'); const semver = require('semver');
const tar = require('tar');
var tmp = require('os').tmpdir(); const distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
var distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
// Use NPM-provided environment variable where available, falling back to require-based method for Electron // Use NPM-provided environment variable where available, falling back to require-based method for Electron
var minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips; const minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
var vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h'); const platform = process.env.npm_config_platform || process.platform;
var platform = process.env.npm_config_platform || process.platform; const arch = process.env.npm_config_arch || process.arch;
var arch = process.env.npm_config_arch || process.arch;
var arm_version = process.env.npm_config_armv || process.config.variables.arm_version;
if (arch === 'arch64' || arch === 'armhf') {
arch = 'arm';
if (arch === 'arch64') arm_version = '8';
}
// -- Helpers // -- Helpers
// Does this file exist? // Does this file exist?
var isFile = function(file) { const isFile = function (file) {
var exists = false;
try { try {
exists = fs.statSync(file).isFile(); return fs.statSync(file).isFile();
} catch (err) {} } catch (err) {}
return exists;
}; };
var unpack = function(tarPath, done) { const unpack = function (tarPath, done) {
var extractor = tar.Extract({ const extractor = tar.Extract({ path: __dirname });
path: __dirname if (done) {
}); extractor.on('end', done);
}
extractor.on('error', error); extractor.on('error', error);
extractor.on('end', function() { fs.createReadStream(tarPath)
if (!isFile(vipsHeaderPath)) { .on('error', error)
error('Could not unpack ' + tarPath);
}
if (typeof done === 'function') {
done();
}
});
fs.createReadStream(tarPath).on('error', error)
.pipe(zlib.Unzip()) .pipe(zlib.Unzip())
.pipe(extractor); .pipe(extractor);
}; };
var platformId = function() { const platformId = function () {
var id = [platform, arch].join('-'); const platformId = [platform];
if (arch === 'arm') { if (arch === 'arm' || arch === 'armhf' || arch === 'arch64') {
switch(arm_version) { const armVersion = (arch === 'arch64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
case '8': platformId.push('armv' + armVersion);
id = id + 'v8'; } else {
break; platformId.push(arch);
case '7':
id = id + 'v7';
break;
default:
id = id + 'v6';
break;
} }
} return platformId.join('-');
return id;
}; };
// Error // Error
var error = function(msg) { const error = function (msg) {
if (msg instanceof Error) { if (msg instanceof Error) {
msg = msg.message; msg = msg.message;
} }
@ -87,16 +64,17 @@ var error = function(msg) {
module.exports.download_vips = function () { module.exports.download_vips = function () {
// Has vips been installed locally? // Has vips been installed locally?
const vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h');
if (!isFile(vipsHeaderPath)) { if (!isFile(vipsHeaderPath)) {
// Ensure Intel 64-bit or ARM // Ensure Intel 64-bit or ARM
if (arch === 'ia32') { if (arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/'); error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
} }
// Ensure glibc >= 2.15 // Ensure glibc >= 2.15
var lddVersion = process.env.LDD_VERSION; const lddVersion = process.env.LDD_VERSION;
if (lddVersion) { if (lddVersion) {
if (/(glibc|gnu libc)/i.test(lddVersion)) { if (/(glibc|gnu libc)/i.test(lddVersion)) {
var glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : ''; const glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) { if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) {
error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/'); error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
} }
@ -105,47 +83,47 @@ module.exports.download_vips = function() {
} }
} }
// Arch/platform-specific .tar.gz // Arch/platform-specific .tar.gz
var tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz'; const tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz';
var tarPath = path.join(__dirname, 'packaging', tarFilename); const tarPathLocal = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPath)) { if (isFile(tarPathLocal)) {
unpack(tarPath); unpack(tarPathLocal);
} else { } else {
// Download to per-process temporary file // Download to per-process temporary file
tarPath = path.join(tmp, process.pid + '-' + tarFilename); const tarPathTemp = path.join(os.tmpdir(), process.pid + '-' + tarFilename);
var tmpFile = fs.createWriteStream(tarPath).on('finish', function() { const tmpFile = fs.createWriteStream(tarPathTemp).on('finish', function () {
unpack(tarPath, function() { unpack(tarPathTemp, function () {
// Attempt to remove temporary file // Attempt to remove temporary file
try { try {
fs.unlinkSync(tarPath); fs.unlinkSync(tarPathTemp);
} catch (err) {} } catch (err) {}
}); });
}); });
var options = { const gotOpt = {};
url: distBaseUrl + tarFilename
};
if (process.env.npm_config_https_proxy) { if (process.env.npm_config_https_proxy) {
// Use the NPM-configured HTTPS proxy // Use the NPM-configured HTTPS proxy
options.proxy = process.env.npm_config_https_proxy; gotOpt.agent = caw(process.env.npm_config_https_proxy);
} }
request(options).on('response', function(response) { const url = distBaseUrl + tarFilename;
got.stream(url, gotOpt).on('response', function (response) {
if (response.statusCode !== 200) { if (response.statusCode !== 200) {
error(distBaseUrl + tarFilename + ' status code ' + response.statusCode); error(url + ' status code ' + response.statusCode);
} }
}).on('error', function (err) { }).on('error', function (err) {
error('Download from ' + distBaseUrl + tarFilename + ' failed: ' + err.message); error('Download of ' + url + ' failed: ' + err.message);
}).pipe(tmpFile); }).pipe(tmpFile);
} }
} }
}; };
module.exports.use_global_vips = function () { module.exports.use_global_vips = function () {
var useGlobalVips = false; const globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
var globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
if (globalVipsVersion) { if (globalVipsVersion) {
useGlobalVips = semver.gte( const useGlobalVips = semver.gte(
globalVipsVersion, globalVipsVersion,
minimumLibvipsVersion minimumLibvipsVersion
); );
}
process.stdout.write(useGlobalVips ? 'true' : 'false'); process.stdout.write(useGlobalVips ? 'true' : 'false');
} else {
process.stdout.write('false');
}
}; };

View File

@ -2,10 +2,12 @@
### v0.17 - "*quill*" ### v0.17 - "*quill*"
Requires libvips v8.4.2 Requires libvips v8.4.2.
#### v0.17.0 - TBD #### v0.17.0 - TBD
* Drop support for versions of Node prior to v4.
* Deprecate the following output format "option" functions: * Deprecate the following output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering, quality, progressive, compressionLevel, withoutAdaptiveFiltering,
withoutChromaSubsampling, trellisQuantisation, trellisQuantization, withoutChromaSubsampling, trellisQuantisation, trellisQuantization,

105
index.js
View File

@ -1,23 +1,22 @@
'use strict'; 'use strict';
var path = require('path'); const path = require('path');
var util = require('util'); const util = require('util');
var stream = require('stream'); const stream = require('stream');
var events = require('events'); const events = require('events');
var semver = require('semver'); const semver = require('semver');
var color = require('color'); const color = require('color');
var BluebirdPromise = require('bluebird');
var sharp = require('./build/Release/sharp.node'); const sharp = require('./build/Release/sharp.node');
// Versioning // Versioning
var versions = { let versions = {
vips: sharp.libvipsVersion() vips: sharp.libvipsVersion()
}; };
(function () { (function () {
// Does libvips meet minimum requirement? // Does libvips meet minimum requirement?
var libvipsVersionMin = require('./package.json').config.libvips; const libvipsVersionMin = require('./package.json').config.libvips;
if (semver.lt(versions.vips, libvipsVersionMin)) { if (semver.lt(versions.vips, libvipsVersionMin)) {
throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin); throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin);
} }
@ -28,14 +27,14 @@ var versions = {
})(); })();
// Limits // Limits
var maximum = { const maximum = {
width: 0x3FFF, width: 0x3FFF,
height: 0x3FFF, height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2) pixels: Math.pow(0x3FFF, 2)
}; };
// Constructor-factory // Constructor-factory
var Sharp = function(input, options) { const Sharp = function (input, options) {
if (!(this instanceof Sharp)) { if (!(this instanceof Sharp)) {
return new Sharp(input, options); return new Sharp(input, options);
} }
@ -144,34 +143,34 @@ module.exports.versions = versions;
/* /*
Validation helpers Validation helpers
*/ */
var isDefined = function(val) { const isDefined = function (val) {
return typeof val !== 'undefined' && val !== null; return typeof val !== 'undefined' && val !== null;
}; };
var isObject = function(val) { const isObject = function (val) {
return typeof val === 'object'; return typeof val === 'object';
}; };
var isFunction = function(val) { const isFunction = function (val) {
return typeof val === 'function'; return typeof val === 'function';
}; };
var isBoolean = function(val) { const isBoolean = function (val) {
return typeof val === 'boolean'; return typeof val === 'boolean';
}; };
var isBuffer = function(val) { const isBuffer = function (val) {
return typeof val === 'object' && val instanceof Buffer; return typeof val === 'object' && val instanceof Buffer;
}; };
var isString = function(val) { const isString = function (val) {
return typeof val === 'string' && val.length > 0; return typeof val === 'string' && val.length > 0;
}; };
var isNumber = function(val) { const isNumber = function (val) {
return typeof val === 'number' && !Number.isNaN(val); return typeof val === 'number' && !Number.isNaN(val);
}; };
var isInteger = function(val) { const isInteger = function (val) {
return isNumber(val) && val % 1 === 0; return isNumber(val) && val % 1 === 0;
}; };
var inRange = function(val, min, max) { const inRange = function (val, min, max) {
return val >= min && val <= max; return val >= min && val <= max;
}; };
var contains = function(val, list) { const contains = function (val, list) {
return list.indexOf(val) !== -1; return list.indexOf(val) !== -1;
}; };
@ -179,7 +178,7 @@ var contains = function(val, list) {
Create Object containing input and input-related options Create Object containing input and input-related options
*/ */
Sharp.prototype._createInputDescriptor = function (input, inputOptions, containerOptions) { Sharp.prototype._createInputDescriptor = function (input, inputOptions, containerOptions) {
var inputDescriptor = {}; const inputDescriptor = {};
if (isString(input)) { if (isString(input)) {
// filesystem // filesystem
inputDescriptor.file = input; inputDescriptor.file = input;
@ -226,7 +225,6 @@ Sharp.prototype._createInputDescriptor = function(input, inputOptions, container
Handle incoming chunk on Writable Stream Handle incoming chunk on Writable Stream
*/ */
Sharp.prototype._write = function (chunk, encoding, callback) { Sharp.prototype._write = function (chunk, encoding, callback) {
/*jslint unused: false */
if (Array.isArray(this.options.input.buffer)) { if (Array.isArray(this.options.input.buffer)) {
if (isBuffer(chunk)) { if (isBuffer(chunk)) {
this.options.input.buffer.push(chunk); this.options.input.buffer.push(chunk);
@ -295,9 +293,9 @@ Sharp.prototype.crop = function(crop) {
}; };
Sharp.prototype.extract = function (options) { Sharp.prototype.extract = function (options) {
var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post'; const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
['left', 'top', 'width', 'height'].forEach(function (name) { ['left', 'top', 'width', 'height'].forEach(function (name) {
var value = options[name]; const value = options[name];
if (isInteger(value) && value >= 0) { if (isInteger(value) && value >= 0) {
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value; this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else { } else {
@ -312,12 +310,13 @@ Sharp.prototype.extract = function(options) {
}; };
Sharp.prototype.extractChannel = function (channel) { Sharp.prototype.extractChannel = function (channel) {
if (channel === 'red') if (channel === 'red') {
channel = 0; channel = 0;
else if (channel === 'green') } else if (channel === 'green') {
channel = 1; channel = 1;
else if (channel === 'blue') } else if (channel === 'blue') {
channel = 2; channel = 2;
}
if (isInteger(channel) && inRange(channel, 0, 4)) { if (isInteger(channel) && inRange(channel, 0, 4)) {
this.options.extractChannel = channel; this.options.extractChannel = channel;
} else { } else {
@ -332,7 +331,7 @@ Sharp.prototype.extractChannel = function(channel) {
but is liberal in what it accepts, clamping values to sensible min/max. but is liberal in what it accepts, clamping values to sensible min/max.
*/ */
Sharp.prototype.background = function (rgba) { Sharp.prototype.background = function (rgba) {
var colour = color(rgba); const colour = color(rgba);
this.options.background = colour.rgbArray(); this.options.background = colour.rgbArray();
this.options.background.push(colour.alpha() * 255); this.options.background.push(colour.alpha() * 255);
return this; return this;
@ -514,7 +513,7 @@ Sharp.prototype.convolve = function(kernel) {
if (!isObject(kernel) || !Array.isArray(kernel.kernel) || if (!isObject(kernel) || !Array.isArray(kernel.kernel) ||
!isInteger(kernel.width) || !isInteger(kernel.height) || !isInteger(kernel.width) || !isInteger(kernel.height) ||
!inRange(kernel.width, 3, 1001) || !inRange(kernel.height, 3, 1001) || !inRange(kernel.width, 3, 1001) || !inRange(kernel.height, 3, 1001) ||
kernel.height * kernel.width != kernel.kernel.length kernel.height * kernel.width !== kernel.kernel.length
) { ) {
// must pass in a kernel // must pass in a kernel
throw new Error('Invalid convolution kernel'); throw new Error('Invalid convolution kernel');
@ -675,52 +674,52 @@ Sharp.prototype.sequentialRead = function(sequentialRead) {
// Deprecated output options // Deprecated output options
Sharp.prototype.quality = util.deprecate(function (quality) { Sharp.prototype.quality = util.deprecate(function (quality) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
var options = { quality: quality }; const options = { quality: quality };
this.jpeg(options).webp(options).tiff(options); this.jpeg(options).webp(options).tiff(options);
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'quality: use jpeg({ quality: ... }), webp({ quality: ... }) and/or tiff({ quality: ... }) instead'); }, 'quality: use jpeg({ quality: ... }), webp({ quality: ... }) and/or tiff({ quality: ... }) instead');
Sharp.prototype.progressive = util.deprecate(function (progressive) { Sharp.prototype.progressive = util.deprecate(function (progressive) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
var options = { progressive: (progressive !== false) }; const options = { progressive: (progressive !== false) };
this.jpeg(options).png(options); this.jpeg(options).png(options);
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'progressive: use jpeg({ progressive: ... }) and/or png({ progressive: ... }) instead'); }, 'progressive: use jpeg({ progressive: ... }) and/or png({ progressive: ... }) instead');
Sharp.prototype.compressionLevel = util.deprecate(function (compressionLevel) { Sharp.prototype.compressionLevel = util.deprecate(function (compressionLevel) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
this.png({ compressionLevel: compressionLevel }); this.png({ compressionLevel: compressionLevel });
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'compressionLevel: use png({ compressionLevel: ... }) instead'); }, 'compressionLevel: use png({ compressionLevel: ... }) instead');
Sharp.prototype.withoutAdaptiveFiltering = util.deprecate(function (withoutAdaptiveFiltering) { Sharp.prototype.withoutAdaptiveFiltering = util.deprecate(function (withoutAdaptiveFiltering) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
this.png({ adaptiveFiltering: (withoutAdaptiveFiltering === false) }); this.png({ adaptiveFiltering: (withoutAdaptiveFiltering === false) });
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'withoutAdaptiveFiltering: use png({ adaptiveFiltering: ... }) instead'); }, 'withoutAdaptiveFiltering: use png({ adaptiveFiltering: ... }) instead');
Sharp.prototype.withoutChromaSubsampling = util.deprecate(function (withoutChromaSubsampling) { Sharp.prototype.withoutChromaSubsampling = util.deprecate(function (withoutChromaSubsampling) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
this.jpeg({ chromaSubsampling: (withoutChromaSubsampling === false) ? '4:2:0' : '4:4:4' }); this.jpeg({ chromaSubsampling: (withoutChromaSubsampling === false) ? '4:2:0' : '4:4:4' });
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'withoutChromaSubsampling: use jpeg({ chromaSubsampling: "4:4:4" }) instead'); }, 'withoutChromaSubsampling: use jpeg({ chromaSubsampling: "4:4:4" }) instead');
Sharp.prototype.trellisQuantisation = util.deprecate(function (trellisQuantisation) { Sharp.prototype.trellisQuantisation = util.deprecate(function (trellisQuantisation) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
this.jpeg({ trellisQuantisation: (trellisQuantisation !== false) }); this.jpeg({ trellisQuantisation: (trellisQuantisation !== false) });
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'trellisQuantisation: use jpeg({ trellisQuantisation: ... }) instead'); }, 'trellisQuantisation: use jpeg({ trellisQuantisation: ... }) instead');
Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation; Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation;
Sharp.prototype.overshootDeringing = util.deprecate(function (overshootDeringing) { Sharp.prototype.overshootDeringing = util.deprecate(function (overshootDeringing) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
this.jpeg({ overshootDeringing: (overshootDeringing !== false) }); this.jpeg({ overshootDeringing: (overshootDeringing !== false) });
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
}, 'overshootDeringing: use jpeg({ overshootDeringing: ... }) instead'); }, 'overshootDeringing: use jpeg({ overshootDeringing: ... }) instead');
Sharp.prototype.optimiseScans = util.deprecate(function (optimiseScans) { Sharp.prototype.optimiseScans = util.deprecate(function (optimiseScans) {
var formatOut = this.options.formatOut; const formatOut = this.options.formatOut;
this.jpeg({ optimiseScans: (optimiseScans !== false) }); this.jpeg({ optimiseScans: (optimiseScans !== false) });
this.options.formatOut = formatOut; this.options.formatOut = formatOut;
return this; return this;
@ -920,19 +919,19 @@ Sharp.prototype.limitInputPixels = function(limit) {
*/ */
Sharp.prototype.toFile = function (fileOut, callback) { Sharp.prototype.toFile = function (fileOut, callback) {
if (!fileOut || fileOut.length === 0) { if (!fileOut || fileOut.length === 0) {
var errOutputInvalid = new Error('Invalid output'); const errOutputInvalid = new Error('Invalid output');
if (isFunction(callback)) { if (isFunction(callback)) {
callback(errOutputInvalid); callback(errOutputInvalid);
} else { } else {
return BluebirdPromise.reject(errOutputInvalid); return Promise.reject(errOutputInvalid);
} }
} else { } else {
if (this.options.input.file === fileOut) { if (this.options.input.file === fileOut) {
var errOutputIsInput = new Error('Cannot use same file for input and output'); const errOutputIsInput = new Error('Cannot use same file for input and output');
if (isFunction(callback)) { if (isFunction(callback)) {
callback(errOutputIsInput); callback(errOutputIsInput);
} else { } else {
return BluebirdPromise.reject(errOutputIsInput); return Promise.reject(errOutputIsInput);
} }
} else { } else {
this.options.fileOut = fileOut; this.options.fileOut = fileOut;
@ -1141,7 +1140,7 @@ Sharp.prototype._read = function() {
Supports callback, stream and promise variants Supports callback, stream and promise variants
*/ */
Sharp.prototype._pipeline = function (callback) { Sharp.prototype._pipeline = function (callback) {
var that = this; const that = this;
if (typeof callback === 'function') { if (typeof callback === 'function') {
// output=file/buffer // output=file/buffer
if (this._isStreamInput()) { if (this._isStreamInput()) {
@ -1188,7 +1187,7 @@ Sharp.prototype._pipeline = function(callback) {
// output=promise // output=promise
if (this._isStreamInput()) { if (this._isStreamInput()) {
// output=promise, input=stream // output=promise, input=stream
return new BluebirdPromise(function(resolve, reject) { return new Promise(function (resolve, reject) {
that.on('finish', function () { that.on('finish', function () {
that._flattenBufferIn(); that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data) { sharp.pipeline(that.options, function (err, data) {
@ -1202,7 +1201,7 @@ Sharp.prototype._pipeline = function(callback) {
}); });
} else { } else {
// output=promise, input=file/buffer // output=promise, input=file/buffer
return new BluebirdPromise(function(resolve, reject) { return new Promise(function (resolve, reject) {
sharp.pipeline(that.options, function (err, data) { sharp.pipeline(that.options, function (err, data) {
if (err) { if (err) {
reject(err); reject(err);
@ -1220,7 +1219,7 @@ Sharp.prototype._pipeline = function(callback) {
Supports callback, stream and promise variants Supports callback, stream and promise variants
*/ */
Sharp.prototype.metadata = function (callback) { Sharp.prototype.metadata = function (callback) {
var that = this; const that = this;
if (typeof callback === 'function') { if (typeof callback === 'function') {
if (this._isStreamInput()) { if (this._isStreamInput()) {
this.on('finish', function () { this.on('finish', function () {
@ -1233,7 +1232,7 @@ Sharp.prototype.metadata = function(callback) {
return this; return this;
} else { } else {
if (this._isStreamInput()) { if (this._isStreamInput()) {
return new BluebirdPromise(function(resolve, reject) { return new Promise(function (resolve, reject) {
that.on('finish', function () { that.on('finish', function () {
that._flattenBufferIn(); that._flattenBufferIn();
sharp.metadata(that.options, function (err, metadata) { sharp.metadata(that.options, function (err, metadata) {
@ -1246,7 +1245,7 @@ Sharp.prototype.metadata = function(callback) {
}); });
}); });
} else { } else {
return new BluebirdPromise(function(resolve, reject) { return new Promise(function (resolve, reject) {
sharp.metadata(that.options, function (err, metadata) { sharp.metadata(that.options, function (err, metadata) {
if (err) { if (err) {
reject(err); reject(err);
@ -1264,9 +1263,9 @@ Sharp.prototype.metadata = function(callback) {
Cloned instances share the same input. Cloned instances share the same input.
*/ */
Sharp.prototype.clone = function () { Sharp.prototype.clone = function () {
var that = this; const that = this;
// Clone existing options // Clone existing options
var clone = new Sharp(); const clone = new Sharp();
util._extend(clone.options, this.options); util._extend(clone.options, this.options);
// Pass 'finish' event to clone for Stream-based input // Pass 'finish' event to clone for Stream-based input
this.on('finish', function () { this.on('finish', function () {

View File

@ -1,5 +1,6 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.17.0", "version": "0.17.0",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"contributors": [ "contributors": [
@ -29,14 +30,11 @@
"Matt Hirsch <mhirsch@media.mit.edu>", "Matt Hirsch <mhirsch@media.mit.edu>",
"Matthias Thoemmes <thoemmes@gmail.com>" "Matthias Thoemmes <thoemmes@gmail.com>"
], ],
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"scripts": { "scripts": {
"clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*", "clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js", "test": "semistandard && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=96 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"test-packaging": "./packaging/test-linux-x64.sh", "test-packaging": "./packaging/test-linux-x64.sh"
"test-clean": "rm -rf coverage/ test/fixtures/output.* && npm install && npm test"
}, },
"main": "index.js", "main": "index.js",
"repository": { "repository": {
@ -59,24 +57,24 @@
"vips" "vips"
], ],
"dependencies": { "dependencies": {
"bluebird": "^3.4.6", "caw": "^2.0.0",
"color": "^0.11.3", "color": "^0.11.3",
"got": "^6.5.0",
"nan": "^2.4.0", "nan": "^2.4.0",
"semver": "^5.3.0", "semver": "^5.3.0",
"request": "^2.75.0",
"tar": "^2.2.1" "tar": "^2.2.1"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.1.2", "async": "^2.1.2",
"bufferutil": "^1.2.1", "bufferutil": "^1.2.1",
"coveralls": "^2.11.14", "cross-env": "^3.1.3",
"exif-reader": "^1.0.1", "exif-reader": "^1.0.1",
"icc": "^0.0.2", "icc": "^0.0.2",
"istanbul": "^0.4.5",
"mocha": "^3.1.2", "mocha": "^3.1.2",
"mocha-jshint": "^2.3.1",
"node-cpplint": "^0.4.0", "node-cpplint": "^0.4.0",
"nyc": "^8.3.2",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"semistandard": "^9.1.0",
"unzip": "^0.1.11" "unzip": "^0.1.11"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
@ -84,6 +82,14 @@
"libvips": "8.4.2" "libvips": "8.4.2"
}, },
"engines": { "engines": {
"node": ">=0.10" "node": ">=4"
},
"semistandard": {
"env": [
"mocha"
],
"ignore": [
"test"
]
} }
} }

View File

@ -1,18 +1,17 @@
'use strict'; 'use strict';
var path = require('path'); const path = require('path');
var assert = require('assert'); const sharp = require('../../index');
var sharp = require('../../index'); const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
var maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
// Helpers // Helpers
var getPath = function(filename) { const getPath = function (filename) {
return path.join(__dirname, filename); return path.join(__dirname, filename);
}; };
// Generates a 64-bit-as-binary-string image fingerprint // Generates a 64-bit-as-binary-string image fingerprint
// Based on the dHash gradient method - see http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html // Based on the dHash gradient method - see http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
var fingerprint = function(image, callback) { const fingerprint = function (image, callback) {
sharp(image) sharp(image)
.greyscale() .greyscale()
.normalise() .normalise()
@ -23,12 +22,11 @@ var fingerprint = function(image, callback) {
if (err) { if (err) {
callback(err); callback(err);
} else { } else {
var fingerprint = ''; let fingerprint = '';
for (var col = 0; col < 8; col++) { for (let col = 0; col < 8; col++) {
var gradient = 0; for (let row = 0; row < 8; row++) {
for (var row = 0; row < 8; row++) { const left = data[row * 8 + col];
var left = data[row * 8 + col]; const right = data[row * 8 + col + 1];
var right = data[row * 8 + col + 1];
fingerprint = fingerprint + (left < right ? '1' : '0'); fingerprint = fingerprint + (left < right ? '1' : '0');
} }
} }
@ -142,8 +140,8 @@ module.exports = {
if (err) return callback(err); if (err) return callback(err);
fingerprint(actualImage, function (err, actualFingerprint) { fingerprint(actualImage, function (err, actualFingerprint) {
if (err) return callback(err); if (err) return callback(err);
var distance = 0; let distance = 0;
for (var i = 0; i < 64; i++) { for (let i = 0; i < 64; i++) {
if (expectedFingerprint[i] !== actualFingerprint[i]) { if (expectedFingerprint[i] !== actualFingerprint[i]) {
distance++; distance++;
} }
@ -169,7 +167,7 @@ module.exports = {
// Default threshold // Default threshold
acceptedDistance = 1; acceptedDistance = 1;
} }
var distance = maxColourDistance(actualImagePath, expectedImagePath); const distance = maxColourDistance(actualImagePath, expectedImagePath);
if (distance > acceptedDistance) { if (distance > acceptedDistance) {
throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance); throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance);
} }

View File

@ -1,13 +1,12 @@
'use strict'; 'use strict';
var assert = require('assert'); const assert = require('assert');
var fs = require('fs'); const fs = require('fs');
var sharp = require('../../index');
var fixtures = require('../fixtures'); const sharp = require('../../index');
var BluebirdPromise = require('bluebird'); const fixtures = require('../fixtures');
describe('Image channel insertion', function () { describe('Image channel insertion', function () {
it('Grayscale to RGB, buffer', function (done) { it('Grayscale to RGB, buffer', function (done) {
sharp(fixtures.inputPng) // gray -> red sharp(fixtures.inputPng) // gray -> red
.resize(320, 240) .resize(320, 240)
@ -86,7 +85,7 @@ describe('Image channel insertion', function() {
}); });
it('Join raw buffers to RGB', function (done) { it('Join raw buffers to RGB', function (done) {
BluebirdPromise.all([ Promise.all([
sharp(fixtures.inputPngTestJoinChannel).toColourspace('b-w').raw().toBuffer(), sharp(fixtures.inputPngTestJoinChannel).toColourspace('b-w').raw().toBuffer(),
sharp(fixtures.inputPngStripesH).toColourspace('b-w').raw().toBuffer() sharp(fixtures.inputPngStripesH).toColourspace('b-w').raw().toBuffer()
]) ])
@ -147,5 +146,4 @@ describe('Image channel insertion', function() {
.joinChannel(); .joinChannel();
}); });
}); });
}); });

View File

@ -1 +0,0 @@
require('mocha-jshint')();

View File

@ -1,23 +1,23 @@
'use strict'; 'use strict';
var fs = require('fs'); const fs = require('fs');
var path = require('path'); const path = require('path');
var assert = require('assert'); const assert = require('assert');
var async = require('async'); const eachLimit = require('async/eachLimit');
var rimraf = require('rimraf'); const rimraf = require('rimraf');
var unzip = require('unzip'); const unzip = require('unzip');
var sharp = require('../../index'); const sharp = require('../../index');
var fixtures = require('../fixtures'); const fixtures = require('../fixtures');
// Verifies all tiles in a given dz output directory are <= size // Verifies all tiles in a given dz output directory are <= size
var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done) { const assertDeepZoomTiles = function (directory, expectedSize, expectedLevels, done) {
// Get levels // Get levels
var levels = fs.readdirSync(directory); const levels = fs.readdirSync(directory);
assert.strictEqual(expectedLevels, levels.length); assert.strictEqual(expectedLevels, levels.length);
// Get tiles // Get tiles
var tiles = []; const tiles = [];
levels.forEach(function (level) { levels.forEach(function (level) {
// Verify level directory name // Verify level directory name
assert.strictEqual(true, /^[0-9]+$/.test(level)); assert.strictEqual(true, /^[0-9]+$/.test(level));
@ -28,7 +28,7 @@ var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done
}); });
}); });
// Verify each tile is <= expectedSize // Verify each tile is <= expectedSize
async.eachSeries(tiles, function(tile, done) { eachLimit(tiles, 8, function (tile, done) {
sharp(tile).metadata(function (err, metadata) { sharp(tile).metadata(function (err, metadata) {
if (err) { if (err) {
done(err); done(err);
@ -47,7 +47,6 @@ var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done
}; };
describe('Tile', function () { describe('Tile', function () {
it('Valid size values pass', function () { it('Valid size values pass', function () {
[1, 8192].forEach(function (size) { [1, 8192].forEach(function (size) {
assert.doesNotThrow(function () { assert.doesNotThrow(function () {
@ -142,9 +141,8 @@ describe('Tile', function() {
}); });
if (sharp.format.dz.output.file) { if (sharp.format.dz.output.file) {
it('Deep Zoom layout', function (done) { it('Deep Zoom layout', function (done) {
var directory = fixtures.path('output.dzi_files'); const directory = fixtures.path('output.dzi_files');
rimraf(directory, function () { rimraf(directory, function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.toFile(fixtures.path('output.dzi'), function (err, info) { .toFile(fixtures.path('output.dzi'), function (err, info) {
@ -156,7 +154,7 @@ describe('Tile', function() {
}); });
it('Deep Zoom layout with custom size+overlap', function (done) { it('Deep Zoom layout with custom size+overlap', function (done) {
var directory = fixtures.path('output.512.dzi_files'); const directory = fixtures.path('output.512.dzi_files');
rimraf(directory, function () { rimraf(directory, function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.tile({ .tile({
@ -172,7 +170,7 @@ describe('Tile', function() {
}); });
it('Zoomify layout', function (done) { it('Zoomify layout', function (done) {
var directory = fixtures.path('output.zoomify.dzi'); const directory = fixtures.path('output.zoomify.dzi');
rimraf(directory, function () { rimraf(directory, function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.tile({ .tile({
@ -192,7 +190,7 @@ describe('Tile', function() {
}); });
it('Google layout', function (done) { it('Google layout', function (done) {
var directory = fixtures.path('output.google.dzi'); const directory = fixtures.path('output.google.dzi');
rimraf(directory, function () { rimraf(directory, function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.tile({ .tile({
@ -212,9 +210,9 @@ describe('Tile', function() {
}); });
it('Write to ZIP container using file extension', function (done) { it('Write to ZIP container using file extension', function (done) {
var container = fixtures.path('output.dz.container.zip'); const container = fixtures.path('output.dz.container.zip');
var extractTo = fixtures.path('output.dz.container'); const extractTo = fixtures.path('output.dz.container');
var directory = path.join(extractTo, 'output.dz.container_files'); const directory = path.join(extractTo, 'output.dz.container_files');
rimraf(directory, function () { rimraf(directory, function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.toFile(container, function (err, info) { .toFile(container, function (err, info) {
@ -236,9 +234,9 @@ describe('Tile', function() {
}); });
it('Write to ZIP container using container tile option', function (done) { it('Write to ZIP container using container tile option', function (done) {
var container = fixtures.path('output.dz.containeropt.zip'); const container = fixtures.path('output.dz.containeropt.zip');
var extractTo = fixtures.path('output.dz.containeropt'); const extractTo = fixtures.path('output.dz.containeropt');
var directory = path.join(extractTo, 'output.dz.containeropt_files'); const directory = path.join(extractTo, 'output.dz.containeropt_files');
rimraf(directory, function () { rimraf(directory, function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.tile({ .tile({
@ -262,7 +260,5 @@ describe('Tile', function() {
}); });
}); });
}); });
} }
}); });