Code coverage report for mongopatch/source/diff.js

Statements: 100% (60 / 60)      Branches: 100% (41 / 41)      Functions: 100% (10 / 10)      Lines: 100% (60 / 60)      Ignored: none     

All files » mongopatch/source/ » diff.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1051 1   1 1 4842     1 594 594 3591 3591   3591 2690 2690     3591 3591     594 5388 3506 4     3502 3502   3502 1882   89       594     1 297   297 297   297 297   297   297 2020 19   2001 1545     456 456   456   456 171   285 259     26     297     1 163   163 254 254     163 163 448 426     22 31       163     1   1  
var flat = require('flat');
var traverse = require('traverse');
 
var isArray = Array.isArray;
var isObject = function(obj) {
	return obj !== null && (typeof obj === 'object') && obj.constructor === Object;
};
 
var flatten = function(o, all, prefixes) {
	var result = {};
	var add = function(path, value) {
		var key = path.join('.');
		var l = 0;
 
		for(var i = 0; i < path.length - 1; i++) {
			l += path[i].length;
			prefixes[key.slice(0, l + i)] = true;
		}
 
		all[key] = true;
		result[key] = value;
	};
 
	traverse(o).forEach(function(obj) {
		if(!isArray(obj) && !isObject(obj)) {
			if(obj === null || obj === undefined) {
				return;
			}
 
			var v = obj.constructor.name + '#' + obj;
			add(this.path, v);
 
			this.block();
		} else if(this.isLeaf && !this.isRoot) {
			// Empty array or object
			add(this.path, isArray(obj) ? 'Array#[]' : 'Object#{}');
		}
	});
 
	return result;
};
 
var diff = function(a, b, options) {
	options = options || {};
 
	var all = {};
	var prefixes = {};
 
	a = flatten(a, all, prefixes);
	b = flatten(b, all, prefixes);
 
	var result = options.accumulate || {};
 
	Object.keys(all).forEach(function(k) {
		if(prefixes[k]) {
			return;
		}
		if (a[k] === b[k]) {
			return;
		}
 
		var resultK = options.group ? k.replace(/\.\d+(\.|$)/, '.[*]$1') : k;
		var r = result[resultK];
 
		result[resultK] = r = r || { added: 0, removed: 0, updated: 0 };
 
		if(!(k in b)) {
			return r.removed++;
		}
		if(!(k in a)) {
			return r.added++;
		}
 
		r.updated++;
	});
 
	return result;
};
 
var deep = function(a, b) {
	var change = diff(a, b);
 
	Object.keys(change).forEach(function(key) {
		var c = change[key];
		change[key] = (c.added && 'added') || (c.removed && 'removed') || (c.updated && 'updated');
	});
 
	change = flat.unflatten(change);
	change = traverse(change).map(function(obj) {
		if(!Array.isArray(obj)) {
			return;
		}
 
		this.update(obj.filter(function(value) {
			return value !== undefined;
		}));
	});
 
	return change;
};
 
diff.deep = deep;
 
module.exports = diff;