├── index.js ├── package.json ├── readme.md └── test.js /index.js: -------------------------------------------------------------------------------- 1 | var Transform = require('stream').Transform 2 | var inherits = require('inherits') 3 | var debug = require('debug')('diffs-to-string') 4 | 5 | module.exports = differ 6 | module.exports.stream = streamIt 7 | 8 | // helpers 9 | function addedValue (key, value) { 10 | debug('added value', key, value) 11 | return " + " + key + ': ' + value + '\n' 12 | } 13 | 14 | function deletedValue (key, value) { 15 | debug('deleted value', key, value) 16 | return " - " + key + ': ' + value + '\n' 17 | } 18 | 19 | function changedValue (key, value1, value2) { 20 | debug('changed value', key, value1, value2) 21 | return " ? " + key + ': ' + value1 + ' -> ' + value2 + '\n' 22 | } 23 | 24 | function unchangedValue (key, value) { 25 | debug('unchanged value', key, value) 26 | return " " + key + ': ' + value + '\n' 27 | } 28 | 29 | function concatRow (row, operation) { 30 | debug('contactRow', row, operation) 31 | var visual = '' 32 | for (var key in row) { 33 | if (row.hasOwnProperty(key)) { 34 | visual += operation(key, row[key]) 35 | } 36 | } 37 | return visual 38 | } 39 | 40 | // the differ! 41 | function differ (diffs, opts) { 42 | if (!opts) opts = {} 43 | if (!diffs[0] || !diffs[0].length) diffs = [diffs] 44 | // takes a diff stream to new heights 45 | if (!opts.getRowValue) opts.getRowValue = function (i) { return i } 46 | if (!opts.getRowHeader) opts.getRowHeader = function (row, i) { 47 | return 'row ' + (i + 1) + '\n' 48 | } 49 | 50 | debug('diffs', diffs) 51 | var visual = '' 52 | 53 | for (var i = 0; i < diffs.length; i++) { 54 | var row = diffs[i] 55 | debug('row', row) 56 | visual += opts.getRowHeader(row, i) 57 | 58 | debug('get row value', opts.getRowValue) 59 | var left = row && row[0] && opts.getRowValue(row[0]) 60 | var right = row && row[1] && opts.getRowValue(row[1]) 61 | 62 | debug('left', left) 63 | debug('right', right) 64 | 65 | if (!left) visual += concatRow(right, addedValue) 66 | else if (!right) visual += concatRow(left, deletedValue) 67 | else { 68 | var allKeys = getAllKeys(left, right) 69 | debug('allKeys', allKeys) 70 | for (var key in allKeys) { 71 | var leftValue = left[key] 72 | var rightValue = right[key] 73 | debug('leftValue', leftValue) 74 | debug('rightValue', rightValue) 75 | if (!leftValue && rightValue) { 76 | visual += addedValue(key, rightValue) 77 | } 78 | else if (!rightValue && leftValue) { 79 | visual += deletedValue(key, leftValue) 80 | } 81 | else if (leftValue !== rightValue) { 82 | visual += changedValue(key, leftValue, rightValue) 83 | } 84 | else{ 85 | visual += unchangedValue(key, leftValue) 86 | } 87 | } 88 | } 89 | } 90 | return visual 91 | } 92 | 93 | 94 | function getAllKeys (left, right) { 95 | var keys = {} 96 | 97 | function addKeys(row) { 98 | for (var key in row) { 99 | if (row.hasOwnProperty(key)) { 100 | keys[key] = 1 101 | } 102 | } 103 | } 104 | 105 | addKeys(left) 106 | addKeys(right) 107 | 108 | return keys 109 | } 110 | 111 | // require('diffs-to-string').stream: 112 | inherits(streamIt, Transform) 113 | function streamIt (opts) { 114 | if (!(this instanceof streamIt)) return new streamIt(opts) 115 | if (!opts) opts = {} 116 | Transform.call(this, {objectMode: true}) 117 | this.destroyed = false 118 | this.opts = opts 119 | } 120 | 121 | streamIt.prototype._transform = function (data, enc, next) { 122 | var self = this 123 | debug('_transform', data) 124 | visual = differ(data, self.opts) 125 | next(null, visual) 126 | } 127 | 128 | streamIt.prototype.destroy = function (err) { 129 | if (this.destroyed) return 130 | this.destroyed = true 131 | this.err = err 132 | this.end() 133 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffs-to-string", 3 | "version": "4.1.0", 4 | "description": "Turns an array of diffs into a string. You can generate a changes stream by using [sorted-diff-stream](github.com/maxogden/sorted-diff-stream).", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/karissa/diffs-to-string.git" 12 | }, 13 | "keywords": [ 14 | "diff", 15 | "string", 16 | "console", 17 | "print", 18 | "tabular", 19 | "diffing" 20 | ], 21 | "author": "Karissa McKelvey (http://karissamck.com/)", 22 | "license": "BSD", 23 | "bugs": { 24 | "url": "https://github.com/karissa/diffs-to-string/issues" 25 | }, 26 | "homepage": "https://github.com/karissa/diffs-to-string", 27 | "dependencies": { 28 | "debug": "^2.2.0", 29 | "inherits": "^2.0.1" 30 | }, 31 | "devDependencies": { 32 | "byte-stream": "^2.1.0", 33 | "from2": "^1.3.0", 34 | "tape": "^4.0.0", 35 | "through2": "^0.6.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # diffs-to-string 2 | 3 | Turns an array of diffs into a string. You can generate a changes stream by using [sorted-diff-stream](github.com/maxogden/sorted-diff-stream). 4 | 5 | [![NPM](https://nodei.co/npm/diffs-to-string.png)](https://nodei.co/npm/diffs-to-string/) 6 | 7 | ### basic example 8 | ```js 9 | var diffs2string = require('diffs-to-string') 10 | 11 | var changes = [ 12 | [ { country: 'germany', capital: null }, 13 | { country: 'germany', code: 'de', capital: 'berlin' } ], 14 | [ { country: 'ireland', capital: 'dublin' }, 15 | { country: 'ireland', code: 'ie', capital: 'dublin' } ], 16 | [ { country: 'france', capital: 'paris' }, 17 | { country: null, code: 'fr', capital: 'paris'} ], 18 | [ { country: 'spain', capital: 'madrid' }, 19 | { country: 'spain', code: 'es', capital: 'barcelona' } ] 20 | ] 21 | 22 | var visual = diffs2string(changes) 23 | console.log(visual) 24 | ``` 25 | 26 | Outputs: 27 | 28 | ``` 29 | row 1 30 | country: germany 31 | + capital: berlin 32 | + code: de 33 | row 2 34 | country: ireland 35 | capital: dublin 36 | + code: ie 37 | row 3 38 | - country: france 39 | capital: paris 40 | + code: fr 41 | row 4 42 | country: spain 43 | ? capital: madrid -> barcelona 44 | + code: es 45 | ``` 46 | 47 | 48 | ### with streams 49 | ```js 50 | var diffs2string = require('diffs-to-string').stream 51 | var diffStream = from.obj(changes) 52 | 53 | diffStream.pipe(diffs2string()) 54 | ``` 55 | 56 | ### custom row path and row header 57 | 58 | ```js 59 | function getRowValue (row) { 60 | return row.value 61 | } 62 | 63 | function getRowHeader (diff) { 64 | return 'this is row ' + diff['some-value'] + '\n' 65 | } 66 | 67 | var opts = { 68 | getRowValue: getRowValue, 69 | getRowHeader: getRowHeader 70 | } 71 | 72 | diffStream.pipe(diffs2string.stream(opts)) 73 | var visual = diffs2string(changes, opts) 74 | ``` 75 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var through = require('through2') 3 | var from = require('from2') 4 | var batcher = require('byte-stream') 5 | var diffs2string = require('./') 6 | 7 | var changes = [ 8 | [ { country: 'germany', capital: null }, 9 | { country: 'germany', code: 'de', capital: 'berlin' } ], 10 | [ { country: 'ireland', capital: 'dublin' }, 11 | { country: 'ireland', code: 'ie', capital: 'dublin' } ], 12 | [ { country: 'france', capital: 'paris' }, 13 | { country: null, code: 'fr', capital: 'paris'} ], 14 | [ { country: 'spain', capital: 'madrid' }, 15 | { country: 'spain', code: 'es', capital: 'barcelona' }], 16 | [ null, 17 | { country: 'united states', code: 'us', capital: 'washington'}] 18 | ] 19 | var visual = diffs2string(changes) 20 | var lines = visual.split('\n') 21 | 22 | test('add prints correctly', function (t) { 23 | t.equals(lines[0], "row 1") 24 | t.equals(lines[1], " country: germany") 25 | t.equals(lines[2], " + capital: berlin") 26 | t.equals(lines[3], " + code: de") 27 | t.equals(lines[4], "row 2") 28 | t.end() 29 | }) 30 | 31 | test('add row prints correctly', function (t) { 32 | t.equals(lines[19], ' + capital: washington') 33 | t.end() 34 | }) 35 | 36 | test('custom header row prints correctly', function (t) { 37 | var opts = { 38 | getRowHeader: function (diff, i) { 39 | var arow = diff && diff[0] || diff[1] 40 | return 'haha im a row header ' + arow['country'] + '\n' 41 | } 42 | } 43 | var visual = diffs2string(changes, opts) 44 | var lines = visual.split('\n') 45 | t.equals(lines[0], 'haha im a row header germany') 46 | t.end() 47 | }) 48 | 49 | test('change prints correctly', function (t) { 50 | t.equals(lines[14], " ? capital: madrid -> barcelona") 51 | t.end() 52 | }) 53 | 54 | test('delete prints correctly', function (t) { 55 | t.equals(lines[9], " - country: france") 56 | t.end() 57 | }) 58 | 59 | test('stream prints correctly with a batcher', function (t) { 60 | var diffStream = from.obj(changes).pipe(batcher(1)) 61 | diffStream.pipe(diffs2string.stream()).pipe(through.obj(function (data, enc, next) { 62 | var these_lines = data.split('\n') 63 | t.equals(these_lines[0], "row 1") 64 | t.equals(these_lines[1], " country: germany") 65 | t.equals(these_lines[2], " + capital: berlin") 66 | t.equals(these_lines[3], " + code: de") 67 | t.equals() 68 | })) 69 | 70 | t.end() 71 | }) 72 | 73 | test('stream prints correctly with uptype to array of ararys', function (t) { 74 | var diffStream = from.obj(changes) 75 | diffStream.pipe(diffs2string.stream()).pipe(through.obj(function (data, enc, next) { 76 | var these_lines = data.split('\n') 77 | t.equals(these_lines[0], "row 1") 78 | t.equals(these_lines[1], " country: germany") 79 | t.equals(these_lines[2], " + capital: berlin") 80 | t.equals(these_lines[3], " + code: de") 81 | t.equals() 82 | })) 83 | 84 | t.end() 85 | }) --------------------------------------------------------------------------------