UNPKG

3.19 kBJavaScriptView Raw
1// Original source
2// https://gist.github.com/plukevdh/dec4b41d5b7d67f83be630afecee499e
3import * as R from 'ramda';
4
5const isObject = R.compose(R.equals('Object'), R.type);
6const allAreObjectsOrLists = R.compose(R.either(R.all(isObject), R.all(Array.isArray)), R.values);
7const hasVersion = versionLabel => obj => R.has(versionLabel, obj);
8const hasBoth = versionLabels => obj => R.all(versionLabel => hasVersion(versionLabel)(obj), versionLabels);
9const isEqual = versionLabels => obj => R.both(
10 hasBoth(versionLabels),
11 R.compose(
12 R.apply(R.equals),
13 R.values
14 )
15)(obj);
16
17const markAdded = obj => R.compose(R.append(undefined), R.values)(obj); // eslint-disable-line no-undefined
18const markRemoved = obj => R.compose(R.prepend(undefined), R.values)(obj); // eslint-disable-line no-undefined
19const isAddition = versionLabels => obj => R.both(
20 hasVersion(R.head(versionLabels)),
21 R.complement(hasVersion(R.last(versionLabels)))
22)(obj);
23const isRemoval = versionLabels => obj => R.both(
24 R.complement(
25 hasVersion(R.head(versionLabels))
26 ),
27 hasVersion(R.last(versionLabels))
28)(obj);
29
30/**
31 * Diffs objects and replaces differences with an object {__left: left value, __right: right value}
32 * @param {[String]} Two values indicating the names of the versions. If left null then __left and __right
33 * are used for the diff
34 * @param {Object} l The left object to diff
35 * @param {Object} r The right object to diff
36 * @returns {Object} A deep diff of l and r. Equal values are removed, different values are replaced by an
37 * object keyed by the two version strings with the corresponding values
38 */
39export const objectDiff = R.curry((versionLabels, l, r) => {
40 const labels = R.defaultTo(['__left', '__right'], versionLabels);
41 const [left, right] = labels;
42 return R.compose(
43 result => R.map(
44 R.cond([
45 [obj => isAddition(labels)(obj),
46 obj => markAdded(obj)
47 ],
48 [obj => isRemoval(labels)(obj),
49 obj => markRemoved(obj)
50 ],
51 [obj => hasBoth(labels)(obj),
52 obj => R.when(
53 allAreObjectsOrLists,
54 // If all are objects or list recurse
55 // Lists necessary become objects keyed by index since some indices are removed
56 o => R.compose(
57 // Remove the left, right labels and recurse
58 values => R.apply(objectDiff(versionLabels), values),
59 R.values
60 )(o)
61 // Otherwise we keep the left and right labels and we are done
62 )(obj)
63 ]
64 ]
65 ), result),
66 result => R.reject(obj => isEqual(labels)(obj), result),
67 R.useWith(
68 R.mergeWith(R.merge),
69 [
70 R.map(R.objOf(left)),
71 R.map(R.objOf(right))
72 ]
73 )
74 )(l, r);
75});
76
77/**
78 * Pretty-prints objectDiff
79 * @param {[String]} versionLabels The two labels or null
80 * @param {Object} l The left-side value
81 * @param {Object} r The right-side value
82 * @returns {String} The diff string
83 */
84export const prettyPrintObjectDiff = (versionLabels, l, r) => {
85 return R.compose(
86 result => JSON.stringify(result, null, 2),
87 objectDiff
88 )(versionLabels, l, r);
89};