pax_global_header00006660000000000000000000000064126416341200014511gustar00rootroot0000000000000052 comment=91493b0f7c49abb660467c0878be58e8b2561053 difflet-1.0.1/000077500000000000000000000000001264163412000131255ustar00rootroot00000000000000difflet-1.0.1/.travis.yml000066400000000000000000000000541264163412000152350ustar00rootroot00000000000000language: node_js node_js: - 0.8 - 0.10 difflet-1.0.1/LICENSE000066400000000000000000000020741264163412000141350ustar00rootroot00000000000000Copyright (C) 2012, 2013 James Halliday Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. difflet-1.0.1/README.markdown000066400000000000000000000045561264163412000156400ustar00rootroot00000000000000difflet ======= Create colorful diffs for javascript objects. example ======= string.js --------- ``` js var difflet = require('difflet'); var s = difflet.compare({ a : 2, c : 5 }, { a : 3, b : 4 }); process.stdout.write(s); ``` output: ![colorful output](http://substack.net/images/screenshots/difflet_string.png) colors.js --------- ``` js var diff = require('difflet')({ indent : 2 }); var prev = { yy : 6, zz : 5, a : [1,2,3], fn : 'beep', c : { x : 7, z : 3 } }; var next = { a : [ 1, 2, "z", /beep/, new Buffer(3) ], fn : function qqq () {}, b : [5,6,7], c : { x : 8, y : 5 } }; diff(prev, next).pipe(process.stdout); ``` output: ![colorful output](http://substack.net/images/screenshots/difflet_colors.png) green for inserts, blue for updates, red for deletes methods ======= var difflet = require('difflet') var diff = difflet(opts={}) --------------------------- Create a difflet from optional options `opts`. With `opts.start(type, stream)` and `opts.stop(type, stream)`, you can write custom handlers for all the types of differences: `'inserted'`, `'updated'`, and `'deleted'`. By default green is used for insertions, blue for updates, and red for deletions. If `opts.indent` is set, output will span multiple lines and `opts.indent` spaces will be used for leading whitespace. If `opts.comma === 'first'` then commas will be placed at the start of lines. Setting `opts.comment` to `true` will turn on comments with the previous contents like this: Setting `opts.deepEqual` allows a different equality function to be used. By default [deep-is](https://npmjs.org/package/deep-is) is used. ![object comments](http://substack.net/images/screenshots/difflet_object_comments.png) diff(prev, next) ---------------- Return a stream with the colorful changes between objects `prev` and `next`. diff.compare(prev, next) ------------------------ Return a string with the colorful changes between `prev` and `next`. difflet.compare(prev, next) --------------------------- Return a string with the colorful changes between `prev` and `next` with the default options. install ======= With [npm](http://npmjs.org) do: ``` npm install difflet ``` test ==== With [npm](http://npmjs.org) do: ``` npm test ``` license ======= Copyright (C) 2012, 2013 James Halliday Licensed under the MIT license, see LICENSE for details. difflet-1.0.1/example/000077500000000000000000000000001264163412000145605ustar00rootroot00000000000000difflet-1.0.1/example/cmp_array.js000066400000000000000000000002461264163412000170750ustar00rootroot00000000000000var difflet = require('../'); var s = difflet({ indent : 2, comment : true }).compare( [ 1, [2,3,{a:4}], 3 ], [ 1, [[5],6,7], 4 ] ); process.stdout.write(s); difflet-1.0.1/example/cmp_object.js000066400000000000000000000003201264163412000172160ustar00rootroot00000000000000var difflet = require('../'); var s = difflet({ indent : 2, comment : true }).compare( { z : [6,7], a : 'abcdefgh', b : [ 31, 'xxt' ] }, { x : 5, a : 'abdcefg', b : [ 51, 'xxs' ] } ); console.log(s); difflet-1.0.1/example/colors.js000066400000000000000000000004751264163412000164250ustar00rootroot00000000000000var diff = require('../')({ indent : 2 }); var prev = { yy : 6, zz : 5, a : [1,2,3], fn : 'beep', c : { x : 7, z : 3 } }; var next = { a : [ 1, 2, "z", /beep/, new Buffer(3) ], fn : function qqq () {}, b : [5,6,7], c : { x : 8, y : 5 } }; diff(prev, next).pipe(process.stdout); difflet-1.0.1/example/comma_first.js000066400000000000000000000003731264163412000174240ustar00rootroot00000000000000var diff = require('../')({ indent : 2, comma : 'first', }); var prev = { yy : 6, zz : 5, a : [1,2,3] }; var next = { a : [ 1, 2, 3, [4], "z", /beep/, new Buffer(3) ], fn : 8, b : [5,6,7] }; diff(prev, next).pipe(process.stdout); difflet-1.0.1/example/deep_array.js000066400000000000000000000005251264163412000172330ustar00rootroot00000000000000var difflet = require('../'); var subjectA = { levelOne: { levelTwo: { array: ['a', 'b', 3] } } }; var subjectB = { levelOne: { levelTwo: { array: [] } } }; var s = difflet({ indent : 2, comment : true }).compare( subjectA, subjectB ); process.stdout.write("DIFFLET:"); process.stdout.write(s); difflet-1.0.1/example/diff.js000066400000000000000000000003711264163412000160270ustar00rootroot00000000000000var difflet = require('../'); var a = { x : 4, z : 8, xs : [ 5, 2, 1, { 0 : 'c' } ], }; var b = { x : 4, y : 5, xs : [ 5, 2, 2, { c : 5 } ], }; var s = difflet({ comment : true, indent : 2 }).compare(a, b); console.log(s); difflet-1.0.1/example/html.js000066400000000000000000000012511264163412000160610ustar00rootroot00000000000000var difflet = require('../'); var ent = require('ent'); var tags = { inserted : '', updated : '', deleted : '', }; var diff = difflet({ start : function (t, s) { s.write(tags[t]); }, stop : function (t, s) { s.write(''); }, write : function (buf) { stream.write(ent.encode(buf)) }, }); var prev = { yy : 6, zz : 5, a : [1,2,3], fn : function qq () {} }; var next = { a : [ 1, 2, 3, [4], "z", /beep/, new Buffer([0,1,2]) ], fn : 'I <3 robots', b : [5,6,7] }; var stream = diff(prev, next); stream.pipe(process.stdout, { end : false }); difflet-1.0.1/example/string.js000066400000000000000000000001651264163412000164260ustar00rootroot00000000000000var difflet = require('../'); var s = difflet.compare({ a : 2, c : 5 }, { a : 3, b : 4 }); process.stdout.write(s); difflet-1.0.1/index.js000066400000000000000000000326071264163412000146020ustar00rootroot00000000000000var traverse = require('traverse'); var Stream = require('stream').Stream; var charm = require('charm'); var defaultDeepEqual = require('deep-is'); var exports = module.exports = function (opts_) { var fn = difflet.bind(null, opts_); fn.compare = function (prev, next) { var opts = Object.keys(opts_ || {}).reduce(function (acc, key) { acc[key] = opts_[key]; return acc; }, {}); var s = opts.stream = new Stream; var data = ''; s.write = function (buf) { data += buf }; s.end = function () {}; s.readable = true; s.writable = true; difflet(opts, prev, next); return data; }; return fn; }; exports.compare = function (prev, next) { return exports({}).compare(prev, next); }; function difflet (opts, prev, next) { var stream = opts.stream || new Stream; if (!opts.stream) { stream.readable = true; stream.writable = true; stream.write = function (buf) { this.emit('data', buf) }; stream.end = function () { this.emit('end') }; } if (!opts) opts = {}; var deepEqual = opts.deepEqual || defaultDeepEqual; if (opts.start === undefined && opts.stop === undefined) { var c = charm(stream); opts.start = function (type) { c.foreground({ inserted : 'green', updated : 'blue', deleted : 'red', comment : 'cyan', }[type]); c.display('bright'); }; opts.stop = function (type) { c.display('reset'); }; } var write = function (buf) { if (opts.write) opts.write(buf, stream) else stream.write(buf) }; var commaFirst = opts.comma === 'first'; var stringify = function (node, params) { return stringifier.call(this, true, node, params || opts); }; var plainStringify = function (node, params) { return stringifier.call(this, false, node, params || opts); }; var levels = 0; function set (type) { if (levels === 0) opts.start(type, stream); levels ++; } function unset (type) { if (--levels === 0) opts.stop(type, stream); } function stringifier (insertable, node, opts) { var indent = opts.indent; var writeObj = function(obj) { traverse(obj).forEach(function (x) { plainStringify.call(this, x, { indent : indent }); }); }; if (insertable) { var prevNode = traverse.get(prev, this.path || []); } var inserted = insertable && prevNode === undefined; var indentx; try { indentx = indent ? Array( ((this.path || []).length + 1) * indent + 1 ).join(' ') : ''; } catch (e) { // at times we get an invalid Array size here and need to prevent crashing indentx = ''; } if (commaFirst) indentx = indentx.slice(indent); if (Array.isArray(node)) { var updated = (prevNode || traverse.has(prev, this.path)) && !Array.isArray(prevNode); if (updated) { set('updated'); } if (opts.comment && !Array.isArray(prevNode)) { indent = 0; } if (Array.isArray(prevNode) && node.length != prevNode.length && node.length == 0) { write('[\n'); set('deleted'); prevNode.forEach(function(obj, i) { if(i != 0) write(', '); writeObj(obj); }); unset('deleted'); return write(indentx + '\n]'); } this.before(function () { if (inserted) set('inserted'); if (indent && commaFirst) { if ((this.path || []).length === 0 || Array.isArray(this.parent.node)) { write('[ '); } else write('\n' + indentx + '[ '); } else if (indent) { write('[\n' + indentx); } else { write('['); } }); this.post(function (child) { if (!child.isLast && !(indent && commaFirst)) { write(','); } var prev = prevNode && prevNode[child.key]; if (indent && opts.comment && child.node !== prev && (typeof child.node !== 'object' || typeof prev !== 'object') ) { set('comment'); write(' // != '); traverse(prev).forEach(writeObj); unset('comment'); } if (!child.isLast) { if (indent && commaFirst) { write('\n' + indentx + ', '); } else if (indent) { write('\n' + indentx); } } else { if(prevNode && prevNode.length > child.parent.node.length) { var missingItems = prevNode.slice(child.key + 1); set('deleted'); missingItems.forEach(function(item) { write(', '); writeObj(item); }); unset('deleted'); } } }); this.after(function () { if (indent && commaFirst) write('\n' + indentx); else if (indent) write('\n' + indentx.slice(indent)); write(']'); if (updated) unset('updated'); if (inserted) unset('inserted'); }); } else if (isRegExp(node)) { this.block(); if (inserted) { set('inserted'); write(node.toString()); unset('inserted'); } else if (insertable && prevNode !== node) { set('updated'); write(node.toString()); unset('updated'); } else write(node.toString()); } else if (typeof node === 'object' && node && typeof node.inspect === 'function') { this.block(); if (inserted) { set('inserted'); write(node.inspect()); unset('inserted'); } else if (!(prevNode && typeof prevNode.inspect === 'function' && prevNode.inspect() === node.inspect())) { set('updated'); write(node.inspect()); unset('updated'); } else write(node.inspect()); } else if (typeof node == 'object' && node !== null) { var insertedKey = false; var deleted = insertable && typeof prevNode === 'object' && prevNode ? Object.keys(prevNode).filter(function (key) { return !Object.hasOwnProperty.call(node, key); }) : [] ; this.before(function () { if (inserted) set('inserted'); write(indent && commaFirst && !this.isRoot ? '\n' + indentx + '{ ' : '{' ); }); this.pre(function (x, key) { if (insertable) { var obj = traverse.get(prev, this.path.concat(key)); if (obj === undefined) { insertedKey = true; set('inserted'); } } if (indent && !commaFirst) write('\n' + indentx); plainStringify(key); write(indent ? ' : ' : ':'); }); this.post(function (child) { if (!child.isLast && !(indent && commaFirst)) { write(','); } if (child.isLast && deleted.length) { if (insertedKey) unset('inserted'); insertedKey = false; } else if (insertedKey) { unset('inserted'); insertedKey = false; } var prev = prevNode && prevNode[child.key]; if (indent && opts.comment && child.node !== prev && (typeof child.node !== 'object' || typeof prev !== 'object') ) { set('comment'); write(' // != '); traverse(prev).forEach(function (x) { plainStringify.call(this, x, { indent : 0 }); }); unset('comment'); } if (child.isLast && deleted.length) { if (insertedKey) unset('inserted'); insertedKey = false; if (indent && commaFirst) { write('\n' + indentx + ', ') } else if (opts.comment && indent) { write('\n' + indentx); } else if (indent) { write(',\n' + indentx); } else write(','); } else { if (!child.isLast) { if (indent && commaFirst) { write('\n' + indentx + ', '); } } } }); this.after(function () { if (inserted) unset('inserted'); if (deleted.length) { if (indent && !commaFirst && Object.keys(node).length === 0) { write('\n' + indentx); } set('deleted'); deleted.forEach(function (key, ix) { if (indent && opts.comment) { unset('deleted'); set('comment'); write('// '); unset('comment'); set('deleted'); } plainStringify(key); write(indent ? ' : ' : ':'); traverse(prevNode[key]).forEach(function (x) { plainStringify.call(this, x, { indent : 0 }); }); var last = ix === deleted.length - 1; if (insertable && !last) { if (indent && commaFirst) { write('\n' + indentx + ', '); } else if (indent) { write(',\n' + indentx); } else write(','); } }); unset('deleted'); } if (commaFirst && indent) { write(indentx.slice(indent) + ' }'); } else if (indent) { write('\n' + indentx.slice(indent) + '}'); } else write('}'); }); } else { var changed = false; if (inserted) set('inserted'); else if (insertable && !deepEqual(prevNode, node)) { changed = true; set('updated'); } if (typeof node === 'string') { write('"' + node.toString().replace(/"/g, '\\"') + '"'); } else if (isRegExp(node)) { write(node.toString()); } else if (typeof node === 'function') { write(node.name ? '[Function: ' + node.name + ']' : '[Function]' ); } else if (node === undefined) { write('undefined'); } else if (node === null) { write('null'); } else { write(node.toString()); } if (inserted) unset('inserted'); else if (changed) unset('updated'); } } if (opts.stream) { traverse(next).forEach(stringify); } else process.nextTick(function () { traverse(next).forEach(stringify); stream.emit('end'); }); return stream; } function isRegExp (node) { return node instanceof RegExp || (node && typeof node.test === 'function' && typeof node.exec === 'function' && typeof node.compile === 'function' && node.constructor && node.constructor.name === 'RegExp' ); } difflet-1.0.1/package.json000066400000000000000000000015771264163412000154250ustar00rootroot00000000000000{ "name" : "difflet", "description" : "colorful diffs for javascript objects", "version" : "1.0.1", "repository" : { "type" : "git", "url" : "git://github.com/substack/difflet.git" }, "main" : "index.js", "keywords" : [ "diff", "object", "compare" ], "directories" : { "lib" : ".", "example" : "example", "test" : "test" }, "scripts" : { "test" : "tap test/*.js" }, "dependencies" : { "traverse" : "0.6.x", "charm" : "0.1.x", "deep-is" : "0.1.x" }, "devDependencies" : { "tap" : "0.1.x", "ent" : "0.0.x" }, "engines" : { "node" : ">=0.4.0" }, "license" : "MIT", "author" : { "name" : "James Halliday", "email" : "mail@substack.net", "url" : "http://substack.net" } } difflet-1.0.1/test/000077500000000000000000000000001264163412000141045ustar00rootroot00000000000000difflet-1.0.1/test/diffing-NaN.js000066400000000000000000000005501264163412000165220ustar00rootroot00000000000000var difflet = require('../'); var diff = difflet(); var test = require('tap').test; test('diffing NaN against NaN', function (t) { t.plan(1); var d = diff.compare(NaN, NaN); t.equal(d, 'NaN'); }); test('diffing { o: NaN } against { o: NaN }', function (t) { t.plan(1); var d = diff.compare({ o: NaN }, { o: NaN }); t.equal(d, '{"o":NaN}'); }); difflet-1.0.1/test/html.js000066400000000000000000000041461264163412000154130ustar00rootroot00000000000000var difflet = require('../'); var test = require('tap').test; var ent = require('ent'); var tags = { inserted : 'g', updated : 'b', deleted : 'r', }; test('html output', function (t) { t.plan(1); var diff = difflet({ start : function (t, s) { s.write('<' + tags[t] + '>'); }, stop : function (t, s) { s.write(''); }, write : function (buf, s) { s.write(ent.encode(buf)); } }); var stream = diff( { yy : 6, zz : 5, a : [1,2,3], fn : function qqq () {} }, { a : [ 1, 2, 3, [4], "z", /beep/, new Buffer([0,1,2]) ], fn : function rrr () {}, b : [5,6,7] } ); var data = '' stream.on('data', function (buf) { data += buf }); stream.on('end', function () { t.equal(data, '{"a":[1,2,3,[4],"z",' + '/beep/,<Buffer 00 01 02>],' + '"fn":[Function: rrr],' + '"b":[5,6,7],"yy":6,' + '"zz":5}' ); t.end(); }); }); test('compare html output', function (t) { t.plan(1); var diff = difflet({ start : function (t, s) { s.write('<' + tags[t] + '>'); }, stop : function (t, s) { s.write(''); }, write : function (buf, s) { s.write(ent.encode(buf)); } }); var data = diff.compare( { yy : 6, zz : 5, a : [1,2,3], fn : function qqq () {} }, { a : [ 1, 2, 3, [4], "z", /beep/, new Buffer([0,1,2]) ], fn : function rrr () {}, b : [5,6,7] } ); t.equal(data, '{"a":[1,2,3,[4],"z",' + '/beep/,<Buffer 00 01 02>],' + '"fn":[Function: rrr],' + '"b":[5,6,7],"yy":6,' + '"zz":5}' ); t.end(); }); difflet-1.0.1/test/object_tests.js000066400000000000000000000025521264163412000171360ustar00rootroot00000000000000var difflet = require('../'); var diff = difflet(); var test = require('tap').test; test('diffing with empty nested array', function (t) { t.plan(1); var subjectA = { levelOne: { levelTwo: { array: ['a', 'b', 3] } } }; var subjectB = { levelOne: { levelTwo: { array: [] } } }; var d = diff.compare(subjectA, subjectB); t.equal(d, "{\"levelOne\":{\"levelTwo\":{\"array\":[\n\u001b[31m\u001b[1m\"a\", \"b\", 3\u001b[0m\n]}}}"); }); test('diffing nested array with missing values', function (t) { t.plan(1); var subjectA = { levelOne: { levelTwo: { array: ['a', 'b', 3] } } }; var subjectB = { levelOne: { levelTwo: { array: ['a'] } } }; var d = diff.compare(subjectA, subjectB); t.equal(d, "{\"levelOne\":{\"levelTwo\":{\"array\":[\"a\"\u001b[31m\u001b[1m, \"b\", 3\u001b[0m]}}}"); }); test('diffing nested array with added values', function (t) { t.plan(1); var subjectA = { levelOne: { levelTwo: { array: ['a'] } } }; var subjectB = { levelOne: { levelTwo: { array: ['a', 'b', 3] } } }; var d = diff.compare(subjectA, subjectB); t.equal(d, "{\"levelOne\":{\"levelTwo\":{\"array\":[\"a\",\u001b[32m\u001b[1m\"b\"\u001b[0m,\u001b[32m\u001b[1m3\u001b[0m]}}}"); });