├── .gitignore ├── package.json ├── readme.md └── diffr.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffr", 3 | "version": "1.1.0", 4 | "description": "CLI diff tool for files, text, and JSON with human readable output.", 5 | "main": "diffr.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "bin": { 10 | "diffr": "./diffr.js" 11 | }, 12 | "keywords": [ 13 | "diff", 14 | "tool", 15 | "json", 16 | "file", 17 | "binary", 18 | "string", 19 | "text", 20 | "unix", 21 | "cli", 22 | "diffing", 23 | "differ", 24 | "request", 25 | "response", 26 | "curl", 27 | "http" 28 | ], 29 | "author": "", 30 | "license": "MIT", 31 | "dependencies": { 32 | "cli-table": "^0.3.1", 33 | "colors": "^1.0.3", 34 | "commander": "^2.7.1", 35 | "line-diff": "^1.0.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Diffr 2 | 3 | CLI diff tool for files, text, and JSON written in Node.js 4 | 5 | ## Install 6 | 7 | ```js 8 | npm install diffr -g 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```bash 14 | 15 | Usage: diffr [options] 16 | 17 | Options: 18 | 19 | -h, --help output usage information 20 | -V, --version output the version number 21 | -t, --text [value] Add text string to be diff'd 22 | -j, --json [string] Add json string to be diff'd 23 | -f, --file Add file to be diff'd 24 | -c, --column Columnized output 25 | 26 | ``` 27 | 28 | ## Examples 29 | 30 | 31 | **Text** 32 | 33 | ```bash 34 | $ diffr -t "hello\nworld" -t "hello\nnijiko" 35 | ``` 36 | 37 | **JSON** 38 | 39 | ```bash 40 | $ diffr -j "{\"hello\":\"world\"}" -j "{\"hello\":\"nijiko\"}" 41 | ``` 42 | 43 | **File** 44 | 45 | ```bash 46 | $ diffr diffr.js package.json 47 | ``` 48 | 49 | **File Descriptors / Streams** 50 | 51 | ```bash 52 | $ diffr <(curl -Is https://httpbin.org/ip 2>&1) <(curl -Is https://httpbin.org 2>&1) 53 | 54 | HTTP/1.1 200 OK 55 | Server: nginx 56 | Date: Wed, 25 Mar 2015 07:10:56 GMT 57 | - Content-Type: text/html; charset=utf-8 58 | + Content-Type: application/json 59 | - Content-Length: 11729 60 | + Content-Length: 32 61 | Connection: keep-alive 62 | Access-Control-Allow-Origin: * 63 | Access-Control-Allow-Credentials: true 64 | 65 | ``` 66 | 67 | **Columnized Output** 68 | 69 | ```bash 70 | ᐅ ./diffr.js -c <(curl -Is https://httpbin.org/ip 2>&1) <(curl -Is https://httpbin.org 2>&1) 71 | 72 | HTTP/1.1 200 OK HTTP/1.1 200 OK 73 | Server: nginx Server: nginx 74 | Date: Wed, 25 Mar 2015 18:29:37 GMT Date: Wed, 25 Mar 2015 18:29:37 GMT 75 | - Content-Type: text/html; charset=utf-8 + Content-Type: application/json 76 | - Content-Length: 11729 + Content-Length: 32 77 | Connection: keep-alive Connection: keep-alive 78 | Access-Control-Allow-Origin: * Access-Control-Allow-Origin: * 79 | Access-Control-Allo 80 | ``` 81 | 82 | ## License 83 | 84 | MIT -------------------------------------------------------------------------------- /diffr.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Globals 4 | require('colors') 5 | 6 | // Locals 7 | var StringDecoder = require('string_decoder').StringDecoder 8 | var Table = require('cli-table') 9 | var program = require('commander') 10 | var diff = require('line-diff') 11 | var pkg = require('./package.json') 12 | var fs = require('fs') 13 | 14 | // Pipes 15 | var descriptors = 0 16 | 17 | // Options 18 | var strings = [] 19 | 20 | // Helpers 21 | function setTrue () { 22 | return true 23 | } 24 | 25 | function push (item, list) { 26 | list.push(item) 27 | return list 28 | } 29 | 30 | function json (item, list) { 31 | list.push(JSON.stringify(JSON.parse(item), null, 2)) 32 | return list 33 | } 34 | 35 | function file (item) { 36 | var str = "" 37 | 38 | descriptors++ 39 | 40 | var readStream = fs.createReadStream(item) 41 | .on('readable', function () { 42 | var decoder = new StringDecoder('utf8') 43 | var chunk 44 | 45 | while (null !== (chunk = readStream.read())) { 46 | str += decoder.write(chunk) 47 | } 48 | }) 49 | .on('end', function () { 50 | push(str, strings) 51 | descriptors-- 52 | 53 | if (descriptors <= 0) 54 | return output() 55 | }) 56 | } 57 | 58 | program 59 | .version(pkg.version) 60 | .usage ('[options] ') 61 | .option ('-t, --text [value]', 'Add text string to be diff\'d', push, strings) 62 | .option ('-j, --json [string]', 'Add json string to be diff\'d', json, strings) 63 | .option ('-f, --file ', 'Add file to be diff\'d', file) 64 | .option ('-c, --column', 'Columnized output', setTrue, false) 65 | .parse (process.argv) 66 | 67 | for (var index in program.args) 68 | file(program.args[index]) 69 | 70 | diff.prototype.toString = function () { 71 | var self = this 72 | var str = "\n" 73 | 74 | self.changes.forEach(function (cChange) { 75 | if (!cChange.modified) { 76 | str += " " + cChange._[1].grey + "\n" 77 | } else { 78 | str += " - ".red + cChange._[0].red + "\n" 79 | 80 | if (cChange._[1]) { 81 | str += " + ".green + cChange._[1].green + "\n" 82 | } 83 | } 84 | }) 85 | 86 | return str.replace(/\s+$/,'') 87 | } 88 | 89 | diff.prototype.toTable = function () { 90 | var self = this 91 | var table = new Table({ 92 | chars: { 'top': '' , 'top-mid': '' , 'top-left': '' , 'top-right': '' 93 | , 'bottom': '' , 'bottom-mid': '' , 'bottom-left': '' , 'bottom-right': '' 94 | , 'left': '' , 'left-mid': '' , 'mid': '' , 'mid-mid': '' 95 | , 'right': '' , 'right-mid': '' , 'middle': ' ' } 96 | }) 97 | 98 | table.push([' ', ' ']) 99 | 100 | function trim (str) { 101 | return str.replace(/(\r|\r\n|\t|\s)+$/g, '') 102 | } 103 | 104 | self.changes.forEach(function (cChange) { 105 | if (!cChange.modified) { 106 | table.push([ 107 | trim(cChange._[1]).grey, 108 | trim(cChange._[1]).grey 109 | ]) 110 | } else { 111 | var entry = [] 112 | 113 | entry[0] = trim(cChange._[0]).red 114 | 115 | if (cChange._[1]) { 116 | entry[1] = trim(cChange._[1]).green 117 | } 118 | 119 | table.push(entry) 120 | } 121 | }) 122 | 123 | while (table[table.length-1][0] === "\u001b[90m\u001b[39m") { 124 | table.pop() 125 | } 126 | 127 | return table 128 | } 129 | 130 | function output () { 131 | var difference = diff(strings[0], strings[1]) 132 | 133 | if (program.column) { 134 | difference = difference.toTable() 135 | } 136 | 137 | console.log(difference.toString()) 138 | } 139 | 140 | if (!descriptors) { 141 | if (!strings.length) { 142 | program.help() 143 | } 144 | 145 | output() 146 | } --------------------------------------------------------------------------------