diff --git a/.gitignore b/.gitignore index 727e9030..9035ef26 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ packaging/libvips* packaging/*.log !packaging/build .DS_Store +.nyc_output diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index 1eb73c30..00000000 --- a/.jshintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -test/bench/node_modules -test/saliency/humanae/node_modules -coverage diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 13d45c65..00000000 --- a/.jshintrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "strict": true, - "node": true, - "maxparams": 4, - "maxcomplexity": 14, - "globals": { - "beforeEach": true, - "afterEach": true, - "describe": true, - "it": true - } -} diff --git a/.npmignore b/.npmignore index 0a75ecca..2b78298f 100644 --- a/.npmignore +++ b/.npmignore @@ -14,3 +14,4 @@ lib include packaging preinstall.sh +.nyc_output diff --git a/.travis.yml b/.travis.yml index b6cf8914..58b79e0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ language: node_js node_js: - - "0.10" - - "0.12" - "4" - "6" + - "7" os: - linux - osx @@ -18,4 +17,5 @@ osx_image: xcode8 before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi after_success: + - npm install coveralls - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js diff --git a/appveyor.yml b/appveyor.yml index 401b6385..c77dfaec 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,14 +3,13 @@ version: "{build}" build: off platform: x64 environment: - VIPS_WARNING: 0 matrix: - - nodejs_version: "0.12" - nodejs_version: "4" - nodejs_version: "6" + - nodejs_version: "7" install: - ps: Install-Product node $env:nodejs_version x64 - npm install -g npm@latest - npm install test_script: - - npm run-script test-win + - npm test diff --git a/binding.js b/binding.js index f56a9cfe..49f72110 100644 --- a/binding.js +++ b/binding.js @@ -1,81 +1,58 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var zlib = require('zlib'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const zlib = require('zlib'); -var semver = require('semver'); -var request = require('request'); -var tar = require('tar'); +const caw = require('caw'); +const got = require('got'); +const semver = require('semver'); +const tar = require('tar'); -var tmp = require('os').tmpdir(); - -var distBaseUrl = 'https://dl.bintray.com/lovell/sharp/'; +const distBaseUrl = 'https://dl.bintray.com/lovell/sharp/'; // 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; - -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'; -} +const arch = process.env.npm_config_arch || process.arch; // -- Helpers // Does this file exist? -var isFile = function(file) { - var exists = false; +const isFile = function (file) { try { - exists = fs.statSync(file).isFile(); + return fs.statSync(file).isFile(); } catch (err) {} - return exists; }; -var unpack = function(tarPath, done) { - var extractor = tar.Extract({ - path: __dirname - }); +const unpack = function (tarPath, done) { + const extractor = tar.Extract({ path: __dirname }); + if (done) { + extractor.on('end', done); + } extractor.on('error', error); - extractor.on('end', function() { - if (!isFile(vipsHeaderPath)) { - error('Could not unpack ' + tarPath); - } - if (typeof done === 'function') { - done(); - } - }); - fs.createReadStream(tarPath).on('error', error) + fs.createReadStream(tarPath) + .on('error', error) .pipe(zlib.Unzip()) .pipe(extractor); }; -var platformId = function() { - var id = [platform, arch].join('-'); - if (arch === 'arm') { - switch(arm_version) { - case '8': - id = id + 'v8'; - break; - case '7': - id = id + 'v7'; - break; - default: - id = id + 'v6'; - break; - } +const platformId = function () { + const platformId = [platform]; + if (arch === 'arm' || arch === 'armhf' || arch === 'arch64') { + const armVersion = (arch === 'arch64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6'; + platformId.push('armv' + armVersion); + } else { + platformId.push(arch); } - return id; + return platformId.join('-'); }; // Error -var error = function(msg) { +const error = function (msg) { if (msg instanceof Error) { msg = msg.message; } @@ -85,18 +62,19 @@ var error = function(msg) { // -- Binary downloaders -module.exports.download_vips = function() { +module.exports.download_vips = function () { // Has vips been installed locally? + const vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h'); if (!isFile(vipsHeaderPath)) { // Ensure Intel 64-bit or ARM if (arch === 'ia32') { error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/'); } // Ensure glibc >= 2.15 - var lddVersion = process.env.LDD_VERSION; + const lddVersion = process.env.LDD_VERSION; if (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')) { 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 - var tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz'; - var tarPath = path.join(__dirname, 'packaging', tarFilename); - if (isFile(tarPath)) { - unpack(tarPath); + const tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz'; + const tarPathLocal = path.join(__dirname, 'packaging', tarFilename); + if (isFile(tarPathLocal)) { + unpack(tarPathLocal); } else { // Download to per-process temporary file - tarPath = path.join(tmp, process.pid + '-' + tarFilename); - var tmpFile = fs.createWriteStream(tarPath).on('finish', function() { - unpack(tarPath, function() { + const tarPathTemp = path.join(os.tmpdir(), process.pid + '-' + tarFilename); + const tmpFile = fs.createWriteStream(tarPathTemp).on('finish', function () { + unpack(tarPathTemp, function () { // Attempt to remove temporary file try { - fs.unlinkSync(tarPath); + fs.unlinkSync(tarPathTemp); } catch (err) {} }); }); - var options = { - url: distBaseUrl + tarFilename - }; + const gotOpt = {}; if (process.env.npm_config_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) { - error(distBaseUrl + tarFilename + ' status code ' + response.statusCode); + error(url + ' status code ' + response.statusCode); } - }).on('error', function(err) { - error('Download from ' + distBaseUrl + tarFilename + ' failed: ' + err.message); + }).on('error', function (err) { + error('Download of ' + url + ' failed: ' + err.message); }).pipe(tmpFile); } } }; -module.exports.use_global_vips = function() { - var useGlobalVips = false; - var globalVipsVersion = process.env.GLOBAL_VIPS_VERSION; +module.exports.use_global_vips = function () { + const globalVipsVersion = process.env.GLOBAL_VIPS_VERSION; if (globalVipsVersion) { - useGlobalVips = semver.gte( + const useGlobalVips = semver.gte( globalVipsVersion, minimumLibvipsVersion ); + process.stdout.write(useGlobalVips ? 'true' : 'false'); + } else { + process.stdout.write('false'); } - process.stdout.write(useGlobalVips ? 'true' : 'false'); }; diff --git a/docs/changelog.md b/docs/changelog.md index 0c724fba..e5912762 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,10 +2,12 @@ ### v0.17 - "*quill*" -Requires libvips v8.4.2 +Requires libvips v8.4.2. #### v0.17.0 - TBD +* Drop support for versions of Node prior to v4. + * Deprecate the following output format "option" functions: quality, progressive, compressionLevel, withoutAdaptiveFiltering, withoutChromaSubsampling, trellisQuantisation, trellisQuantization, diff --git a/index.js b/index.js index 3a520614..a9236415 100644 --- a/index.js +++ b/index.js @@ -1,23 +1,22 @@ 'use strict'; -var path = require('path'); -var util = require('util'); -var stream = require('stream'); -var events = require('events'); +const path = require('path'); +const util = require('util'); +const stream = require('stream'); +const events = require('events'); -var semver = require('semver'); -var color = require('color'); -var BluebirdPromise = require('bluebird'); +const semver = require('semver'); +const color = require('color'); -var sharp = require('./build/Release/sharp.node'); +const sharp = require('./build/Release/sharp.node'); // Versioning -var versions = { +let versions = { vips: sharp.libvipsVersion() }; -(function() { +(function () { // 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)) { throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin); } @@ -28,14 +27,14 @@ var versions = { })(); // Limits -var maximum = { +const maximum = { width: 0x3FFF, height: 0x3FFF, pixels: Math.pow(0x3FFF, 2) }; // Constructor-factory -var Sharp = function(input, options) { +const Sharp = function (input, options) { if (!(this instanceof Sharp)) { return new Sharp(input, options); } @@ -91,8 +90,8 @@ var Sharp = function(input, options) { colourspace: 'srgb', // overlay overlayGravity: 0, - overlayXOffset : -1, - overlayYOffset : -1, + overlayXOffset: -1, + overlayYOffset: -1, overlayTile: false, overlayCutout: false, // output @@ -116,7 +115,7 @@ var Sharp = function(input, options) { tileSize: 256, tileOverlap: 0, // Function to notify of queue length changes - queueListener: function(queueLength) { + queueListener: function (queueLength) { module.exports.queue.emit('change', queueLength); } }; @@ -144,42 +143,42 @@ module.exports.versions = versions; /* Validation helpers */ -var isDefined = function(val) { +const isDefined = function (val) { return typeof val !== 'undefined' && val !== null; }; -var isObject = function(val) { +const isObject = function (val) { return typeof val === 'object'; }; -var isFunction = function(val) { +const isFunction = function (val) { return typeof val === 'function'; }; -var isBoolean = function(val) { +const isBoolean = function (val) { return typeof val === 'boolean'; }; -var isBuffer = function(val) { +const isBuffer = function (val) { return typeof val === 'object' && val instanceof Buffer; }; -var isString = function(val) { +const isString = function (val) { return typeof val === 'string' && val.length > 0; }; -var isNumber = function(val) { +const isNumber = function (val) { return typeof val === 'number' && !Number.isNaN(val); }; -var isInteger = function(val) { +const isInteger = function (val) { return isNumber(val) && val % 1 === 0; }; -var inRange = function(val, min, max) { +const inRange = function (val, min, max) { return val >= min && val <= max; }; -var contains = function(val, list) { +const contains = function (val, list) { return list.indexOf(val) !== -1; }; /* Create Object containing input and input-related options */ -Sharp.prototype._createInputDescriptor = function(input, inputOptions, containerOptions) { - var inputDescriptor = {}; +Sharp.prototype._createInputDescriptor = function (input, inputOptions, containerOptions) { + const inputDescriptor = {}; if (isString(input)) { // filesystem inputDescriptor.file = input; @@ -225,8 +224,7 @@ Sharp.prototype._createInputDescriptor = function(input, inputOptions, container /* Handle incoming chunk on Writable Stream */ -Sharp.prototype._write = function(chunk, encoding, callback) { - /*jslint unused: false */ +Sharp.prototype._write = function (chunk, encoding, callback) { if (Array.isArray(this.options.input.buffer)) { if (isBuffer(chunk)) { this.options.input.buffer.push(chunk); @@ -242,12 +240,12 @@ Sharp.prototype._write = function(chunk, encoding, callback) { /* Flattens the array of chunks accumulated in input.buffer */ -Sharp.prototype._flattenBufferIn = function() { +Sharp.prototype._flattenBufferIn = function () { if (this._isStreamInput()) { this.options.input.buffer = Buffer.concat(this.options.input.buffer); } }; -Sharp.prototype._isStreamInput = function() { +Sharp.prototype._isStreamInput = function () { return Array.isArray(this.options.input.buffer); }; @@ -274,7 +272,7 @@ module.exports.strategy = { /* What part of the image should be retained when cropping? */ -Sharp.prototype.crop = function(crop) { +Sharp.prototype.crop = function (crop) { this.options.canvas = 'crop'; if (!isDefined(crop)) { // Default @@ -294,10 +292,10 @@ Sharp.prototype.crop = function(crop) { return this; }; -Sharp.prototype.extract = function(options) { - var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post'; +Sharp.prototype.extract = function (options) { + const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post'; ['left', 'top', 'width', 'height'].forEach(function (name) { - var value = options[name]; + const value = options[name]; if (isInteger(value) && value >= 0) { this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value; } else { @@ -311,14 +309,15 @@ Sharp.prototype.extract = function(options) { return this; }; -Sharp.prototype.extractChannel = function(channel) { - if (channel === 'red') +Sharp.prototype.extractChannel = function (channel) { + if (channel === 'red') { channel = 0; - else if (channel === 'green') + } else if (channel === 'green') { channel = 1; - else if (channel === 'blue') + } else if (channel === 'blue') { channel = 2; - if(isInteger(channel) && inRange(channel,0,4)) { + } + if (isInteger(channel) && inRange(channel, 0, 4)) { this.options.extractChannel = channel; } else { throw new Error('Cannot extract invalid channel ' + channel); @@ -331,24 +330,24 @@ Sharp.prototype.extractChannel = function(channel) { Delegates to the 'Color' module, which can throw an Error but is liberal in what it accepts, clamping values to sensible min/max. */ -Sharp.prototype.background = function(rgba) { - var colour = color(rgba); +Sharp.prototype.background = function (rgba) { + const colour = color(rgba); this.options.background = colour.rgbArray(); this.options.background.push(colour.alpha() * 255); return this; }; -Sharp.prototype.embed = function() { +Sharp.prototype.embed = function () { this.options.canvas = 'embed'; return this; }; -Sharp.prototype.max = function() { +Sharp.prototype.max = function () { this.options.canvas = 'max'; return this; }; -Sharp.prototype.min = function() { +Sharp.prototype.min = function () { this.options.canvas = 'min'; return this; }; @@ -357,17 +356,17 @@ Sharp.prototype.min = function() { Ignoring the aspect ratio of the input, stretch the image to the exact width and/or height provided via the resize method. */ -Sharp.prototype.ignoreAspectRatio = function() { +Sharp.prototype.ignoreAspectRatio = function () { this.options.canvas = 'ignore_aspect'; return this; }; -Sharp.prototype.flatten = function(flatten) { +Sharp.prototype.flatten = function (flatten) { this.options.flatten = isBoolean(flatten) ? flatten : true; return this; }; -Sharp.prototype.negate = function(negate) { +Sharp.prototype.negate = function (negate) { this.options.negate = isBoolean(negate) ? negate : true; return this; }; @@ -375,7 +374,7 @@ Sharp.prototype.negate = function(negate) { /* Bitwise boolean operations between images */ -Sharp.prototype.boolean = function(operand, operator, options) { +Sharp.prototype.boolean = function (operand, operator, options) { this.options.boolean = this._createInputDescriptor(operand, options); if (isString(operator) && contains(operator, ['and', 'or', 'eor'])) { this.options.booleanOp = operator; @@ -388,7 +387,7 @@ Sharp.prototype.boolean = function(operand, operator, options) { /* Overlay with another image, using an optional gravity */ -Sharp.prototype.overlayWith = function(overlay, options) { +Sharp.prototype.overlayWith = function (overlay, options) { this.options.overlay = this._createInputDescriptor(overlay, options, { allowStream: false }); @@ -419,7 +418,7 @@ Sharp.prototype.overlayWith = function(overlay, options) { } } if (isDefined(options.gravity)) { - if(isInteger(options.gravity) && inRange(options.gravity, 0, 8)) { + if (isInteger(options.gravity) && inRange(options.gravity, 0, 8)) { this.options.overlayGravity = options.gravity; } else if (isString(options.gravity) && isInteger(module.exports.gravity[options.gravity])) { this.options.overlayGravity = module.exports.gravity[options.gravity]; @@ -434,9 +433,9 @@ Sharp.prototype.overlayWith = function(overlay, options) { /* Add another color channel to the image */ -Sharp.prototype.joinChannel = function(images, options) { +Sharp.prototype.joinChannel = function (images, options) { if (Array.isArray(images)) { - images.forEach(function(image) { + images.forEach(function (image) { this.options.joinChannelIn.push(this._createInputDescriptor(image, options)); }, this); } else { @@ -449,7 +448,7 @@ Sharp.prototype.joinChannel = function(images, options) { Rotate output image by 0, 90, 180 or 270 degrees Auto-rotation based on the EXIF Orientation tag is represented by an angle of -1 */ -Sharp.prototype.rotate = function(angle) { +Sharp.prototype.rotate = function (angle) { if (!isDefined(angle)) { this.options.angle = -1; } else if (isInteger(angle) && contains(angle, [0, 90, 180, 270])) { @@ -463,7 +462,7 @@ Sharp.prototype.rotate = function(angle) { /* Flip the image vertically, about the Y axis */ -Sharp.prototype.flip = function(flip) { +Sharp.prototype.flip = function (flip) { this.options.flip = isBoolean(flip) ? flip : true; return this; }; @@ -471,7 +470,7 @@ Sharp.prototype.flip = function(flip) { /* Flop the image horizontally, about the X axis */ -Sharp.prototype.flop = function(flop) { +Sharp.prototype.flop = function (flop) { this.options.flop = isBoolean(flop) ? flop : true; return this; }; @@ -481,7 +480,7 @@ Sharp.prototype.flop = function(flop) { This is equivalent to GraphicsMagick's ">" geometry option: "change the dimensions of the image only if its width or height exceeds the geometry specification" */ -Sharp.prototype.withoutEnlargement = function(withoutEnlargement) { +Sharp.prototype.withoutEnlargement = function (withoutEnlargement) { this.options.withoutEnlargement = isBoolean(withoutEnlargement) ? withoutEnlargement : true; return this; }; @@ -491,7 +490,7 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) { Call without a sigma to use a fast, mild blur. Call with a sigma to use a slower, more accurate Gaussian blur. */ -Sharp.prototype.blur = function(sigma) { +Sharp.prototype.blur = function (sigma) { if (!isDefined(sigma)) { // No arguments: default to mild blur this.options.blurSigma = -1; @@ -510,18 +509,18 @@ Sharp.prototype.blur = function(sigma) { /* Convolve the image with a kernel. */ -Sharp.prototype.convolve = function(kernel) { +Sharp.prototype.convolve = function (kernel) { if (!isObject(kernel) || !Array.isArray(kernel.kernel) || !isInteger(kernel.width) || !isInteger(kernel.height) || !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 throw new Error('Invalid convolution kernel'); } // Default scale is sum of kernel values if (!isInteger(kernel.scale)) { - kernel.scale = kernel.kernel.reduce(function(a, b) { + kernel.scale = kernel.kernel.reduce(function (a, b) { return a + b; }, 0); } @@ -544,7 +543,7 @@ Sharp.prototype.convolve = function(kernel) { flat - level of "flat" area sharpen, default 1 jagged - level of "jagged" area sharpen, default 2 */ -Sharp.prototype.sharpen = function(sigma, flat, jagged) { +Sharp.prototype.sharpen = function (sigma, flat, jagged) { if (!isDefined(sigma)) { // No arguments: default to mild sharpen this.options.sharpenSigma = -1; @@ -576,7 +575,7 @@ Sharp.prototype.sharpen = function(sigma, flat, jagged) { return this; }; -Sharp.prototype.threshold = function(threshold, options) { +Sharp.prototype.threshold = function (threshold, options) { if (!isDefined(threshold)) { this.options.threshold = 128; } else if (isBoolean(threshold)) { @@ -599,7 +598,7 @@ Sharp.prototype.threshold = function(threshold, options) { tolerance - if present, is a percentaged tolerance level between 0 and 100 to trim away similar color values Defaulting to 10 when no tolerance is given. */ -Sharp.prototype.trim = function(tolerance) { +Sharp.prototype.trim = function (tolerance) { if (!isDefined(tolerance)) { this.options.trimTolerance = 10; } else if (isInteger(tolerance) && inRange(tolerance, 1, 99)) { @@ -614,7 +613,7 @@ Sharp.prototype.trim = function(tolerance) { Darken image pre-resize (1/gamma) and brighten post-resize (gamma). Improves brightness of resized image in non-linear colour spaces. */ -Sharp.prototype.gamma = function(gamma) { +Sharp.prototype.gamma = function (gamma) { if (!isDefined(gamma)) { // Default gamma correction of 2.2 (sRGB) this.options.gamma = 2.2; @@ -629,7 +628,7 @@ Sharp.prototype.gamma = function(gamma) { /* Enhance output image contrast by stretching its luminance to cover the full dynamic range */ -Sharp.prototype.normalize = function(normalize) { +Sharp.prototype.normalize = function (normalize) { this.options.normalize = isBoolean(normalize) ? normalize : true; return this; }; @@ -638,7 +637,7 @@ Sharp.prototype.normalise = Sharp.prototype.normalize; /* Perform boolean/bitwise operation on image color channels - results in one channel image */ -Sharp.prototype.bandbool = function(boolOp) { +Sharp.prototype.bandbool = function (boolOp) { if (isString(boolOp) && contains(boolOp, ['and', 'or', 'eor'])) { this.options.bandBoolOp = boolOp; } else { @@ -650,7 +649,7 @@ Sharp.prototype.bandbool = function(boolOp) { /* Convert to greyscale */ -Sharp.prototype.greyscale = function(greyscale) { +Sharp.prototype.greyscale = function (greyscale) { this.options.greyscale = isBoolean(greyscale) ? greyscale : true; return this; }; @@ -659,8 +658,8 @@ Sharp.prototype.grayscale = Sharp.prototype.greyscale; /* Set output colourspace */ -Sharp.prototype.toColourspace = function(colourspace) { - if (!isString(colourspace) ) { +Sharp.prototype.toColourspace = function (colourspace) { + if (!isString(colourspace)) { throw new Error('Invalid output colourspace ' + colourspace); } this.options.colourspace = colourspace; @@ -668,59 +667,59 @@ Sharp.prototype.toColourspace = function(colourspace) { }; Sharp.prototype.toColorspace = Sharp.prototype.toColourspace; -Sharp.prototype.sequentialRead = function(sequentialRead) { +Sharp.prototype.sequentialRead = function (sequentialRead) { this.options.sequentialRead = isBoolean(sequentialRead) ? sequentialRead : true; return this; }; // Deprecated output options -Sharp.prototype.quality = util.deprecate(function(quality) { - var formatOut = this.options.formatOut; - var options = { quality: quality }; +Sharp.prototype.quality = util.deprecate(function (quality) { + const formatOut = this.options.formatOut; + const options = { quality: quality }; this.jpeg(options).webp(options).tiff(options); this.options.formatOut = formatOut; return this; }, 'quality: use jpeg({ quality: ... }), webp({ quality: ... }) and/or tiff({ quality: ... }) instead'); -Sharp.prototype.progressive = util.deprecate(function(progressive) { - var formatOut = this.options.formatOut; - var options = { progressive: (progressive !== false) }; +Sharp.prototype.progressive = util.deprecate(function (progressive) { + const formatOut = this.options.formatOut; + const options = { progressive: (progressive !== false) }; this.jpeg(options).png(options); this.options.formatOut = formatOut; return this; }, 'progressive: use jpeg({ progressive: ... }) and/or png({ progressive: ... }) instead'); -Sharp.prototype.compressionLevel = util.deprecate(function(compressionLevel) { - var formatOut = this.options.formatOut; +Sharp.prototype.compressionLevel = util.deprecate(function (compressionLevel) { + const formatOut = this.options.formatOut; this.png({ compressionLevel: compressionLevel }); this.options.formatOut = formatOut; return this; }, 'compressionLevel: use png({ compressionLevel: ... }) instead'); -Sharp.prototype.withoutAdaptiveFiltering = util.deprecate(function(withoutAdaptiveFiltering) { - var formatOut = this.options.formatOut; +Sharp.prototype.withoutAdaptiveFiltering = util.deprecate(function (withoutAdaptiveFiltering) { + const formatOut = this.options.formatOut; this.png({ adaptiveFiltering: (withoutAdaptiveFiltering === false) }); this.options.formatOut = formatOut; return this; }, 'withoutAdaptiveFiltering: use png({ adaptiveFiltering: ... }) instead'); -Sharp.prototype.withoutChromaSubsampling = util.deprecate(function(withoutChromaSubsampling) { - var formatOut = this.options.formatOut; +Sharp.prototype.withoutChromaSubsampling = util.deprecate(function (withoutChromaSubsampling) { + const formatOut = this.options.formatOut; this.jpeg({ chromaSubsampling: (withoutChromaSubsampling === false) ? '4:2:0' : '4:4:4' }); this.options.formatOut = formatOut; return this; }, 'withoutChromaSubsampling: use jpeg({ chromaSubsampling: "4:4:4" }) instead'); -Sharp.prototype.trellisQuantisation = util.deprecate(function(trellisQuantisation) { - var formatOut = this.options.formatOut; +Sharp.prototype.trellisQuantisation = util.deprecate(function (trellisQuantisation) { + const formatOut = this.options.formatOut; this.jpeg({ trellisQuantisation: (trellisQuantisation !== false) }); this.options.formatOut = formatOut; return this; }, 'trellisQuantisation: use jpeg({ trellisQuantisation: ... }) instead'); Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation; -Sharp.prototype.overshootDeringing = util.deprecate(function(overshootDeringing) { - var formatOut = this.options.formatOut; +Sharp.prototype.overshootDeringing = util.deprecate(function (overshootDeringing) { + const formatOut = this.options.formatOut; this.jpeg({ overshootDeringing: (overshootDeringing !== false) }); this.options.formatOut = formatOut; return this; }, 'overshootDeringing: use jpeg({ overshootDeringing: ... }) instead'); -Sharp.prototype.optimiseScans = util.deprecate(function(optimiseScans) { - var formatOut = this.options.formatOut; +Sharp.prototype.optimiseScans = util.deprecate(function (optimiseScans) { + const formatOut = this.options.formatOut; this.jpeg({ optimiseScans: (optimiseScans !== false) }); this.options.formatOut = formatOut; return this; @@ -732,7 +731,7 @@ Sharp.prototype.optimizeScans = Sharp.prototype.optimiseScans; Optionally provide an Object with attributes to update: orientation: numeric value for EXIF Orientation tag */ -Sharp.prototype.withMetadata = function(withMetadata) { +Sharp.prototype.withMetadata = function (withMetadata) { this.options.withMetadata = isBoolean(withMetadata) ? withMetadata : true; if (isObject(withMetadata)) { if (isDefined(withMetadata.orientation)) { @@ -749,7 +748,7 @@ Sharp.prototype.withMetadata = function(withMetadata) { /* Tile-based deep zoom output options: size, overlap, layout */ -Sharp.prototype.tile = function(tile) { +Sharp.prototype.tile = function (tile) { if (isObject(tile)) { // Size of square tiles, in pixels if (isDefined(tile.size)) { @@ -793,7 +792,7 @@ Sharp.prototype.tile = function(tile) { /* Extend edges */ -Sharp.prototype.extend = function(extend) { +Sharp.prototype.extend = function (extend) { if (isInteger(extend) && extend > 0) { this.options.extendTop = extend; this.options.extendBottom = extend; @@ -854,7 +853,7 @@ module.exports.colorspace = module.exports.colourspace; options.kernel is the kernel to use for reductions, default 'lanczos3' options.interpolator is the interpolator to use for enlargements, default 'bicubic' */ -Sharp.prototype.resize = function(width, height, options) { +Sharp.prototype.resize = function (width, height, options) { if (isDefined(width)) { if (isInteger(width) && inRange(width, 1, maximum.width)) { this.options.width = width; @@ -899,11 +898,11 @@ Sharp.prototype.resize = function(width, height, options) { Assumes the image dimensions contained in the file header can be trusted. Alternatively can use boolean to disable or reset to default (maximum pixels) */ -Sharp.prototype.limitInputPixels = function(limit) { - //if we pass in false we represent the integer as 0 to disable - if(limit === false) { +Sharp.prototype.limitInputPixels = function (limit) { + // if we pass in false we represent the integer as 0 to disable + if (limit === false) { limit = 0; - } else if(limit === true) { + } else if (limit === true) { limit = maximum.pixels; } if (typeof limit === 'number' && !Number.isNaN(limit) && limit % 1 === 0 && limit >= 0) { @@ -918,21 +917,21 @@ Sharp.prototype.limitInputPixels = function(limit) { * Write output image data to a file * @throws {Error} if an attempt has been made to force Buffer/Stream output type */ -Sharp.prototype.toFile = function(fileOut, callback) { +Sharp.prototype.toFile = function (fileOut, callback) { if (!fileOut || fileOut.length === 0) { - var errOutputInvalid = new Error('Invalid output'); + const errOutputInvalid = new Error('Invalid output'); if (isFunction(callback)) { callback(errOutputInvalid); } else { - return BluebirdPromise.reject(errOutputInvalid); + return Promise.reject(errOutputInvalid); } } else { 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)) { callback(errOutputIsInput); } else { - return BluebirdPromise.reject(errOutputIsInput); + return Promise.reject(errOutputIsInput); } } else { this.options.fileOut = fileOut; @@ -947,7 +946,7 @@ Sharp.prototype.toFile = function(fileOut, callback) { * @param {Function} [callback] * @returns {Promise} when no callback is provided */ -Sharp.prototype.toBuffer = function(callback) { +Sharp.prototype.toBuffer = function (callback) { return this._pipeline(callback); }; @@ -960,7 +959,7 @@ Sharp.prototype.toBuffer = function(callback) { * @param {Boolean} [options.force=true] - force output format, otherwise attempt to use input format * @returns {Object} this */ -Sharp.prototype._updateFormatOut = function(formatOut, options) { +Sharp.prototype._updateFormatOut = function (formatOut, options) { this.options.formatOut = (isObject(options) && options.force === false) ? 'input' : formatOut; return this; }; @@ -972,7 +971,7 @@ Sharp.prototype._updateFormatOut = function(formatOut, options) { * @param {Boolean} val * @returns {void} */ -Sharp.prototype._setBooleanOption = function(key, val) { +Sharp.prototype._setBooleanOption = function (key, val) { if (isBoolean(val)) { this.options[key] = val; } else { @@ -993,7 +992,7 @@ Sharp.prototype._setBooleanOption = function(key, val) { * @returns {Object} this * @throws {Error} Invalid options */ -Sharp.prototype.jpeg = function(options) { +Sharp.prototype.jpeg = function (options) { if (isObject(options)) { if (isDefined(options.quality)) { if (isInteger(options.quality) && inRange(options.quality, 1, 100)) { @@ -1040,7 +1039,7 @@ Sharp.prototype.jpeg = function(options) { * @returns {Object} this * @throws {Error} Invalid options */ -Sharp.prototype.png = function(options) { +Sharp.prototype.png = function (options) { if (isObject(options)) { if (isDefined(options.progressive)) { this._setBooleanOption('pngProgressive', options.progressive); @@ -1067,7 +1066,7 @@ Sharp.prototype.png = function(options) { * @returns {Object} this * @throws {Error} Invalid options */ -Sharp.prototype.webp = function(options) { +Sharp.prototype.webp = function (options) { if (isObject(options)) { if (isDefined(options.quality)) { if (isInteger(options.quality) && inRange(options.quality, 1, 100)) { @@ -1088,7 +1087,7 @@ Sharp.prototype.webp = function(options) { * @returns {Object} this * @throws {Error} Invalid options */ -Sharp.prototype.tiff = function(options) { +Sharp.prototype.tiff = function (options) { if (isObject(options)) { if (isDefined(options.quality)) { if (isInteger(options.quality) && inRange(options.quality, 1, 100)) { @@ -1105,7 +1104,7 @@ Sharp.prototype.tiff = function(options) { * Force output to be raw, uncompressed uint8 pixel data. * @returns {Object} this */ -Sharp.prototype.raw = function() { +Sharp.prototype.raw = function () { return this._updateFormatOut('raw'); }; @@ -1116,7 +1115,7 @@ Sharp.prototype.raw = function() { * @returns {Object} this * @throws {Error} unsupported format or options */ -Sharp.prototype.toFormat = function(format, options) { +Sharp.prototype.toFormat = function (format, options) { if (isObject(format) && isString(format.id)) { format = format.id; } @@ -1129,7 +1128,7 @@ Sharp.prototype.toFormat = function(format, options) { /* Used by a Writable Stream to notify that it is ready for data */ -Sharp.prototype._read = function() { +Sharp.prototype._read = function () { if (!this.options.streamOut) { this.options.streamOut = true; this._pipeline(); @@ -1140,13 +1139,13 @@ Sharp.prototype._read = function() { Invoke the C++ image processing pipeline Supports callback, stream and promise variants */ -Sharp.prototype._pipeline = function(callback) { - var that = this; +Sharp.prototype._pipeline = function (callback) { + const that = this; if (typeof callback === 'function') { // output=file/buffer if (this._isStreamInput()) { // output=file/buffer, input=stream - this.on('finish', function() { + this.on('finish', function () { that._flattenBufferIn(); sharp.pipeline(that.options, callback); }); @@ -1159,9 +1158,9 @@ Sharp.prototype._pipeline = function(callback) { // output=stream if (this._isStreamInput()) { // output=stream, input=stream - this.on('finish', function() { + this.on('finish', function () { that._flattenBufferIn(); - sharp.pipeline(that.options, function(err, data, info) { + sharp.pipeline(that.options, function (err, data, info) { if (err) { that.emit('error', err); } else { @@ -1173,7 +1172,7 @@ Sharp.prototype._pipeline = function(callback) { }); } else { // output=stream, input=file/buffer - sharp.pipeline(this.options, function(err, data, info) { + sharp.pipeline(this.options, function (err, data, info) { if (err) { that.emit('error', err); } else { @@ -1188,10 +1187,10 @@ Sharp.prototype._pipeline = function(callback) { // output=promise if (this._isStreamInput()) { // output=promise, input=stream - return new BluebirdPromise(function(resolve, reject) { - that.on('finish', function() { + return new Promise(function (resolve, reject) { + that.on('finish', function () { that._flattenBufferIn(); - sharp.pipeline(that.options, function(err, data) { + sharp.pipeline(that.options, function (err, data) { if (err) { reject(err); } else { @@ -1202,8 +1201,8 @@ Sharp.prototype._pipeline = function(callback) { }); } else { // output=promise, input=file/buffer - return new BluebirdPromise(function(resolve, reject) { - sharp.pipeline(that.options, function(err, data) { + return new Promise(function (resolve, reject) { + sharp.pipeline(that.options, function (err, data) { if (err) { reject(err); } else { @@ -1219,11 +1218,11 @@ Sharp.prototype._pipeline = function(callback) { Reads the image header and returns metadata Supports callback, stream and promise variants */ -Sharp.prototype.metadata = function(callback) { - var that = this; +Sharp.prototype.metadata = function (callback) { + const that = this; if (typeof callback === 'function') { if (this._isStreamInput()) { - this.on('finish', function() { + this.on('finish', function () { that._flattenBufferIn(); sharp.metadata(that.options, callback); }); @@ -1233,10 +1232,10 @@ Sharp.prototype.metadata = function(callback) { return this; } else { if (this._isStreamInput()) { - return new BluebirdPromise(function(resolve, reject) { - that.on('finish', function() { + return new Promise(function (resolve, reject) { + that.on('finish', function () { that._flattenBufferIn(); - sharp.metadata(that.options, function(err, metadata) { + sharp.metadata(that.options, function (err, metadata) { if (err) { reject(err); } else { @@ -1246,8 +1245,8 @@ Sharp.prototype.metadata = function(callback) { }); }); } else { - return new BluebirdPromise(function(resolve, reject) { - sharp.metadata(that.options, function(err, metadata) { + return new Promise(function (resolve, reject) { + sharp.metadata(that.options, function (err, metadata) { if (err) { reject(err); } else { @@ -1263,13 +1262,13 @@ Sharp.prototype.metadata = function(callback) { Clone new instance using existing options. Cloned instances share the same input. */ -Sharp.prototype.clone = function() { - var that = this; +Sharp.prototype.clone = function () { + const that = this; // Clone existing options - var clone = new Sharp(); + const clone = new Sharp(); util._extend(clone.options, this.options); // Pass 'finish' event to clone for Stream-based input - this.on('finish', function() { + this.on('finish', function () { // Clone inherits input data that._flattenBufferIn(); clone.options.bufferIn = that.options.bufferIn; @@ -1281,7 +1280,7 @@ Sharp.prototype.clone = function() { /** Get and set cache memory, file and item limits */ -module.exports.cache = function(options) { +module.exports.cache = function (options) { if (isBoolean(options)) { if (options) { // Default cache settings of 50MB, 20 files, 100 items @@ -1301,21 +1300,21 @@ module.exports.cache(true); /* Get and set size of thread pool */ -module.exports.concurrency = function(concurrency) { +module.exports.concurrency = function (concurrency) { return sharp.concurrency(isInteger(concurrency) ? concurrency : null); }; /* Get internal counters */ -module.exports.counters = function() { +module.exports.counters = function () { return sharp.counters(); }; /* Get and set use of SIMD vector unit instructions */ -module.exports.simd = function(simd) { +module.exports.simd = function (simd) { return sharp.simd(isBoolean(simd) ? simd : null); }; // Switch off default diff --git a/package.json b/package.json index ea56d608..8af294af 100755 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "sharp", + "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images", "version": "0.17.0", "author": "Lovell Fuller ", "contributors": [ @@ -29,14 +30,11 @@ "Matt Hirsch ", "Matthias Thoemmes " ], - "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images", "scripts": { "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-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=60000 ./test/unit/*.js", + "test": "semistandard && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=96 mocha --slow=5000 --timeout=60000 ./test/unit/*.js", "test-leak": "./test/leak/leak.sh", - "test-packaging": "./packaging/test-linux-x64.sh", - "test-clean": "rm -rf coverage/ test/fixtures/output.* && npm install && npm test" + "test-packaging": "./packaging/test-linux-x64.sh" }, "main": "index.js", "repository": { @@ -59,24 +57,24 @@ "vips" ], "dependencies": { - "bluebird": "^3.4.6", + "caw": "^2.0.0", "color": "^0.11.3", + "got": "^6.5.0", "nan": "^2.4.0", "semver": "^5.3.0", - "request": "^2.75.0", "tar": "^2.2.1" }, "devDependencies": { "async": "^2.1.2", "bufferutil": "^1.2.1", - "coveralls": "^2.11.14", + "cross-env": "^3.1.3", "exif-reader": "^1.0.1", "icc": "^0.0.2", - "istanbul": "^0.4.5", "mocha": "^3.1.2", - "mocha-jshint": "^2.3.1", "node-cpplint": "^0.4.0", + "nyc": "^8.3.2", "rimraf": "^2.5.4", + "semistandard": "^9.1.0", "unzip": "^0.1.11" }, "license": "Apache-2.0", @@ -84,6 +82,14 @@ "libvips": "8.4.2" }, "engines": { - "node": ">=0.10" + "node": ">=4" + }, + "semistandard": { + "env": [ + "mocha" + ], + "ignore": [ + "test" + ] } } diff --git a/test/fixtures/index.js b/test/fixtures/index.js index c7678563..651f1571 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -1,34 +1,32 @@ 'use strict'; -var path = require('path'); -var assert = require('assert'); -var sharp = require('../../index'); -var maxColourDistance = require('../../build/Release/sharp')._maxColourDistance; +const path = require('path'); +const sharp = require('../../index'); +const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance; // Helpers -var getPath = function(filename) { +const getPath = function (filename) { return path.join(__dirname, filename); }; // 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 -var fingerprint = function(image, callback) { +const fingerprint = function (image, callback) { sharp(image) .greyscale() .normalise() .resize(9, 8) .ignoreAspectRatio() .raw() - .toBuffer(function(err, data) { + .toBuffer(function (err, data) { if (err) { callback(err); } else { - var fingerprint = ''; - for (var col = 0; col < 8; col++) { - var gradient = 0; - for (var row = 0; row < 8; row++) { - var left = data[row * 8 + col]; - var right = data[row * 8 + col + 1]; + let fingerprint = ''; + for (let col = 0; col < 8; col++) { + for (let row = 0; row < 8; row++) { + const left = data[row * 8 + col]; + const right = data[row * 8 + col + 1]; fingerprint = fingerprint + (left < right ? '1' : '0'); } } @@ -109,14 +107,14 @@ module.exports = { path: getPath, // Path for expected output images - expected: function(filename) { + expected: function (filename) { return getPath(path.join('expected', filename)); }, // Verify similarity of expected vs actual images via fingerprint // Specify distance threshold using `options={threshold: 42}`, default // `threshold` is 5; - assertSimilar: function(expectedImage, actualImage, options, callback) { + assertSimilar: function (expectedImage, actualImage, options, callback) { if (typeof options === 'function') { callback = options; options = {}; @@ -138,12 +136,12 @@ module.exports = { throw new TypeError('`callback` must be a function'); } - fingerprint(expectedImage, function(err, expectedFingerprint) { + fingerprint(expectedImage, function (err, expectedFingerprint) { if (err) return callback(err); - fingerprint(actualImage, function(err, actualFingerprint) { + fingerprint(actualImage, function (err, actualFingerprint) { if (err) return callback(err); - var distance = 0; - for (var i = 0; i < 64; i++) { + let distance = 0; + for (let i = 0; i < 64; i++) { if (expectedFingerprint[i] !== actualFingerprint[i]) { distance++; } @@ -158,7 +156,7 @@ module.exports = { }); }, - assertMaxColourDistance: function(actualImagePath, expectedImagePath, acceptedDistance) { + assertMaxColourDistance: function (actualImagePath, expectedImagePath, acceptedDistance) { if (typeof actualImagePath !== 'string') { throw new TypeError('`actualImagePath` must be a string; got ' + actualImagePath); } @@ -169,7 +167,7 @@ module.exports = { // Default threshold acceptedDistance = 1; } - var distance = maxColourDistance(actualImagePath, expectedImagePath); + const distance = maxColourDistance(actualImagePath, expectedImagePath); if (distance > acceptedDistance) { throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance); } diff --git a/test/unit/joinChannel.js b/test/unit/joinChannel.js index 36c80e26..4ddcc19e 100644 --- a/test/unit/joinChannel.js +++ b/test/unit/joinChannel.js @@ -1,19 +1,18 @@ 'use strict'; -var assert = require('assert'); -var fs = require('fs'); -var sharp = require('../../index'); -var fixtures = require('../fixtures'); -var BluebirdPromise = require('bluebird'); +const assert = require('assert'); +const fs = require('fs'); -describe('Image channel insertion', function() { +const sharp = require('../../index'); +const fixtures = require('../fixtures'); - it('Grayscale to RGB, buffer', function(done) { +describe('Image channel insertion', function () { + it('Grayscale to RGB, buffer', function (done) { sharp(fixtures.inputPng) // gray -> red .resize(320, 240) .joinChannel(fixtures.inputPngTestJoinChannel) // new green channel .joinChannel(fixtures.inputPngStripesH) // new blue channel - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -22,12 +21,12 @@ describe('Image channel insertion', function() { }); }); - it('Grayscale to RGB, file', function(done) { + it('Grayscale to RGB, file', function (done) { sharp(fixtures.inputPng) // gray -> red .resize(320, 240) .joinChannel(fs.readFileSync(fixtures.inputPngTestJoinChannel)) // new green channel .joinChannel(fs.readFileSync(fixtures.inputPngStripesH)) // new blue channel - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -36,14 +35,14 @@ describe('Image channel insertion', function() { }); }); - it('Grayscale to RGBA, buffer', function(done) { + it('Grayscale to RGBA, buffer', function (done) { sharp(fixtures.inputPng) // gray -> red .resize(320, 240) .joinChannel([fixtures.inputPngTestJoinChannel, fixtures.inputPngStripesH, fixtures.inputPngStripesV]) // new green + blue + alpha channel .toColourspace(sharp.colourspace.srgb) - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -52,14 +51,14 @@ describe('Image channel insertion', function() { }); }); - it('Grayscale to RGBA, file', function(done) { + it('Grayscale to RGBA, file', function (done) { sharp(fixtures.inputPng) // gray -> red .resize(320, 240) .joinChannel([fs.readFileSync(fixtures.inputPngTestJoinChannel), // new green channel fs.readFileSync(fixtures.inputPngStripesH), // new blue channel fs.readFileSync(fixtures.inputPngStripesV)]) // new alpha channel .toColourspace('srgb') - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -68,7 +67,7 @@ describe('Image channel insertion', function() { }); }); - it('Grayscale to CMYK, buffers', function(done) { + it('Grayscale to CMYK, buffers', function (done) { sharp(fixtures.inputPng) // gray -> magenta .resize(320, 240) .joinChannel([fs.readFileSync(fixtures.inputPngTestJoinChannel), // new cyan channel @@ -76,7 +75,7 @@ describe('Image channel insertion', function() { fs.readFileSync(fixtures.inputPngStripesV)]) // new black channel .toColorspace('cmyk') .toFormat('jpeg') - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -85,12 +84,12 @@ describe('Image channel insertion', function() { }); }); - it('Join raw buffers to RGB', function(done) { - BluebirdPromise.all([ + it('Join raw buffers to RGB', function (done) { + Promise.all([ sharp(fixtures.inputPngTestJoinChannel).toColourspace('b-w').raw().toBuffer(), sharp(fixtures.inputPngStripesH).toColourspace('b-w').raw().toBuffer() ]) - .then(function(buffers) { + .then(function (buffers) { sharp(fixtures.inputPng) .resize(320, 240) .joinChannel(buffers, @@ -99,7 +98,7 @@ describe('Image channel insertion', function() { height: 240, channels: 1 }}) - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -107,19 +106,19 @@ describe('Image channel insertion', function() { fixtures.assertSimilar(fixtures.expected('joinChannel-rgb.jpg'), data, done); }); }) - .catch(function(err) { + .catch(function (err) { throw err; }); }); - it('Grayscale to RGBA, files, two arrays', function(done) { + it('Grayscale to RGBA, files, two arrays', function (done) { sharp(fixtures.inputPng) // gray -> red .resize(320, 240) .joinChannel([fs.readFileSync(fixtures.inputPngTestJoinChannel)]) // new green channel .joinChannel([fs.readFileSync(fixtures.inputPngStripesH), // new blue channel fs.readFileSync(fixtures.inputPngStripesV)]) // new alpha channel .toColourspace('srgb') - .toBuffer(function(err, data, info) { + .toBuffer(function (err, data, info) { if (err) throw err; assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); @@ -128,24 +127,23 @@ describe('Image channel insertion', function() { }); }); - it('Invalid raw buffer description', function() { - assert.throws(function() { - sharp().joinChannel(fs.readFileSync(fixtures.inputPng),{raw:{}}); + it('Invalid raw buffer description', function () { + assert.throws(function () { + sharp().joinChannel(fs.readFileSync(fixtures.inputPng), {raw: {}}); }); }); - it('Invalid input', function() { - assert.throws(function() { + it('Invalid input', function () { + assert.throws(function () { sharp(fixtures.inputJpg) .joinChannel(1); }); }); - it('No arguments', function() { - assert.throws(function() { + it('No arguments', function () { + assert.throws(function () { sharp(fixtures.inputJpg) .joinChannel(); }); }); - }); diff --git a/test/unit/jshint.js b/test/unit/jshint.js deleted file mode 100644 index ee8b8294..00000000 --- a/test/unit/jshint.js +++ /dev/null @@ -1 +0,0 @@ -require('mocha-jshint')(); diff --git a/test/unit/tile.js b/test/unit/tile.js index 189443d7..798a0921 100644 --- a/test/unit/tile.js +++ b/test/unit/tile.js @@ -1,35 +1,35 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); -var async = require('async'); -var rimraf = require('rimraf'); -var unzip = require('unzip'); +const eachLimit = require('async/eachLimit'); +const rimraf = require('rimraf'); +const unzip = require('unzip'); -var sharp = require('../../index'); -var fixtures = require('../fixtures'); +const sharp = require('../../index'); +const fixtures = require('../fixtures'); // 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 - var levels = fs.readdirSync(directory); + const levels = fs.readdirSync(directory); assert.strictEqual(expectedLevels, levels.length); // Get tiles - var tiles = []; - levels.forEach(function(level) { + const tiles = []; + levels.forEach(function (level) { // Verify level directory name assert.strictEqual(true, /^[0-9]+$/.test(level)); - fs.readdirSync(path.join(directory, level)).forEach(function(tile) { + fs.readdirSync(path.join(directory, level)).forEach(function (tile) { // Verify tile file name assert.strictEqual(true, /^[0-9]+_[0-9]+\.jpeg$/.test(tile)); tiles.push(path.join(directory, level, tile)); }); }); // Verify each tile is <= expectedSize - async.eachSeries(tiles, function(tile, done) { - sharp(tile).metadata(function(err, metadata) { + eachLimit(tiles, 8, function (tile, done) { + sharp(tile).metadata(function (err, metadata) { if (err) { done(err); } else { @@ -46,11 +46,10 @@ var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done }, done); }; -describe('Tile', function() { - - it('Valid size values pass', function() { - [1, 8192].forEach(function(size) { - assert.doesNotThrow(function() { +describe('Tile', function () { + it('Valid size values pass', function () { + [1, 8192].forEach(function (size) { + assert.doesNotThrow(function () { sharp().tile({ size: size }); @@ -58,9 +57,9 @@ describe('Tile', function() { }); }); - it('Invalid size values fail', function() { - ['zoinks', 1.1, -1, 0, 8193].forEach(function(size) { - assert.throws(function() { + it('Invalid size values fail', function () { + ['zoinks', 1.1, -1, 0, 8193].forEach(function (size) { + assert.throws(function () { sharp().tile({ size: size }); @@ -68,9 +67,9 @@ describe('Tile', function() { }); }); - it('Valid overlap values pass', function() { - [0, 8192].forEach(function(overlap) { - assert.doesNotThrow(function() { + it('Valid overlap values pass', function () { + [0, 8192].forEach(function (overlap) { + assert.doesNotThrow(function () { sharp().tile({ size: 8192, overlap: overlap @@ -79,9 +78,9 @@ describe('Tile', function() { }); }); - it('Invalid overlap values fail', function() { - ['zoinks', 1.1, -1, 8193].forEach(function(overlap) { - assert.throws(function() { + it('Invalid overlap values fail', function () { + ['zoinks', 1.1, -1, 8193].forEach(function (overlap) { + assert.throws(function () { sharp().tile({ overlap: overlap }); @@ -89,9 +88,9 @@ describe('Tile', function() { }); }); - it('Valid container values pass', function() { - ['fs', 'zip'].forEach(function(container) { - assert.doesNotThrow(function() { + it('Valid container values pass', function () { + ['fs', 'zip'].forEach(function (container) { + assert.doesNotThrow(function () { sharp().tile({ container: container }); @@ -99,9 +98,9 @@ describe('Tile', function() { }); }); - it('Invalid container values fail', function() { - ['zoinks', 1].forEach(function(container) { - assert.throws(function() { + it('Invalid container values fail', function () { + ['zoinks', 1].forEach(function (container) { + assert.throws(function () { sharp().tile({ container: container }); @@ -109,9 +108,9 @@ describe('Tile', function() { }); }); - it('Valid layout values pass', function() { - ['dz', 'google', 'zoomify'].forEach(function(layout) { - assert.doesNotThrow(function() { + it('Valid layout values pass', function () { + ['dz', 'google', 'zoomify'].forEach(function (layout) { + assert.doesNotThrow(function () { sharp().tile({ layout: layout }); @@ -119,9 +118,9 @@ describe('Tile', function() { }); }); - it('Invalid layout values fail', function() { - ['zoinks', 1].forEach(function(layout) { - assert.throws(function() { + it('Invalid layout values fail', function () { + ['zoinks', 1].forEach(function (layout) { + assert.throws(function () { sharp().tile({ layout: layout }); @@ -129,25 +128,24 @@ describe('Tile', function() { }); }); - it('Prevent larger overlap than default size', function() { - assert.throws(function() { + it('Prevent larger overlap than default size', function () { + assert.throws(function () { sharp().tile({overlap: 257}); }); }); - it('Prevent larger overlap than provided size', function() { - assert.throws(function() { + it('Prevent larger overlap than provided size', function () { + assert.throws(function () { sharp().tile({size: 512, overlap: 513}); }); }); if (sharp.format.dz.output.file) { - - it('Deep Zoom layout', function(done) { - var directory = fixtures.path('output.dzi_files'); - rimraf(directory, function() { + it('Deep Zoom layout', function (done) { + const directory = fixtures.path('output.dzi_files'); + rimraf(directory, function () { sharp(fixtures.inputJpg) - .toFile(fixtures.path('output.dzi'), function(err, info) { + .toFile(fixtures.path('output.dzi'), function (err, info) { if (err) throw err; assert.strictEqual('dz', info.format); assertDeepZoomTiles(directory, 256, 13, done); @@ -155,15 +153,15 @@ describe('Tile', function() { }); }); - it('Deep Zoom layout with custom size+overlap', function(done) { - var directory = fixtures.path('output.512.dzi_files'); - rimraf(directory, function() { + it('Deep Zoom layout with custom size+overlap', function (done) { + const directory = fixtures.path('output.512.dzi_files'); + rimraf(directory, function () { sharp(fixtures.inputJpg) .tile({ size: 512, overlap: 16 }) - .toFile(fixtures.path('output.512.dzi'), function(err, info) { + .toFile(fixtures.path('output.512.dzi'), function (err, info) { if (err) throw err; assert.strictEqual('dz', info.format); assertDeepZoomTiles(directory, 512 + 2 * 16, 13, done); @@ -171,17 +169,17 @@ describe('Tile', function() { }); }); - it('Zoomify layout', function(done) { - var directory = fixtures.path('output.zoomify.dzi'); - rimraf(directory, function() { + it('Zoomify layout', function (done) { + const directory = fixtures.path('output.zoomify.dzi'); + rimraf(directory, function () { sharp(fixtures.inputJpg) .tile({ layout: 'zoomify' }) - .toFile(fixtures.path('output.zoomify.dzi'), function(err, info) { + .toFile(fixtures.path('output.zoomify.dzi'), function (err, info) { if (err) throw err; assert.strictEqual('dz', info.format); - fs.stat(path.join(directory, 'ImageProperties.xml'), function(err, stat) { + fs.stat(path.join(directory, 'ImageProperties.xml'), function (err, stat) { if (err) throw err; assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.size > 0); @@ -191,17 +189,17 @@ describe('Tile', function() { }); }); - it('Google layout', function(done) { - var directory = fixtures.path('output.google.dzi'); - rimraf(directory, function() { + it('Google layout', function (done) { + const directory = fixtures.path('output.google.dzi'); + rimraf(directory, function () { sharp(fixtures.inputJpg) .tile({ layout: 'google' }) - .toFile(directory, function(err, info) { + .toFile(directory, function (err, info) { if (err) throw err; assert.strictEqual('dz', info.format); - fs.stat(path.join(directory, '0', '0', '0.jpg'), function(err, stat) { + fs.stat(path.join(directory, '0', '0', '0.jpg'), function (err, stat) { if (err) throw err; assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.size > 0); @@ -211,23 +209,23 @@ describe('Tile', function() { }); }); - it('Write to ZIP container using file extension', function(done) { - var container = fixtures.path('output.dz.container.zip'); - var extractTo = fixtures.path('output.dz.container'); - var directory = path.join(extractTo, 'output.dz.container_files'); - rimraf(directory, function() { + it('Write to ZIP container using file extension', function (done) { + const container = fixtures.path('output.dz.container.zip'); + const extractTo = fixtures.path('output.dz.container'); + const directory = path.join(extractTo, 'output.dz.container_files'); + rimraf(directory, function () { sharp(fixtures.inputJpg) - .toFile(container, function(err, info) { + .toFile(container, function (err, info) { if (err) throw err; assert.strictEqual('dz', info.format); - fs.stat(container, function(err, stat) { + fs.stat(container, function (err, stat) { if (err) throw err; assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.size > 0); fs.createReadStream(container) .pipe(unzip.Extract({path: path.dirname(extractTo)})) - .on('error', function(err) { throw err; }) - .on('close', function() { + .on('error', function (err) { throw err; }) + .on('close', function () { assertDeepZoomTiles(directory, 256, 13, done); }); }); @@ -235,34 +233,32 @@ describe('Tile', function() { }); }); - it('Write to ZIP container using container tile option', function(done) { - var container = fixtures.path('output.dz.containeropt.zip'); - var extractTo = fixtures.path('output.dz.containeropt'); - var directory = path.join(extractTo, 'output.dz.containeropt_files'); - rimraf(directory, function() { + it('Write to ZIP container using container tile option', function (done) { + const container = fixtures.path('output.dz.containeropt.zip'); + const extractTo = fixtures.path('output.dz.containeropt'); + const directory = path.join(extractTo, 'output.dz.containeropt_files'); + rimraf(directory, function () { sharp(fixtures.inputJpg) .tile({ container: 'zip' }) - .toFile(container, function(err, info) { + .toFile(container, function (err, info) { // Vips overrides .dzi extension to .zip used by container var below if (err) throw err; assert.strictEqual('dz', info.format); - fs.stat(container, function(err, stat) { + fs.stat(container, function (err, stat) { if (err) throw err; assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.size > 0); fs.createReadStream(container) .pipe(unzip.Extract({path: path.dirname(extractTo)})) - .on('error', function(err) { throw err; }) - .on('close', function() { + .on('error', function (err) { throw err; }) + .on('close', function () { assertDeepZoomTiles(directory, 256, 13, done); }); }); }); }); }); - } - });