Name: js-handler/node_modules/nodeunit/node_modules/tap/node_modules/difflet/index.js
| 1: | var traverse = require('traverse'); |
| 2: | var Stream = require('stream').Stream; |
| 3: | var charm = require('charm'); |
| 4: | var deepEqual = require('deep-is'); |
| 5: | |
| 6: | var exports = module.exports = function (opts_) { |
| 7: | var fn = difflet.bind(null, opts_); |
| 8: | fn.compare = function (prev, next) { |
| 9: | var opts = Object.keys(opts_ || {}).reduce(function (acc, key) { |
| 10: | acc[key] = opts_[key]; |
| 11: | return acc; |
| 12: | }, {}); |
| 13: | var s = opts.stream = new Stream; |
| 14: | var data = ''; |
| 15: | s.write = function (buf) { data += buf }; |
| 16: | s.end = function () {}; |
| 17: | s.readable = true; |
| 18: | s.writable = true; |
| 19: | |
| 20: | difflet(opts, prev, next); |
| 21: | return data; |
| 22: | }; |
| 23: | return fn; |
| 24: | }; |
| 25: | |
| 26: | exports.compare = function (prev, next) { |
| 27: | return exports({}).compare(prev, next); |
| 28: | }; |
| 29: | |
| 30: | function difflet (opts, prev, next) { |
| 31: | var stream = opts.stream || new Stream; |
| 32: | if (!opts.stream) { |
| 33: | stream.readable = true; |
| 34: | stream.writable = true; |
| 35: | stream.write = function (buf) { this.emit('data', buf) }; |
| 36: | stream.end = function () { this.emit('end') }; |
| 37: | } |
| 38: | |
| 39: | if (!opts) opts = {}; |
| 40: | if (opts.start === undefined && opts.stop === undefined) { |
| 41: | var c = charm(stream); |
| 42: | opts.start = function (type) { |
| 43: | c.foreground({ |
| 44: | inserted : 'green', |
| 45: | updated : 'blue', |
| 46: | deleted : 'red', |
| 47: | comment : 'cyan', |
| 48: | }[type]); |
| 49: | c.display('bright'); |
| 50: | }; |
| 51: | opts.stop = function (type) { |
| 52: | c.display('reset'); |
| 53: | }; |
| 54: | } |
| 55: | var write = function (buf) { |
| 56: | if (opts.write) opts.write(buf, stream) |
| 57: | else stream.write(buf) |
| 58: | }; |
| 59: | |
| 60: | var commaFirst = opts.comma === 'first'; |
| 61: | |
| 62: | var stringify = function (node, params) { |
| 63: | return stringifier.call(this, true, node, params || opts); |
| 64: | }; |
| 65: | var plainStringify = function (node, params) { |
| 66: | return stringifier.call(this, false, node, params || opts); |
| 67: | }; |
| 68: | |
| 69: | var levels = 0; |
| 70: | function set (type) { |
| 71: | if (levels === 0) opts.start(type, stream); |
| 72: | levels ++; |
| 73: | } |
| 74: | |
| 75: | function unset (type) { |
| 76: | if (--levels === 0) opts.stop(type, stream); |
| 77: | } |
| 78: | |
| 79: | function stringifier (insertable, node, opts) { |
| 80: | var indent = opts.indent; |
| 81: | |
| 82: | if (insertable) { |
| 83: | var prevNode = traverse.get(prev, this.path || []); |
| 84: | } |
| 85: | var inserted = insertable && prevNode === undefined; |
| 86: | |
| 87: | var indentx; |
| 88: | try { |
| 89: | indentx = indent ? Array( |
| 90: | ((this.path || []).length + 1) * indent + 1 |
| 91: | ).join(' ') : ''; |
| 92: | } catch (e) { |
| 93: | // at times we get an invalid Array size here and need to prevent crashing |
| 94: | indentx = ''; |
| 95: | } |
| 96: | if (commaFirst) indentx = indentx.slice(indent); |
| 97: | |
| 98: | if (Array.isArray(node)) { |
| 99: | var updated = (prevNode || traverse.has(prev, this.path)) |
| 100: | && !Array.isArray(prevNode); |
| 101: | if (updated) { |
| 102: | set('updated'); |
| 103: | } |
| 104: | |
| 105: | if (opts.comment && !Array.isArray(prevNode)) { |
| 106: | indent = 0; |
| 107: | } |
| 108: | |
| 109: | this.before(function () { |
| 110: | if (inserted) set('inserted'); |
| 111: | if (indent && commaFirst) { |
| 112: | if ((this.path || []).length === 0 |
| 113: | || Array.isArray(this.parent.node)) { |
| 114: | write('[ '); |
| 115: | } |
| 116: | else write('\n' + indentx + '[ '); |
| 117: | } |
| 118: | else if (indent) { |
| 119: | write('[\n' + indentx); |
| 120: | } |
| 121: | else { |
| 122: | write('['); |
| 123: | } |
| 124: | }); |
| 125: | |
| 126: | this.post(function (child) { |
| 127: | if (!child.isLast && !(indent && commaFirst)) { |
| 128: | write(','); |
| 129: | } |
| 130: | |
| 131: | var prev = prevNode && prevNode[child.key]; |
| 132: | if (indent && opts.comment && child.node !== prev |
| 133: | && (typeof child.node !== 'object' || typeof prev !== 'object') |
| 134: | ) { |
| 135: | set('comment'); |
| 136: | write(' // != '); |
| 137: | traverse(prev).forEach(function (x) { |
| 138: | plainStringify.call(this, x, { indent : 0 }); |
| 139: | }); |
| 140: | unset('comment'); |
| 141: | } |
| 142: | |
| 143: | if (!child.isLast) { |
| 144: | if (indent && commaFirst) { |
| 145: | write('\n' + indentx + ', '); |
| 146: | } |
| 147: | else if (indent) { |
| 148: | write('\n' + indentx); |
| 149: | } |
| 150: | } |
| 151: | }); |
| 152: | |
| 153: | this.after(function () { |
| 154: | if (indent && commaFirst) write('\n' + indentx); |
| 155: | else if (indent) write('\n' + indentx.slice(indent)); |
| 156: | |
| 157: | write(']'); |
| 158: | if (updated) unset('updated'); |
| 159: | if (inserted) unset('inserted'); |
| 160: | }); |
| 161: | } |
| 162: | else if (isRegExp(node)) { |
| 163: | this.block(); |
| 164: | |
| 165: | if (inserted) { |
| 166: | set('inserted'); |
| 167: | write(node.toString()); |
| 168: | unset('inserted'); |
| 169: | } |
| 170: | else if (insertable && prevNode !== node) { |
| 171: | set('updated'); |
| 172: | write(node.toString()); |
| 173: | unset('updated'); |
| 174: | } |
| 175: | else write(node.toString()); |
| 176: | } |
| 177: | else if (typeof node === 'object' |
| 178: | && node && typeof node.inspect === 'function') { |
| 179: | this.block(); |
| 180: | if (inserted) { |
| 181: | set('inserted'); |
| 182: | write(node.inspect()); |
| 183: | unset('inserted'); |
| 184: | } |
| 185: | else if (!(prevNode && typeof prevNode.inspect === 'function' |
| 186: | && prevNode.inspect() === node.inspect())) { |
| 187: | set('updated'); |
| 188: | write(node.inspect()); |
| 189: | unset('updated'); |
| 190: | } |
| 191: | else write(node.inspect()); |
| 192: | } |
| 193: | else if (typeof node == 'object' && node !== null) { |
| 194: | var insertedKey = false; |
| 195: | var deleted = insertable && typeof prevNode === 'object' && prevNode |
| 196: | ? Object.keys(prevNode).filter(function (key) { |
| 197: | return !Object.hasOwnProperty.call(node, key); |
| 198: | }) |
| 199: | : [] |
| 200: | ; |
| 201: | |
| 202: | this.before(function () { |
| 203: | if (inserted) set('inserted'); |
| 204: | write(indent && commaFirst && !this.isRoot |
| 205: | ? '\n' + indentx + '{ ' |
| 206: | : '{' |
| 207: | ); |
| 208: | }); |
| 209: | |
| 210: | this.pre(function (x, key) { |
| 211: | if (insertable) { |
| 212: | var obj = traverse.get(prev, this.path.concat(key)); |
| 213: | if (obj === undefined) { |
| 214: | insertedKey = true; |
| 215: | set('inserted'); |
| 216: | } |
| 217: | } |
| 218: | |
| 219: | if (indent && !commaFirst) write('\n' + indentx); |
| 220: | |
| 221: | plainStringify(key); |
| 222: | write(indent ? ' : ' : ':'); |
| 223: | }); |
| 224: | |
| 225: | this.post(function (child) { |
| 226: | if (!child.isLast && !(indent && commaFirst)) { |
| 227: | write(','); |
| 228: | } |
| 229: | |
| 230: | if (child.isLast && deleted.length) { |
| 231: | if (insertedKey) unset('inserted'); |
| 232: | insertedKey = false; |
| 233: | } |
| 234: | else if (insertedKey) { |
| 235: | unset('inserted'); |
| 236: | insertedKey = false; |
| 237: | } |
| 238: | |
| 239: | var prev = prevNode && prevNode[child.key]; |
| 240: | if (indent && opts.comment && child.node !== prev |
| 241: | && (typeof child.node !== 'object' || typeof prev !== 'object') |
| 242: | ) { |
| 243: | set('comment'); |
| 244: | write(' // != '); |
| 245: | traverse(prev).forEach(function (x) { |
| 246: | plainStringify.call(this, x, { indent : 0 }); |
| 247: | }); |
| 248: | unset('comment'); |
| 249: | } |
| 250: | |
| 251: | if (child.isLast && deleted.length) { |
| 252: | if (insertedKey) unset('inserted'); |
| 253: | insertedKey = false; |
| 254: | |
| 255: | if (indent && commaFirst) { |
| 256: | write('\n' + indentx + ', ') |
| 257: | } |
| 258: | else if (opts.comment && indent) { |
| 259: | write('\n' + indentx); |
| 260: | } |
| 261: | else if (indent) { |
| 262: | write(',\n' + indentx); |
| 263: | } |
| 264: | else write(','); |
| 265: | } |
| 266: | else { |
| 267: | if (!child.isLast) { |
| 268: | if (indent && commaFirst) { |
| 269: | write('\n' + indentx + ', '); |
| 270: | } |
| 271: | } |
| 272: | } |
| 273: | }); |
| 274: | |
| 275: | this.after(function () { |
| 276: | if (inserted) unset('inserted'); |
| 277: | |
| 278: | if (deleted.length) { |
| 279: | if (indent && !commaFirst |
| 280: | && Object.keys(node).length === 0) { |
| 281: | write('\n' + indentx); |
| 282: | } |
| 283: | |
| 284: | set('deleted'); |
| 285: | deleted.forEach(function (key, ix) { |
| 286: | if (indent && opts.comment) { |
| 287: | unset('deleted'); |
| 288: | set('comment'); |
| 289: | write('// '); |
| 290: | unset('comment'); |
| 291: | set('deleted'); |
| 292: | } |
| 293: | |
| 294: | plainStringify(key); |
| 295: | write(indent ? ' : ' : ':'); |
| 296: | traverse(prevNode[key]).forEach(function (x) { |
| 297: | plainStringify.call(this, x, { indent : 0 }); |
| 298: | }); |
| 299: | |
| 300: | var last = ix === deleted.length - 1; |
| 301: | if (insertable && !last) { |
| 302: | if (indent && commaFirst) { |
| 303: | write('\n' + indentx + ', '); |
| 304: | } |
| 305: | else if (indent) { |
| 306: | write(',\n' + indentx); |
| 307: | } |
| 308: | else write(','); |
| 309: | } |
| 310: | }); |
| 311: | unset('deleted'); |
| 312: | } |
| 313: | |
| 314: | if (commaFirst && indent) { |
| 315: | write(indentx.slice(indent) + ' }'); |
| 316: | } |
| 317: | else if (indent) { |
| 318: | write('\n' + indentx.slice(indent) + '}'); |
| 319: | } |
| 320: | else write('}'); |
| 321: | }); |
| 322: | } |
| 323: | else { |
| 324: | var changed = false; |
| 325: | |
| 326: | if (inserted) set('inserted'); |
| 327: | else if (insertable && !deepEqual(prevNode, node)) { |
| 328: | changed = true; |
| 329: | set('updated'); |
| 330: | } |
| 331: | |
| 332: | if (typeof node === 'string') { |
| 333: | write('"' + node.toString().replace(/"/g, '\\"') + '"'); |
| 334: | } |
| 335: | else if (isRegExp(node)) { |
| 336: | write(node.toString()); |
| 337: | } |
| 338: | else if (typeof node === 'function') { |
| 339: | write(node.name |
| 340: | ? '[Function: ' + node.name + ']' |
| 341: | : '[Function]' |
| 342: | ); |
| 343: | } |
| 344: | else if (node === undefined) { |
| 345: | write('undefined'); |
| 346: | } |
| 347: | else if (node === null) { |
| 348: | write('null'); |
| 349: | } |
| 350: | else { |
| 351: | write(node.toString()); |
| 352: | } |
| 353: | |
| 354: | if (inserted) unset('inserted'); |
| 355: | else if (changed) unset('updated'); |
| 356: | } |
| 357: | } |
| 358: | |
| 359: | if (opts.stream) { |
| 360: | traverse(next).forEach(stringify); |
| 361: | } |
| 362: | else process.nextTick(function () { |
| 363: | traverse(next).forEach(stringify); |
| 364: | stream.emit('end'); |
| 365: | }); |
| 366: | |
| 367: | return stream; |
| 368: | } |
| 369: | |
| 370: | function isRegExp (node) { |
| 371: | return node instanceof RegExp || (node |
| 372: | && typeof node.test === 'function' |
| 373: | && typeof node.exec === 'function' |
| 374: | && typeof node.compile === 'function' |
| 375: | && node.constructor && node.constructor.name === 'RegExp' |
| 376: | ); |
| 377: | } |
