-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
140 lines (119 loc) · 3.53 KB
/
index.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
'use strict';
var chalk = require('chalk');
var typeColors = {
modified: 'yellow',
added: 'green',
removed: 'red'
};
var defaultOptions = {
indent: ' ',
newLine: '\n',
wrap: function wrap(type, text) {
return chalk[typeColors[type]](text);
},
color: true
};
function isObject(obj) {
return typeof obj === 'object' && obj && !Array.isArray(obj);
}
function printVar(variable) {
if (typeof variable === 'function') {
return variable.toString().replace(/\{.+\}/,'{}');
} else if((typeof variable === 'object' || typeof variable === 'string') && !(variable instanceof RegExp)) {
return JSON.stringify(variable);
}
return '' + variable;
}
function indentSubItem(text, options) {
return text.split(options.newLine).map(function onMap(line, index) {
if (index === 0) {
return line;
}
return options.indent + line;
}).join(options.newLine);
}
function keyChanged(key, text, options) {
return options.indent + key + ': ' + indentSubItem(text, options) + options.newLine
}
function keyRemoved(key, variable, options) {
return options.wrap('removed', '- ' + key + ': ' + printVar(variable)) + options.newLine;
}
function keyAdded(key, variable, options) {
return options.wrap('added', '+ ' + key + ': ' + printVar(variable)) + options.newLine;
}
function diffInternal(left, right, options) {
var text = '';
var changed = false;
var itemDiff;
var keys;
var subOutput = '';
if (Array.isArray(left) && Array.isArray(right)) {
for (var i = 0; i < left.length; i++) {
if (i < right.length) {
itemDiff = diffInternal(left[i], right[i], options);
if (itemDiff.changed) {
subOutput += keyChanged(i, itemDiff.text, options);
changed = true;
}
} else {
subOutput += keyRemoved(i, left[i], options);
changed = true;
}
}
if (right.length > left.length) {
for (; i < right.length; i++) {
subOutput += keyAdded(i, right[i], options);
}
changed = true;
}
if (changed) {
text = '[' + options.newLine + subOutput + ']';
}
} else if (isObject(left) && isObject(right)) {
keys = Object.keys(left);
var rightObj = Object.assign({}, right);
var key;
keys.sort();
for (var i = 0; i < keys.length; i++) {
key = keys[i];
if (right.hasOwnProperty(key)) {
itemDiff = diffInternal(left[key], right[key], options);
if (itemDiff.changed) {
subOutput += keyChanged(key, itemDiff.text, options);
changed = true;
}
delete rightObj[key];
} else {
subOutput += keyRemoved(key, left[key], options);
changed = true;
}
}
var addedKeys = Object.keys(rightObj);
for (var i = 0; i < addedKeys.length; i++) {
subOutput += keyAdded(addedKeys[i], right[addedKeys[i]], options);
changed = true;
}
if (changed) {
text = '{' + options.newLine + subOutput + '}';
}
} else if (left !== right) {
text = options.wrap('modified', printVar(left) + ' => ' + printVar(right));
changed = true;
}
return {
changed: changed,
text: text
};
}
function diff(left, right, options) {
options = options || {};
if (!options.color && options.wrap) {
throw new Error('Can\'t specify wrap and color options together.')
}
var combinedOptions = Object.assign({}, defaultOptions, options);
if (!combinedOptions.color) {
combinedOptions.wrap = function(type, text) { return text }
}
return diffInternal(left, right, combinedOptions)
}
module.exports = diff;