From db4b760d9eabf0c617943552fb12bb575dba9943 Mon Sep 17 00:00:00 2001 From: Adam Reis Date: Mon, 13 Feb 2017 18:29:46 +1300 Subject: [PATCH] CLI functionality --- README.md | 12 +- bin/cli.js | 49 +++++ lib/replace-in-file.js | 61 ++++-- {test => lib}/replace-in-file.spec.js | 270 +++++++++++++++----------- package.json | 9 +- test/helpers/setup.js | 8 +- yarn.lock | 203 ++++++++++++++++++- 7 files changed, 459 insertions(+), 153 deletions(-) create mode 100755 bin/cli.js rename {test => lib}/replace-in-file.spec.js (75%) diff --git a/README.md b/README.md index 4d5f3be..e495d13 100644 --- a/README.md +++ b/README.md @@ -37,16 +37,16 @@ const options = { ], //Replacement to make (string or regex) - replace: /foo/g, - with: 'bar', + from: /foo/g, + to: 'bar', //Multiple replacements with the same string (replaced sequentially) - replace: [/foo/g, /baz/g], - with: 'bar', + from: [/foo/g, /baz/g], + to: 'bar', //Multiple replacements with different strings (replaced sequentially) - replace: [/foo/g, /baz/g], - with: ['bar', 'bax'], + from: [/foo/g, /baz/g], + to: ['bar', 'bax'], //Specify if empty/invalid file paths are allowed (defaults to false) //If set to true these paths will fail silently and no error will be thrown. diff --git a/bin/cli.js b/bin/cli.js new file mode 100755 index 0000000..97abefd --- /dev/null +++ b/bin/cli.js @@ -0,0 +1,49 @@ +#!/usr/bin/env node +'use strict'; + +/** + * Dependencies + */ +const chalk = require('chalk'); +const argv = require('yargs').argv; +const replace = require('../lib/replace-in-file'); + +//Verify arguments +if (argv._.length < 3) { + console.error(chalk.red('Replace in file needs at least 3 arguments')); + process.exit(1); +} + +//Collect main arguments +const from = argv._.shift(); +const to = argv._.shift(); +const files = argv._.shift().split(','); + +//Log +console.log(`Replacing '${from}' with '${to}' in ${files}`); + +//Create options +const options = {files, from, to}; +if (typeof argv.encoding !== 'undefined') { + options.encoding = argv.encoding; +} +if (typeof argv.allowEmptyPaths !== 'undefined') { + options.allowEmptyPaths = argv.allowEmptyPaths; +} + +//Replace +try { + const changedFiles = replace.sync(options); + if (changedFiles.length > 0) { + console.log(chalk.green(changedFiles.length, 'file(s) were changed')); + if (argv.verbose) { + changedFiles.forEach(file => console.log(chalk.grey('-', file))); + } + } + else { + console.log(chalk.yellow('No files were changed')); + } +} +catch (error) { + console.error(error); +} diff --git a/lib/replace-in-file.js b/lib/replace-in-file.js index 64f47d2..bf2030d 100644 --- a/lib/replace-in-file.js +++ b/lib/replace-in-file.js @@ -5,6 +5,7 @@ */ const fs = require('fs'); const glob = require('glob'); +const chalk = require('chalk'); /** * Defaults @@ -19,24 +20,42 @@ const defaults = { */ function parseConfig(config) { - //Validate input + //Validate config if (typeof config !== 'object' || config === null) { throw new Error('Must specify configuration object'); } + + //Backwards compatibilility + if (typeof config.replace !== 'undefined' && + typeof config.from === 'undefined') { + console.log( + chalk.yellow('Option `replace` is deprecated. Use `from` instead.') + ); + config.from = config.replace; + } + if (typeof config.with !== 'undefined' && + typeof config.to === 'undefined') { + console.log( + chalk.yellow('Option `with` is deprecated. Use `to` instead.') + ); + config.to = config.with; + } + + //Validate values if (typeof config.files === 'undefined') { throw new Error('Must specify file or files'); } - if (typeof config.replace === 'undefined') { + if (typeof config.from === 'undefined') { throw new Error('Must specify string or regex to replace'); } - if (typeof config.with === 'undefined') { + if (typeof config.to === 'undefined') { throw new Error('Must specify a replacement (can be blank string)'); } - //Use different naming internally as we can't use `with` as a variable name - config.find = config.replace; - config.replace = config.with; - delete config.with; + //Use default encoding if invalid + if (typeof config.encoding !== 'string' || config.encoding === '') { + config.encoding = 'utf-8'; + } //Merge config with defaults return Object.assign({}, defaults, config); @@ -58,21 +77,21 @@ function getReplacement(replace, isArray, i) { /** * Helper to make replacements */ -function makeReplacements(contents, find, replace) { +function makeReplacements(contents, from, to) { //Turn into array - if (!Array.isArray(find)) { - find = [find]; + if (!Array.isArray(from)) { + from = [from]; } //Check if replace value is an array - const isReplaceArray = Array.isArray(replace); + const isArray = Array.isArray(to); //Make replacements - find.forEach((item, i) => { + from.forEach((item, i) => { //Get replacement value - const replacement = getReplacement(replace, isReplaceArray, i); + const replacement = getReplacement(to, isArray, i); if (replacement === null) { return; } @@ -88,13 +107,13 @@ function makeReplacements(contents, find, replace) { /** * Helper to replace in a single file (sync) */ -function replaceSync(file, find, replace, enc) { +function replaceSync(file, from, to, enc) { //Read contents const contents = fs.readFileSync(file, enc); //Replace contents and check if anything changed - const newContents = makeReplacements(contents, find, replace); + const newContents = makeReplacements(contents, from, to); if (newContents === contents) { return false; } @@ -107,7 +126,7 @@ function replaceSync(file, find, replace, enc) { /** * Helper to replace in a single file (async) */ -function replaceAsync(file, find, replace, enc) { +function replaceAsync(file, from, to, enc) { return new Promise((resolve, reject) => { fs.readFile(file, enc, (error, contents) => { //istanbul ignore if @@ -116,7 +135,7 @@ function replaceAsync(file, find, replace, enc) { } //Replace contents and check if anything changed - let newContents = makeReplacements(contents, find, replace); + let newContents = makeReplacements(contents, from, to); if (newContents === contents) { return resolve({file, hasChanged: false}); } @@ -173,7 +192,7 @@ function replaceInFile(config, cb) { } //Get config and globs - const {files, find, replace, allowEmptyPaths, encoding} = config; + const {files, from, to, allowEmptyPaths, encoding} = config; const globs = Array.isArray(files) ? files : [files]; //Find files @@ -185,7 +204,7 @@ function replaceInFile(config, cb) { //Make replacements .then(files => Promise.all(files.map(file => { - return replaceAsync(file, find, replace, encoding); + return replaceAsync(file, from, to, encoding); }))) //Convert results to array of changed files @@ -223,7 +242,7 @@ replaceInFile.sync = function(config) { config = parseConfig(config); //Get config and globs - const {files, find, replace, encoding} = config; + const {files, from, to, encoding} = config; const globs = Array.isArray(files) ? files : [files]; const changedFiles = []; @@ -232,7 +251,7 @@ replaceInFile.sync = function(config) { glob .sync(pattern, {nodir: true}) .forEach(file => { - if (replaceSync(file, find, replace, encoding)) { + if (replaceSync(file, from, to, encoding)) { changedFiles.push(file); } }); diff --git a/test/replace-in-file.spec.js b/lib/replace-in-file.spec.js similarity index 75% rename from test/replace-in-file.spec.js rename to lib/replace-in-file.spec.js index 5acb5b3..a4959e2 100644 --- a/test/replace-in-file.spec.js +++ b/lib/replace-in-file.spec.js @@ -47,32 +47,32 @@ describe('Replace in file', () => { return expect(replace(42)).to.eventually.be.rejectedWith(Error); }); - it('should throw an error when no files defined', () => { + it('should throw an error when no `files` defined', () => { return expect(replace({ - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', })).to.eventually.be.rejectedWith(Error); }); - it('should throw an error when no replace defined', () => { + it('should throw an error when no `from` defined', () => { return expect(replace({ files: 'test1', - with: 'b', + to: 'b', })).to.eventually.be.rejectedWith(Error); }); - it('should throw an error when no with defined', () => { + it('should throw an error when no `to` defined', () => { return expect(replace({ files: 'test1', - replace: /re\splace/g, + from: /re\splace/g, })).to.eventually.be.rejectedWith(Error); }); it('should replace contents in a single file with regex', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -85,8 +85,8 @@ describe('Replace in file', () => { it('should replace contents with a string replacement', done => { replace({ files: 'test1', - replace: 're place', - with: 'b', + from: 're place', + to: 'b', }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); expect(test1).to.equal('a b c'); @@ -97,8 +97,8 @@ describe('Replace in file', () => { it('should replace contents in a an array of files', done => { replace({ files: ['test1', 'test2'], - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -111,8 +111,8 @@ describe('Replace in file', () => { it('should expand globs', done => { replace({ files: 'test*', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -125,16 +125,16 @@ describe('Replace in file', () => { it('should fulfill the promise on success', () => { return replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).should.be.fulfilled; }); it('should reject the promise with an error on failure', () => { return expect(replace({ files: 'nope', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', })).to.eventually.be.rejectedWith(Error); }); @@ -142,16 +142,16 @@ describe('Replace in file', () => { return replace({ files: 'nope', allowEmptyPaths: true, - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).should.be.fulfilled; }); it('should return a changed files array', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).then(changedFiles => { expect(changedFiles).to.be.instanceof(Array); done(); @@ -161,8 +161,8 @@ describe('Replace in file', () => { it('should return in files if something was replaced', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).then(changedFiles => { expect(changedFiles).to.have.length(1); expect(changedFiles[0]).to.equal('test1'); @@ -173,8 +173,8 @@ describe('Replace in file', () => { it('should not return in files if nothing replaced', done => { replace({ files: 'test1', - replace: 'nope', - with: 'b', + from: 'nope', + to: 'b', }).then(changedFiles => { expect(changedFiles).to.have.length(0); done(); @@ -184,8 +184,8 @@ describe('Replace in file', () => { it('should return changed files for multiple files', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }).then(changedFiles => { expect(changedFiles).to.have.length(2); expect(changedFiles).to.contain('test1'); @@ -197,8 +197,8 @@ describe('Replace in file', () => { it('should make multiple replacements with the same string', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: 'b', + from: [/re/g, /place/g], + to: 'b', }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -211,8 +211,8 @@ describe('Replace in file', () => { it('should make multiple replacements with different strings', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: ['b', 'e'], + from: [/re/g, /place/g], + to: ['b', 'e'], }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -225,8 +225,8 @@ describe('Replace in file', () => { it('should not replace with missing replacement values', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: ['b'], + from: [/re/g, /place/g], + to: ['b'], }).then(() => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -256,30 +256,30 @@ describe('Replace in file', () => { }); }); - it('should throw an error when no files defined', done => { + it('should throw an error when no `files` defined', done => { replace({ - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error) => { expect(error).to.be.instanceof(Error); done(); }); }); - it('should throw an error when no replace defined', done => { + it('should throw an error when no `from` defined', done => { replace({ files: 'test1', - with: 'b', + to: 'b', }, (error) => { expect(error).to.be.instanceof(Error); done(); }); }); - it('should throw an error when no with defined', done => { + it('should throw an error when no `to` defined', done => { replace({ files: 'test1', - replace: /re\splace/g, + from: /re\splace/g, }, (error) => { expect(error).to.be.instanceof(Error); done(); @@ -289,8 +289,8 @@ describe('Replace in file', () => { it('should replace contents in a single file with regex', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, () => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -303,10 +303,10 @@ describe('Replace in file', () => { it('should replace contents with a string replacement', done => { replace({ files: 'test1', - replace: 're place', - with: 'b', + from: 're place', + to: 'b', }, () => { - let test1 = fs.readFileSync('test1', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); expect(test1).to.equal('a b c'); done(); }); @@ -315,11 +315,11 @@ describe('Replace in file', () => { it('should replace contents in a an array of files', done => { replace({ files: ['test1', 'test2'], - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, () => { - let test1 = fs.readFileSync('test1', 'utf8'); - let test2 = fs.readFileSync('test2', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); + const test2 = fs.readFileSync('test2', 'utf8'); expect(test1).to.equal('a b c'); expect(test2).to.equal('a b c'); done(); @@ -329,11 +329,11 @@ describe('Replace in file', () => { it('should expand globs', done => { replace({ files: 'test*', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, () => { - let test1 = fs.readFileSync('test1', 'utf8'); - let test2 = fs.readFileSync('test2', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); + const test2 = fs.readFileSync('test2', 'utf8'); expect(test1).to.equal('a b c'); expect(test2).to.equal('a b c'); done(); @@ -343,8 +343,8 @@ describe('Replace in file', () => { it('should not return an error on success', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error) => { expect(error).to.equal(null); done(); @@ -354,8 +354,8 @@ describe('Replace in file', () => { it('should return an error on failure', done => { replace({ files: 'nope', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error) => { expect(error).to.be.instanceof(Error); done(); @@ -366,8 +366,8 @@ describe('Replace in file', () => { replace({ files: 'nope', allowEmptyPaths: true, - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error) => { expect(error).to.equal(null); done(); @@ -377,8 +377,8 @@ describe('Replace in file', () => { it('should return a changed files array', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error, changedFiles) => { expect(changedFiles).to.be.instanceof(Array); done(); @@ -388,8 +388,8 @@ describe('Replace in file', () => { it('should return in files if something was replaced', done => { replace({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error, changedFiles) => { expect(changedFiles).to.have.length(1); expect(changedFiles[0]).to.equal('test1'); @@ -400,8 +400,8 @@ describe('Replace in file', () => { it('should not return in files if nothing replaced', done => { replace({ files: 'test1', - replace: 'nope', - with: 'b', + from: 'nope', + to: 'b', }, (error, changedFiles) => { expect(changedFiles).to.have.length(0); done(); @@ -411,8 +411,8 @@ describe('Replace in file', () => { it('should return changed files for multiple files', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }, (error, changedFiles) => { expect(changedFiles).to.have.length(2); expect(changedFiles).to.contain('test1'); @@ -424,8 +424,8 @@ describe('Replace in file', () => { it('should make multiple replacements with the same string', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: 'b', + from: [/re/g, /place/g], + to: 'b', }, () => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -438,8 +438,8 @@ describe('Replace in file', () => { it('should make multiple replacements with different strings', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: ['b', 'e'], + from: [/re/g, /place/g], + to: ['b', 'e'], }, () => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -452,8 +452,8 @@ describe('Replace in file', () => { it('should not replace with missing replacement values', done => { replace({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: ['b'], + from: [/re/g, /place/g], + to: ['b'], }, () => { const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -481,41 +481,81 @@ describe('Replace in file', () => { }).to.throw(Error); }); - it('should throw an error when no files defined', () => { + it('should throw an error when no `files` defined', () => { expect(function() { replace.sync({ - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); }).to.throw(Error); }); - it('should throw an error when no replace defined', () => { + it('should throw an error when no `from` defined', () => { expect(function() { replace.sync({ files: 'test1', - with: 'b', + to: 'b', }); }).to.throw(Error); }); - it('should throw an error when no with defined', () => { + it('should throw an error when no `to` defined', () => { + expect(function() { + replace.sync({ + files: 'test1', + from: /re\splace/g, + }); + }).to.throw(Error); + }); + + it('should support `replace` and `with` params for now', () => { expect(function() { replace.sync({ files: 'test1', replace: /re\splace/g, + with: 'b', }); - }).to.throw(Error); + }).to.not.throw(Error); + }); + + it('should support the encoding parameter', () => { + expect(function() { + replace.sync({ + files: 'test1', + from: /re\splace/g, + to: 'b', + encoding: 'utf-8', + }); + }).to.not.throw(Error); + }); + + it('should fall back to utf-8 encoding with invalid configuration', () => { + expect(function() { + replace.sync({ + files: 'test1', + from: /re\splace/g, + to: 'b', + encoding: '', + }); + }).to.not.throw(Error); + expect(function() { + replace.sync({ + files: 'test1', + from: /re\splace/g, + to: 'b', + encoding: null, + }); + }).to.not.throw(Error); }); it('should replace contents in a single file with regex', function() { replace.sync({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); - let test1 = fs.readFileSync('test1', 'utf8'); - let test2 = fs.readFileSync('test2', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); + const test2 = fs.readFileSync('test2', 'utf8'); expect(test1).to.equal('a b c'); expect(test2).to.equal(testData); }); @@ -523,21 +563,21 @@ describe('Replace in file', () => { it('should replace contents with a string replacement', function() { replace.sync({ files: 'test1', - replace: 're place', - with: 'b', + from: 're place', + to: 'b', }); - let test1 = fs.readFileSync('test1', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); expect(test1).to.equal('a b c'); }); it('should replace contents in a an array of files', function() { replace.sync({ files: ['test1', 'test2'], - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); - let test1 = fs.readFileSync('test1', 'utf8'); - let test2 = fs.readFileSync('test2', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); + const test2 = fs.readFileSync('test2', 'utf8'); expect(test1).to.equal('a b c'); expect(test2).to.equal('a b c'); }); @@ -545,48 +585,48 @@ describe('Replace in file', () => { it('should expand globs', function() { replace.sync({ files: 'test*', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); - let test1 = fs.readFileSync('test1', 'utf8'); - let test2 = fs.readFileSync('test2', 'utf8'); + const test1 = fs.readFileSync('test1', 'utf8'); + const test2 = fs.readFileSync('test2', 'utf8'); expect(test1).to.equal('a b c'); expect(test2).to.equal('a b c'); }); it('should return a changed files array', function() { - let changedFiles = replace.sync({ + const changedFiles = replace.sync({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); expect(changedFiles).to.be.instanceof(Array); }); it('should return in changed files if something was replaced', function() { - let changedFiles = replace.sync({ + const changedFiles = replace.sync({ files: 'test1', - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); expect(changedFiles).to.have.length(1); expect(changedFiles[0]).to.equal('test1'); }); it('should not return in changed files if nothing replaced', function() { - let changedFiles = replace.sync({ + const changedFiles = replace.sync({ files: 'test1', - replace: 'nope', - with: 'b', + from: 'nope', + to: 'b', }); expect(changedFiles).to.have.length(0); }); it('should return changed files for multiple files', function() { - let changedFiles = replace.sync({ + const changedFiles = replace.sync({ files: ['test1', 'test2', 'test3'], - replace: /re\splace/g, - with: 'b', + from: /re\splace/g, + to: 'b', }); expect(changedFiles).to.have.length(2); expect(changedFiles).to.contain('test1'); @@ -596,8 +636,8 @@ describe('Replace in file', () => { it('should make multiple replacements with the same string', () => { replace.sync({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: 'b', + from: [/re/g, /place/g], + to: 'b', }); const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -608,8 +648,8 @@ describe('Replace in file', () => { it('should make multiple replacements with different strings', () => { replace.sync({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: ['b', 'e'], + from: [/re/g, /place/g], + to: ['b', 'e'], }); const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); @@ -620,8 +660,8 @@ describe('Replace in file', () => { it('should not replace with missing replacement values', () => { replace.sync({ files: ['test1', 'test2', 'test3'], - replace: [/re/g, /place/g], - with: ['b'], + from: [/re/g, /place/g], + to: ['b'], }); const test1 = fs.readFileSync('test1', 'utf8'); const test2 = fs.readFileSync('test2', 'utf8'); diff --git a/package.json b/package.json index 328626e..30e7775 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,10 @@ "file" ], "main": "index.js", + "bin": "./bin/cli.js", "scripts": { "lint": "eslint . --fix", - "istanbul": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha test/**/*.spec.js", + "istanbul": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha lib/**/*.spec.js", "test": "npm run istanbul -s", "postversion": "git push && git push --tags && npm publish", "coverage": "open -a \"Google Chrome\" ./coverage/lcov-report/index.html" @@ -37,12 +38,14 @@ "chai": "^3.5.0", "chai-as-promised": "^6.0.0", "dirty-chai": "^1.2.2", - "eslint": "^3.14.0", + "eslint": "^3.15.0", "istanbul": "^1.0.0-alpha.2", "mocha": "^3.2.0" }, "dependencies": { - "glob": "^7.1.1" + "chalk": "^1.1.3", + "glob": "^7.1.1", + "yargs": "^6.6.0" }, "browserify": { "transform": [ diff --git a/test/helpers/setup.js b/test/helpers/setup.js index d23327a..3795bd6 100644 --- a/test/helpers/setup.js +++ b/test/helpers/setup.js @@ -1,10 +1,10 @@ 'use strict'; //Load dependencies -let Promise = require('bluebird'); -let chai = require('chai'); -let dirtyChai = require('dirty-chai'); -let chaiAsPromised = require('chai-as-promised'); +const Promise = require('bluebird'); +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); //Enable should assertion style for usage with chai-as-promised chai.should(); diff --git a/yarn.lock b/yarn.lock index a6c26d9..0b19bcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -642,6 +642,10 @@ buffer-shims@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" @@ -656,6 +660,10 @@ camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" @@ -732,6 +740,14 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" @@ -808,7 +824,7 @@ debug@2.2.0, debug@^2.1.1, debug@^2.2.0, debug@~2.2.0: dependencies: ms "0.7.1" -decamelize@^1.0.0: +decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -873,6 +889,12 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +error-ex@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" + dependencies: + is-arrayish "^0.2.1" + es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: version "0.10.12" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" @@ -1083,6 +1105,13 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + flat-cache@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.1.tgz#6c837d6225a7de5659323740b36d5361f71691ff" @@ -1170,6 +1199,10 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + getpass@^0.1.1: version "0.1.6" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" @@ -1305,6 +1338,10 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +hosted-git-info@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -1364,6 +1401,14 @@ invariant@^2.2.0: dependencies: loose-envify "^1.0.0" +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -1374,6 +1419,12 @@ is-buffer@^1.0.2: version "1.1.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + is-dotfile@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" @@ -1467,6 +1518,10 @@ is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -1644,6 +1699,12 @@ lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -1651,6 +1712,16 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -1818,6 +1889,15 @@ nopt@3.x, nopt@~3.0.6: dependencies: abbrev "1" +normalize-package-data@^2.3.2: + version "2.3.5" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" @@ -1888,6 +1968,12 @@ os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + os-tmpdir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -1909,6 +1995,18 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1921,6 +2019,14 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -1983,6 +2089,21 @@ rc@~1.1.6: minimist "^1.2.0" strip-json-comments "~1.0.4" +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + "readable-stream@^2.0.0 || ^1.1.13", readable-stream@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" @@ -2109,6 +2230,14 @@ request@^2.79.0: tunnel-agent "~0.4.1" uuid "^3.0.0" +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" @@ -2153,11 +2282,11 @@ rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" -semver@^5.3.0, semver@~5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -set-blocking@~2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -2207,6 +2336,20 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -2226,7 +2369,7 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -string-width@^1.0.1: +string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: @@ -2255,6 +2398,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -2398,12 +2547,23 @@ v8flags@^2.0.10: dependencies: user-home "^1.1.1" +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + verror@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" dependencies: extsprintf "1.0.2" +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + which@^1.1.1: version "1.2.12" resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" @@ -2432,6 +2592,13 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -2446,6 +2613,34 @@ xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yargs-parser@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" + dependencies: + camelcase "^3.0.0" + +yargs@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^4.2.0" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"