From 2d1b2df1c715420e82523ad5d5c49f7466678954 Mon Sep 17 00:00:00 2001 From: Tomato Date: Sat, 11 Nov 2017 07:41:58 +0100 Subject: [PATCH 01/15] Add support for different resize methods This adds support for the three (or actually just two, as the default has always been scale) new resize modes in the tinypng API. --- tinypng-cli.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tinypng-cli.js b/tinypng-cli.js index 77dc430..c4fc6db 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -33,6 +33,7 @@ if (argv.v || argv.version) { ' -r, --recursive Walk given directory recursively\n' + ' --width Resize an image to a specified width\n' + ' --height Resize an image to a specified height\n' + + ' --resize-mode Specify the resize method to use (scale, fit or cover)\n', ' -v, --version Show installed version\n' + ' -h, --help Show help' ); @@ -46,6 +47,7 @@ if (argv.v || argv.version) { var key = ''; var resize = {}; + var resize_modes = ['scale', 'fit', 'cover']; if (argv.k || argv.key) { key = typeof(argv.k || argv.key) === 'string' ? (argv.k || argv.key).trim() : ''; @@ -69,6 +71,19 @@ if (argv.v || argv.version) { } } + if (argv['resize-method']) { + if (typeof(argv['resize-method']) === 'string' && resize_modes.includes(argv['resize-method'].toLowerCase())) + { + if (argv['resize-method'] != 'scale' && resize.width === undefined || argv['resize-method'] != 'scale' && resize.height === undefined) { + console.log(chalk.bold.red('This resize mode requires you to specify a width and a height.')); + } else { + resize.method = argv['resize-method']; + } + } else { + console.log(chalk.bold.red(`Invalid resize mode specified. Valid modes are: ${resize_modes.join(', ')}`)); + } + } + if (key.length === 0) { console.log(chalk.bold.red('No API key specified. You can get one at ' + chalk.underline('https://tinypng.com/developers') + '.')); From 4015b337153f88006968ce99cb7b229ca13c4a61 Mon Sep 17 00:00:00 2001 From: Tomato Date: Sun, 12 Nov 2017 03:05:28 +0100 Subject: [PATCH 02/15] Update tinypng-cli.js --- tinypng-cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinypng-cli.js b/tinypng-cli.js index c4fc6db..a45b558 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -33,7 +33,7 @@ if (argv.v || argv.version) { ' -r, --recursive Walk given directory recursively\n' + ' --width Resize an image to a specified width\n' + ' --height Resize an image to a specified height\n' + - ' --resize-mode Specify the resize method to use (scale, fit or cover)\n', + ' --resize-mode Specify the resize method to use (scale, fit or cover)\n' + ' -v, --version Show installed version\n' + ' -h, --help Show help' ); From 721589fdd87941be9664af998fa8cf8db33dae59 Mon Sep 17 00:00:00 2001 From: Franck Date: Sat, 28 Apr 2018 23:19:48 +0200 Subject: [PATCH 03/15] Optimize only if width or height is bigger than the provided value (in pixels) --- package.json | 5 +- tinypng-cli.js | 123 +++++++++++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 88d3816..7f5c45b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "array-uniq": "^1.0.2", "chalk": "^1.1.3", "glob": "^7.0.3", + "imageinfo": "^1.0.4", "minimatch": "^3.0.0", "minimist": "^1.2.0", "prettysize": "0.0.3", @@ -33,8 +34,8 @@ "tinypng": "tinypng-cli.js" }, "devDependencies": { - "eslint": "^2.7.0", - "jshint": "^2.9.1" + "eslint": "^2.13.1", + "jshint": "^2.9.5" }, "scripts": { "test": "jshint tinypng-cli.js && eslint tinypng-cli.js" diff --git a/tinypng-cli.js b/tinypng-cli.js index a45b558..bccd0cf 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -7,6 +7,7 @@ var glob = require('glob'); var uniq = require('array-uniq'); var chalk = require('chalk'); var pretty = require('prettysize'); +var imageinfo = require('imageinfo'); var argv = require('minimist')(process.argv.slice(2)); var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; @@ -34,6 +35,7 @@ if (argv.v || argv.version) { ' --width Resize an image to a specified width\n' + ' --height Resize an image to a specified height\n' + ' --resize-mode Specify the resize method to use (scale, fit or cover)\n' + + ' --if-larger-than Optimize only if width or height is bigger than the provided value (in pixels)\n' + ' -v, --version Show installed version\n' + ' -h, --help Show help' ); @@ -72,15 +74,23 @@ if (argv.v || argv.version) { } if (argv['resize-method']) { - if (typeof(argv['resize-method']) === 'string' && resize_modes.includes(argv['resize-method'].toLowerCase())) - { + if (typeof(argv['resize-method']) === 'string' && resize_modes.includes(argv['resize-method'].toLowerCase())) { if (argv['resize-method'] != 'scale' && resize.width === undefined || argv['resize-method'] != 'scale' && resize.height === undefined) { - console.log(chalk.bold.red('This resize mode requires you to specify a width and a height.')); + console.log(chalk.bold.red('This resize mode requires you to specify a width and a height.')); } else { resize.method = argv['resize-method']; } } else { - console.log(chalk.bold.red(`Invalid resize mode specified. Valid modes are: ${resize_modes.join(', ')}`)); + console.log(chalk.bold.red(`Invalid resize mode specified. Valid modes are: ${resize_modes.join(', ')}`)); + } + } + + if (argv['if-larger-than']) { + if (typeof(argv['if-larger-than']) === 'number') { + resize.maxSize = argv['if-larger-than']; + console.log('filter: image should be larger than ' + resize.maxSize + ' pixels (width or height) to be processed\n'); + } else { + console.log(chalk.bold.red(`Invalid size specified. Please use a number (in pixels).`)); } } @@ -117,72 +127,87 @@ if (argv.v || argv.version) { unique.forEach(function(file) { - fs.createReadStream(file).pipe(request.post('https://api.tinify.com/shrink', { - auth: { - 'user': 'api', - 'pass': key - } - }, function(error, response, body) { + fs.readFile(file, function(err, data) { + if (err) throw err; + + var push = true; + var info = imageinfo(data); - try { - body = JSON.parse(body); - } catch (e) { - console.log(chalk.red('\u2718 Not a valid JSON response for `' + file + '`')); - return; + if (resize.maxSize) { + var thisMaxSize = Math.max(info.width, info.height); + if (thisMaxSize < resize.maxSize) { + console.log(chalk.red('\u0021 Image is too small (' + info.width + 'x' + info.height + '): `' + file + '`')); + push = false; + } } - if (!error && response) { + if (push === true) { - if (response.statusCode === 201) { + fs.createReadStream(file).pipe(request.post('https://api.tinify.com/shrink', { + auth: { + 'user': 'api', + 'pass': key + } + }, function(error, response, body) { - if (body.output.size < body.input.size) { + try { + body = JSON.parse(body); + } catch (e) { + console.log(chalk.red('\u2718 Not a valid JSON response for `' + file + '`')); + return; + } - console.log(chalk.green('\u2714 Panda just saved you ' + chalk.bold(pretty(body.input.size - body.output.size) + ' (' + Math.round(100 - 100 / body.input.size * body.output.size) + '%)') + ' for `' + file + '`')); + if (!error && response) { - if (resize.hasOwnProperty('height') || resize.hasOwnProperty('width')) { + if (response.statusCode === 201) { - request.get(body.output.url, { - auth: { - 'user': 'api', - 'pass': key - }, - json: { - 'resize': resize - } - }).pipe(fs.createWriteStream(file)); + if (body.output.size < body.input.size) { - } else { + console.log(chalk.green('\u2714 Panda just saved you ' + chalk.bold(pretty(body.input.size - body.output.size) + ' (' + Math.round(100 - 100 / body.input.size * body.output.size) + '%)') + ' for `' + file + '`')); - request.get(body.output.url).pipe(fs.createWriteStream(file)); + if (resize.hasOwnProperty('height') || resize.hasOwnProperty('width')) { - } + request.get(body.output.url, { + auth: { + 'user': 'api', + 'pass': key + }, + json: { + 'resize': resize + } + }).pipe(fs.createWriteStream(file)); - } else { + } else { - console.log(chalk.yellow('\u2718 Couldn’t compress `' + file + '` any further')); + request.get(body.output.url).pipe(fs.createWriteStream(file)); - } + } + + } else { + + console.log(chalk.yellow('\u2718 Couldn’t compress `' + file + '` any further')); + + } + + } else { - } else { + if (body.error === 'TooManyRequests') { + console.log(chalk.red('\u2718 Compression failed for `' + file + '` as your monthly limit has been exceeded')); + } else if (body.error === 'Unauthorized') { + console.log(chalk.red('\u2718 Compression failed for `' + file + '` as your credentials are invalid')); + } else { + console.log(chalk.red('\u2718 Compression failed for `' + file + '`')); + } - if (body.error === 'TooManyRequests') { - console.log(chalk.red('\u2718 Compression failed for `' + file + '` as your monthly limit has been exceeded')); - } else if (body.error === 'Unauthorized') { - console.log(chalk.red('\u2718 Compression failed for `' + file + '` as your credentials are invalid')); + } } else { - console.log(chalk.red('\u2718 Compression failed for `' + file + '`')); + console.log(chalk.red('\u2718 Got no response for `' + file + '`')); } + })); - } - } else { - console.log(chalk.red('\u2718 Got no response for `' + file + '`')); } - })); - + }); }); - } - } - } From 9c637f7b97727110fdc93c510498854f76acfaaa Mon Sep 17 00:00:00 2001 From: Franck Date: Sat, 28 Apr 2018 23:23:25 +0200 Subject: [PATCH 04/15] Optimize only if file weight if bigger than the provided value (in bytes) --- tinypng-cli.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tinypng-cli.js b/tinypng-cli.js index bccd0cf..4b3e2e7 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -36,6 +36,7 @@ if (argv.v || argv.version) { ' --height Resize an image to a specified height\n' + ' --resize-mode Specify the resize method to use (scale, fit or cover)\n' + ' --if-larger-than Optimize only if width or height is bigger than the provided value (in pixels)\n' + + ' --if-bigger-than Optimize only if file weight if bigger than the provided value (in bytes)\n' + ' -v, --version Show installed version\n' + ' -h, --help Show help' ); @@ -94,6 +95,15 @@ if (argv.v || argv.version) { } } + if (argv['if-bigger-than']) { + if (typeof(argv['if-bigger-than']) === 'number') { + resize.maxWeight = argv['if-bigger-than']; + console.log('filter: files should be bigger than ' + pretty(resize.maxWeight) + ' to be processed\n'); + } else { + console.log(chalk.bold.red(`Invalid weight specified. Please use a number (in bytes).`)); + } + } + if (key.length === 0) { console.log(chalk.bold.red('No API key specified. You can get one at ' + chalk.underline('https://tinypng.com/developers') + '.')); @@ -141,6 +151,13 @@ if (argv.v || argv.version) { } } + if (resize.maxWeight) { + if (data.length < resize.maxWeight) { + console.log(chalk.red('\u0021 File is too light ' + chalk.bold(pretty(data.length)) + ': `' + file + '`')); + push = false; + } + } + if (push === true) { fs.createReadStream(file).pipe(request.post('https://api.tinify.com/shrink', { From c63e61301647729af7f7ded22d26e950ab955b10 Mon Sep 17 00:00:00 2001 From: Franck Date: Sat, 28 Apr 2018 23:29:25 +0200 Subject: [PATCH 05/15] Returns the number of files optimized by Panda during the current month --- tinypng-cli.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tinypng-cli.js b/tinypng-cli.js index 4b3e2e7..c877c2a 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -13,6 +13,8 @@ var argv = require('minimist')(process.argv.slice(2)); var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; var version = require('./package.json').version; +var optimizedCounter; + if (argv.v || argv.version) { console.log(version); @@ -132,6 +134,10 @@ if (argv.v || argv.version) { } else { + process.on('exit', function() { + if (optimizedCounter) console.log(chalk.bold.green('\u2714 Number of optimized files this month: ' + optimizedCounter)); + }); + console.log(chalk.bold.green('\u2714 Found ' + unique.length + ' image' + (unique.length === 1 ? '' : 's')) + '\n'); console.log(chalk.bold('Processing...')); @@ -178,6 +184,8 @@ if (argv.v || argv.version) { if (response.statusCode === 201) { + optimizedCounter = response.headers['compression-count']; + if (body.output.size < body.input.size) { console.log(chalk.green('\u2714 Panda just saved you ' + chalk.bold(pretty(body.input.size - body.output.size) + ' (' + Math.round(100 - 100 / body.input.size * body.output.size) + '%)') + ' for `' + file + '`')); From 5ed2cdfc61f310d6a8fc98d057759d5660dbc136 Mon Sep 17 00:00:00 2001 From: Franck Date: Sat, 28 Apr 2018 23:35:00 +0200 Subject: [PATCH 06/15] missing package-lock.json --- package-lock.json | 1619 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1619 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4ef546a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1619 @@ +{ + "name": "tinypng-cli", + "version": "0.0.7", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "4.2.1" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "7.1.2" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "1.0.1" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "0.1.4" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.1" + } + } + } + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "0.10.42" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "1.1.3", + "entities": "1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "es5-ext": { + "version": "0.10.42", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", + "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", + "dev": true, + "requires": { + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "next-tick": "1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-symbol": "3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.1", + "estraverse": "4.2.0" + } + }, + "eslint": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-2.13.1.tgz", + "integrity": "sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "concat-stream": "1.6.2", + "debug": "2.6.9", + "doctrine": "1.5.0", + "es6-map": "0.1.5", + "escope": "3.6.0", + "espree": "3.5.4", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "1.3.1", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.8", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.17.2", + "is-resolvable": "1.1.0", + "js-yaml": "3.11.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "optionator": "0.8.2", + "path-is-absolute": "1.0.1", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.6.1", + "strip-json-comments": "1.0.4", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" + } + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "5.5.3", + "acorn-jsx": "3.0.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "file-entry-cache": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", + "integrity": "sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "1.0.2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.3.0", + "domutils": "1.5.1", + "entities": "1.0.0", + "readable-stream": "1.1.14" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.1" + } + }, + "ignore": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", + "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", + "dev": true + }, + "imageinfo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/imageinfo/-/imageinfo-1.0.4.tgz", + "integrity": "sha1-HdJFbsuW/DlfCqEXnEZ9+z1deio=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "requires": { + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.2.0", + "figures": "1.7.0", + "lodash": "4.17.10", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true + }, + "is-my-json-valid": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", + "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "is-my-ip-valid": "1.0.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-yaml": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "jshint": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz", + "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", + "dev": true, + "requires": { + "cli": "1.0.1", + "console-browserify": "1.1.0", + "exit": "0.1.2", + "htmlparser2": "3.8.3", + "lodash": "3.7.0", + "minimatch": "3.0.4", + "shelljs": "0.3.0", + "strip-json-comments": "1.0.4" + }, + "dependencies": { + "lodash": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", + "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=", + "dev": true + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + } + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "1.33.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prettysize": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/prettysize/-/prettysize-0.0.3.tgz", + "integrity": "sha1-FK//amReWRpN3xxykZwjtBRhgaE=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "mute-stream": "0.0.5" + } + }, + "request": { + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", + "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "1.1.1", + "onetime": "1.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "shelljs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz", + "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "requires": { + "hoek": "4.2.1" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.10", + "slice-ansi": "0.0.4", + "string-width": "2.1.1" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } +} From 4257739f91151bdec2d740afad0dc285e5a8d69a Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 09:14:39 +0200 Subject: [PATCH 07/15] halt script on invalid argument --- tinypng-cli.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tinypng-cli.js b/tinypng-cli.js index c877c2a..e8a7f46 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -65,6 +65,7 @@ if (argv.v || argv.version) { resize.width = argv.width; } else { console.log(chalk.bold.red('Invalid width specified. Please specify a numeric value only.')); + return; } } @@ -73,6 +74,7 @@ if (argv.v || argv.version) { resize.height = argv.height; } else { console.log(chalk.bold.red('Invalid height specified. Please specify a numeric value only.')); + return; } } @@ -85,6 +87,7 @@ if (argv.v || argv.version) { } } else { console.log(chalk.bold.red(`Invalid resize mode specified. Valid modes are: ${resize_modes.join(', ')}`)); + return; } } @@ -94,6 +97,7 @@ if (argv.v || argv.version) { console.log('filter: image should be larger than ' + resize.maxSize + ' pixels (width or height) to be processed\n'); } else { console.log(chalk.bold.red(`Invalid size specified. Please use a number (in pixels).`)); + return; } } @@ -103,6 +107,7 @@ if (argv.v || argv.version) { console.log('filter: files should be bigger than ' + pretty(resize.maxWeight) + ' to be processed\n'); } else { console.log(chalk.bold.red(`Invalid weight specified. Please use a number (in bytes).`)); + return; } } From aa887cc9e5a53775c198e004eff5f174b8c13f72 Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 10:18:47 +0200 Subject: [PATCH 08/15] Refacto --- tinypng-cli.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tinypng-cli.js b/tinypng-cli.js index e8a7f46..9bcd1a3 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -13,8 +13,6 @@ var argv = require('minimist')(process.argv.slice(2)); var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; var version = require('./package.json').version; -var optimizedCounter; - if (argv.v || argv.version) { console.log(version); @@ -139,6 +137,7 @@ if (argv.v || argv.version) { } else { + var optimizedCounter; process.on('exit', function() { if (optimizedCounter) console.log(chalk.bold.green('\u2714 Number of optimized files this month: ' + optimizedCounter)); }); From e6c7129d0b3995275702db8af4d0ca2ede6a79bd Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 16:12:34 +0200 Subject: [PATCH 09/15] Use a cache to not optimize the same file twice --- package-lock.json | 109 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 2 + tinypng-cli.js | 57 +++++++++++++++++++----- 3 files changed, 149 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ef546a..b005dad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -199,6 +199,19 @@ "glob": "7.1.2" } }, + "cli-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.2.0.tgz", + "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=", + "requires": { + "ansi-regex": "2.1.1", + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "memoizee": "0.4.12", + "timers-ext": "0.1.5" + } + }, "cli-cursor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", @@ -286,7 +299,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, "requires": { "es5-ext": "0.10.42" } @@ -408,6 +420,17 @@ "jsbn": "0.1.1" } }, + "empty-folder": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/empty-folder/-/empty-folder-2.0.1.tgz", + "integrity": "sha1-FCJdhdZuAqA5z306zJzr5ZvVIgc=", + "requires": { + "list-contents": "4.0.1", + "move-on": "1.1.1", + "of-type": "2.1.3", + "typeof-arguments": "3.1.2" + } + }, "entities": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", @@ -418,7 +441,6 @@ "version": "0.10.42", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", - "dev": true, "requires": { "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", @@ -429,7 +451,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.42", @@ -467,7 +488,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.42" @@ -477,7 +497,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.42", @@ -584,7 +603,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, "requires": { "d": "1.0.0", "es5-ext": "0.10.42" @@ -933,6 +951,11 @@ "path-is-inside": "1.0.2" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -961,6 +984,11 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "js-md5": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz", + "integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==" + }, "js-yaml": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", @@ -1064,12 +1092,45 @@ "type-check": "0.3.2" } }, + "list-contents": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/list-contents/-/list-contents-4.0.1.tgz", + "integrity": "sha1-T/iD94IoM1PmESb2dO2JiJgodMY=", + "requires": { + "move-on": "1.1.1", + "of-type": "2.1.3", + "typeof-arguments": "3.1.2" + } + }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "requires": { + "es5-ext": "0.10.42" + } + }, + "memoizee": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.12.tgz", + "integrity": "sha512-sprBu6nwxBWBvBOh5v2jcsGqiGLlL2xr2dLub3vR8dnE8YB17omwtm/0NSHl8jjNbcsJd5GMWJAnTSVe/O0Wfg==", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-weak-map": "2.0.2", + "event-emitter": "0.3.5", + "is-promise": "2.1.0", + "lru-queue": "0.1.0", + "next-tick": "1.0.0", + "timers-ext": "0.1.5" + } + }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", @@ -1113,6 +1174,16 @@ } } }, + "move-on": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/move-on/-/move-on-1.1.1.tgz", + "integrity": "sha512-0AvPNEQYJYjMYGfLNzCjQkGZfdF/FNs1Xb1zB+3wTtyO15SCUL7bA5+vO4hfEPR4rPzS6b3qqCQ6OIKTV+5Jqw==", + "requires": { + "cli-color": "1.2.0", + "of-type": "2.1.3", + "typeof-arguments": "3.1.2" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1128,8 +1199,7 @@ "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "number-is-nan": { "version": "1.0.1", @@ -1148,6 +1218,11 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "of-type": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/of-type/-/of-type-2.1.3.tgz", + "integrity": "sha512-LnrQJfK/GOhu1RC+wEva9SjdsqI8aXAXn9oh0IB/IO3/GIg8PeuOdVfilIv4UQ0TXRyZ5xQX7ov4xgt+VvVpcw==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1522,6 +1597,15 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "timers-ext": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz", + "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==", + "requires": { + "es5-ext": "0.10.42", + "next-tick": "1.0.0" + } + }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", @@ -1559,6 +1643,15 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typeof-arguments": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/typeof-arguments/-/typeof-arguments-3.1.2.tgz", + "integrity": "sha512-1pIRwDrVgoZaAXpX+1+kDijuFhV1wiy8hcDs/jDp0siS8wrBvG5B3NieN0msptcy/B5criVXUDJIEhZcgkwB/w==", + "requires": { + "cli-color": "1.2.0", + "of-type": "2.1.3" + } + }, "user-home": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", diff --git a/package.json b/package.json index 7f5c45b..c514898 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,10 @@ "dependencies": { "array-uniq": "^1.0.2", "chalk": "^1.1.3", + "empty-folder": "^2.0.1", "glob": "^7.0.3", "imageinfo": "^1.0.4", + "js-md5": "^0.7.3", "minimatch": "^3.0.0", "minimist": "^1.2.0", "prettysize": "0.0.3", diff --git a/tinypng-cli.js b/tinypng-cli.js index 9bcd1a3..ff383b6 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -1,18 +1,22 @@ #!/usr/bin/env node -var fs = require('fs'); -var request = require('request'); -var minimatch = require('minimatch'); -var glob = require('glob'); -var uniq = require('array-uniq'); -var chalk = require('chalk'); -var pretty = require('prettysize'); -var imageinfo = require('imageinfo'); +const fs = require('fs'); +const request = require('request'); +const minimatch = require('minimatch'); +const glob = require('glob'); +const uniq = require('array-uniq'); +const chalk = require('chalk'); +const pretty = require('prettysize'); +const imageinfo = require('imageinfo'); +const md5 = require('js-md5'); +const emptyFolder = require('empty-folder'); var argv = require('minimist')(process.argv.slice(2)); var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; var version = require('./package.json').version; +var cacheDirectory = home + '/.tinypng-cache/'; + if (argv.v || argv.version) { console.log(version); @@ -37,6 +41,7 @@ if (argv.v || argv.version) { ' --resize-mode Specify the resize method to use (scale, fit or cover)\n' + ' --if-larger-than Optimize only if width or height is bigger than the provided value (in pixels)\n' + ' --if-bigger-than Optimize only if file weight if bigger than the provided value (in bytes)\n' + + ' --clear-cache Clear cache and stop\n' + ' -v, --version Show installed version\n' + ' -h, --help Show help' ); @@ -58,6 +63,25 @@ if (argv.v || argv.version) { key = fs.readFileSync(home + '/.tinypng', 'utf8').trim(); } + if (!fs.existsSync(cacheDirectory)) { + fs.mkdirSync(cacheDirectory); + console.log(chalk.bold(`Cache directory does not exist:`) + ` creating ${cacheDirectory}`); + } + + if (argv['clear-cache']) { + if (fs.existsSync(cacheDirectory)) { + + emptyFolder(cacheDirectory, false, (o) => { + if (o.error) { + console.log(chalk.bold(`Cannot empty cache directory: ` + err)); + return; + } + }); + } + console.log(chalk.bold(`Cache is clean`)); + return; + }; + if (argv.width) { if (typeof(argv.width) === 'number') { resize.width = argv.width; @@ -137,7 +161,7 @@ if (argv.v || argv.version) { } else { - var optimizedCounter; + var optimizedCounter; process.on('exit', function() { if (optimizedCounter) console.log(chalk.bold.green('\u2714 Number of optimized files this month: ' + optimizedCounter)); }); @@ -168,6 +192,12 @@ if (argv.v || argv.version) { } } + var hash = md5(file + JSON.stringify(resize)); + if (fs.existsSync(`${cacheDirectory}/${hash}`)) { + console.log(chalk.red(`\u0021 File already packed: ${file}`)); + push = false; + } + if (push === true) { fs.createReadStream(file).pipe(request.post('https://api.tinify.com/shrink', { @@ -194,6 +224,12 @@ if (argv.v || argv.version) { console.log(chalk.green('\u2714 Panda just saved you ' + chalk.bold(pretty(body.input.size - body.output.size) + ' (' + Math.round(100 - 100 / body.input.size * body.output.size) + '%)') + ' for `' + file + '`')); + fs.writeFile(cacheDirectory + hash, hash, (error) => { + if (error) { + console.log(chalk.red('\u2718 Cannot write cache file')); + } + }); + if (resize.hasOwnProperty('height') || resize.hasOwnProperty('width')) { request.get(body.output.url, { @@ -211,10 +247,9 @@ if (argv.v || argv.version) { request.get(body.output.url).pipe(fs.createWriteStream(file)); } - } else { - console.log(chalk.yellow('\u2718 Couldn’t compress `' + file + '` any further')); + console.log(chalk.yellow(`\u2718 Couldn’t compress ${file} any further`)); } From d79b81377fa65c8c1f4483eae5c41fcb1d5f4452 Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 16:23:05 +0200 Subject: [PATCH 10/15] Verbose mode --- tinypng-cli.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tinypng-cli.js b/tinypng-cli.js index ff383b6..74fe4f3 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -43,6 +43,7 @@ if (argv.v || argv.version) { ' --if-bigger-than Optimize only if file weight if bigger than the provided value (in bytes)\n' + ' --clear-cache Clear cache and stop\n' + ' -v, --version Show installed version\n' + + ' -V Verbose mode\n' + ' -h, --help Show help' ); @@ -57,6 +58,8 @@ if (argv.v || argv.version) { var resize = {}; var resize_modes = ['scale', 'fit', 'cover']; + var verbose = false; + if (argv.k || argv.key) { key = typeof(argv.k || argv.key) === 'string' ? (argv.k || argv.key).trim() : ''; } else if (fs.existsSync(home + '/.tinypng')) { @@ -82,6 +85,11 @@ if (argv.v || argv.version) { return; }; + if (argv.V) { + console.log('. Verbose mode is ON'); + verbose = true; + } + if (argv.width) { if (typeof(argv.width) === 'number') { resize.width = argv.width; @@ -116,7 +124,7 @@ if (argv.v || argv.version) { if (argv['if-larger-than']) { if (typeof(argv['if-larger-than']) === 'number') { resize.maxSize = argv['if-larger-than']; - console.log('filter: image should be larger than ' + resize.maxSize + ' pixels (width or height) to be processed\n'); + console.log('. Image(s) should be larger than ' + resize.maxSize + ' pixels (width or height) to be processed\n'); } else { console.log(chalk.bold.red(`Invalid size specified. Please use a number (in pixels).`)); return; @@ -126,7 +134,7 @@ if (argv.v || argv.version) { if (argv['if-bigger-than']) { if (typeof(argv['if-bigger-than']) === 'number') { resize.maxWeight = argv['if-bigger-than']; - console.log('filter: files should be bigger than ' + pretty(resize.maxWeight) + ' to be processed\n'); + console.log('. Files should be bigger than ' + pretty(resize.maxWeight) + ' to be processed\n'); } else { console.log(chalk.bold.red(`Invalid weight specified. Please use a number (in bytes).`)); return; @@ -136,6 +144,7 @@ if (argv.v || argv.version) { if (key.length === 0) { console.log(chalk.bold.red('No API key specified. You can get one at ' + chalk.underline('https://tinypng.com/developers') + '.')); + return; } else { @@ -180,21 +189,21 @@ if (argv.v || argv.version) { if (resize.maxSize) { var thisMaxSize = Math.max(info.width, info.height); if (thisMaxSize < resize.maxSize) { - console.log(chalk.red('\u0021 Image is too small (' + info.width + 'x' + info.height + '): `' + file + '`')); + if (verbose) console.log(chalk.red('\u0021 Image is too small (' + info.width + 'x' + info.height + '): `' + file + '`')); push = false; } } if (resize.maxWeight) { if (data.length < resize.maxWeight) { - console.log(chalk.red('\u0021 File is too light ' + chalk.bold(pretty(data.length)) + ': `' + file + '`')); + if (verbose) console.log(chalk.red('\u0021 File is too light ' + chalk.bold(pretty(data.length)) + ': `' + file + '`')); push = false; } } var hash = md5(file + JSON.stringify(resize)); if (fs.existsSync(`${cacheDirectory}/${hash}`)) { - console.log(chalk.red(`\u0021 File already packed: ${file}`)); + if (verbose) console.log(chalk.red(`\u0021 File already packed: ${file}`)); push = false; } From 8ceffcdf0fe0e7adb9ced38795a9580c8b23c61e Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 17:14:09 +0200 Subject: [PATCH 11/15] more es6 compliant way to end script on error --- tinypng-cli.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tinypng-cli.js b/tinypng-cli.js index 74fe4f3..b151948 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -82,8 +82,8 @@ if (argv.v || argv.version) { }); } console.log(chalk.bold(`Cache is clean`)); - return; - }; + process.exit(-1); + } if (argv.V) { console.log('. Verbose mode is ON'); @@ -95,7 +95,7 @@ if (argv.v || argv.version) { resize.width = argv.width; } else { console.log(chalk.bold.red('Invalid width specified. Please specify a numeric value only.')); - return; + process.exit(-1); } } @@ -104,7 +104,7 @@ if (argv.v || argv.version) { resize.height = argv.height; } else { console.log(chalk.bold.red('Invalid height specified. Please specify a numeric value only.')); - return; + process.exit(-1); } } @@ -117,7 +117,7 @@ if (argv.v || argv.version) { } } else { console.log(chalk.bold.red(`Invalid resize mode specified. Valid modes are: ${resize_modes.join(', ')}`)); - return; + process.exit(-1); } } @@ -127,7 +127,7 @@ if (argv.v || argv.version) { console.log('. Image(s) should be larger than ' + resize.maxSize + ' pixels (width or height) to be processed\n'); } else { console.log(chalk.bold.red(`Invalid size specified. Please use a number (in pixels).`)); - return; + process.exit(-1); } } @@ -137,14 +137,14 @@ if (argv.v || argv.version) { console.log('. Files should be bigger than ' + pretty(resize.maxWeight) + ' to be processed\n'); } else { console.log(chalk.bold.red(`Invalid weight specified. Please use a number (in bytes).`)); - return; + process.exit(-1); } } if (key.length === 0) { console.log(chalk.bold.red('No API key specified. You can get one at ' + chalk.underline('https://tinypng.com/developers') + '.')); - return; + process.exit(-1); } else { From faa3a123f1e49df9a5896f8d0cd075c919a55120 Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 17:56:36 +0200 Subject: [PATCH 12/15] Add an argument to set the minimal acceptable compression --- tinypng-cli.js | 53 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/tinypng-cli.js b/tinypng-cli.js index b151948..a43c95b 100644 --- a/tinypng-cli.js +++ b/tinypng-cli.js @@ -38,12 +38,14 @@ if (argv.v || argv.version) { ' -r, --recursive Walk given directory recursively\n' + ' --width Resize an image to a specified width\n' + ' --height Resize an image to a specified height\n' + + ' --minimal Minimal compression we accept (in %)\n' + ' --resize-mode Specify the resize method to use (scale, fit or cover)\n' + ' --if-larger-than Optimize only if width or height is bigger than the provided value (in pixels)\n' + ' --if-bigger-than Optimize only if file weight if bigger than the provided value (in bytes)\n' + ' --clear-cache Clear cache and stop\n' + + ' --bypass-cache Bypass cache and force optimisation\n' + ' -v, --version Show installed version\n' + - ' -V Verbose mode\n' + + ' -V, --verbose Verbose mode\n' + ' -h, --help Show help' ); @@ -59,6 +61,7 @@ if (argv.v || argv.version) { var resize_modes = ['scale', 'fit', 'cover']; var verbose = false; + var bypassCache = false; if (argv.k || argv.key) { key = typeof(argv.k || argv.key) === 'string' ? (argv.k || argv.key).trim() : ''; @@ -85,17 +88,21 @@ if (argv.v || argv.version) { process.exit(-1); } - if (argv.V) { - console.log('. Verbose mode is ON'); - verbose = true; - } + if (argv['bypass-cache']) { + bypassCache = true; + } + + if (argv.V || argv.verbose) { + console.log('. Verbose mode is ON'); + verbose = true; + } if (argv.width) { if (typeof(argv.width) === 'number') { resize.width = argv.width; } else { console.log(chalk.bold.red('Invalid width specified. Please specify a numeric value only.')); - process.exit(-1); + process.exit(-1); } } @@ -104,7 +111,17 @@ if (argv.v || argv.version) { resize.height = argv.height; } else { console.log(chalk.bold.red('Invalid height specified. Please specify a numeric value only.')); - process.exit(-1); + process.exit(-1); + } + } + + if (argv.minimal) { + if (typeof(argv.minimal) === 'number') { + resize.minimal = argv.minimal; + console.log(`. Acceptable minimal efficiency is set to ${resize.minimal}%`); + } else { + console.log(chalk.bold.red('Invalid minimal specified. Please specify a numeric value only.')); + process.exit(-1); } } @@ -117,7 +134,7 @@ if (argv.v || argv.version) { } } else { console.log(chalk.bold.red(`Invalid resize mode specified. Valid modes are: ${resize_modes.join(', ')}`)); - process.exit(-1); + process.exit(-1); } } @@ -127,7 +144,7 @@ if (argv.v || argv.version) { console.log('. Image(s) should be larger than ' + resize.maxSize + ' pixels (width or height) to be processed\n'); } else { console.log(chalk.bold.red(`Invalid size specified. Please use a number (in pixels).`)); - process.exit(-1); + process.exit(-1); } } @@ -137,14 +154,14 @@ if (argv.v || argv.version) { console.log('. Files should be bigger than ' + pretty(resize.maxWeight) + ' to be processed\n'); } else { console.log(chalk.bold.red(`Invalid weight specified. Please use a number (in bytes).`)); - process.exit(-1); + process.exit(-1); } } if (key.length === 0) { console.log(chalk.bold.red('No API key specified. You can get one at ' + chalk.underline('https://tinypng.com/developers') + '.')); - process.exit(-1); + process.exit(-1); } else { @@ -202,7 +219,7 @@ if (argv.v || argv.version) { } var hash = md5(file + JSON.stringify(resize)); - if (fs.existsSync(`${cacheDirectory}/${hash}`)) { + if (fs.existsSync(`${cacheDirectory}/${hash}`) && bypassCache === false) { if (verbose) console.log(chalk.red(`\u0021 File already packed: ${file}`)); push = false; } @@ -229,9 +246,15 @@ if (argv.v || argv.version) { optimizedCounter = response.headers['compression-count']; - if (body.output.size < body.input.size) { + var efficiency = Math.round(100 - 100 / body.input.size * body.output.size); + var save = true; + if (efficiency < resize.minimal) save = false; + + if (body.output.size < body.input.size && save === true) { - console.log(chalk.green('\u2714 Panda just saved you ' + chalk.bold(pretty(body.input.size - body.output.size) + ' (' + Math.round(100 - 100 / body.input.size * body.output.size) + '%)') + ' for `' + file + '`')); + console.log(chalk.green('\u2714 Panda just saved you ' + + chalk.bold(pretty(body.input.size - body.output.size) + ` (${efficiency}%)`) + + ` for ${file}`)); fs.writeFile(cacheDirectory + hash, hash, (error) => { if (error) { @@ -258,7 +281,7 @@ if (argv.v || argv.version) { } } else { - console.log(chalk.yellow(`\u2718 Couldn’t compress ${file} any further`)); + if (verbose) console.log(chalk.yellow('\u2718 Couldn’t compress ' + chalk.bold(file) + ' any further')); } From 52d7b7962eeb0140086065061ef955b394aeeea5 Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 17:57:09 +0200 Subject: [PATCH 13/15] bump version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c514898..c91c6ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tinypng-cli", - "version": "0.0.7", + "version": "0.0.9", "description": "Handy command line tool for shrinking PNG images using the TinyPNG API", "homepage": "https://github.com/websperts/tinypng-cli", "author": "websperts ", From 10145863f0916e6752481f8c9c9cacd6cf982572 Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 18:19:10 +0200 Subject: [PATCH 14/15] Update readme file --- README.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ec4f0a..4bc2d01 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ To shrink all PNG images within a specific directory (`assets/img` in this examp tinypng assets/img -You may also provide multiple directories. +You may also provide multiple directories or a wildcard at the end to filter some files - tinypng assets/img1 assets/img2 + tinypng assets/img1 assets/img2 assets/imagesBeginningWithSomething* To shrink a single PNG image (`assets/img/demo.png` in this example), you may run the following command. @@ -47,10 +47,39 @@ To resize an image, use the `--width` and/or `--height` flag. tinypng assets/img/demo.png --height 123 tinypng assets/img/demo.png --width 123 --height 123 +You may need to resize some large files only, you know, these ones provided by your customers ;) + + tinypng --if-larger-than 1600 assets/img + tinypng --if-bigger-than 1572864 assets/img + +To avoid the same files to be processed multiple times, a cache is created in the $HOME/.tinypng-cache/ directory. + +You can bypass it: + + tinypng --bypass-cache assets/img + +Or clear it: + + tinypng --clear-cache + +And maybe you need to print all the results on screen, in that case juste activate the verbose mode + + tinypng assets/img/demo.png --width 123 --verbose + That's it. Pretty easy, huh? ## Changelog +* 0.0.9 + * Optimize only if file weight if bigger than the provided value (in bytes) + * Optimize only if width or height is bigger than the provided value (in pixels) + * Halt script on invalid argument + * Use a cache to not optimize the same file twice + * Verbose mode + * Add an argument to set the minimal acceptable compression (in %) + * Update README +* 0.0.8 + * Add support for different resize methods (by tomatolicious) * 0.0.7 * Implement support for uppercase file extensions * 0.0.6 @@ -88,3 +117,4 @@ See LICENSE for more info. - [@jblok](https://github.com/jblok) - [@tomatolicious](https://github.com/tomatolicious) - [@kolya182](https://github.com/kolya182) +- [@zefranck](https://github.com/zefranck) From 572e7c758bff5107b38eb8c035560ff967d49673 Mon Sep 17 00:00:00 2001 From: Franck Date: Sun, 29 Apr 2018 18:24:31 +0200 Subject: [PATCH 15/15] Update readme file --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4bc2d01..da46dc8 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ That's it. Pretty easy, huh? * Use a cache to not optimize the same file twice * Verbose mode * Add an argument to set the minimal acceptable compression (in %) + * Returns the number of calls for the current month (value is sent by the tinypng service) * Update README * 0.0.8 * Add support for different resize methods (by tomatolicious)