diff --git a/src/index.ts b/src/index.ts index b14e1f0..d729581 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export { getObjectDiff } from "./object-diff"; export { getListDiff } from "./list-diff"; +export { isEqual, isObject } from "./utils"; diff --git a/src/object-diff.ts b/src/object-diff.ts index d5189ff..64e88de 100644 --- a/src/object-diff.ts +++ b/src/object-diff.ts @@ -59,27 +59,69 @@ function formatSingleObjectDiff( } function getPreviousMatch( - prevSubValues: [string, any][] | null, + previousValue: any | undefined, nextSubProperty: any ): any | undefined { - if (!prevSubValues) { + if (!previousValue) { return undefined; } - const previousMatch = prevSubValues.find(([subPreviousKey]) => + const previousMatch = Object.entries(previousValue).find(([subPreviousKey]) => isEqual(subPreviousKey, nextSubProperty) ); return previousMatch ? previousMatch[1] : undefined; } +function getValueStatus(previousValue: any, nextValue: any): DiffStatus { + if (isEqual(previousValue, nextValue)) { + return STATUS.EQUAL; + } + return STATUS.UPDATED; +} + +function getPropertyStatus(subPropertiesDiff: Subproperties[]): DiffStatus { + return subPropertiesDiff.some((property) => property.status !== STATUS.EQUAL) + ? STATUS.UPDATED + : STATUS.EQUAL; +} + +function getDeletedProperties( + previousValue: any, + nextValue: any, + nextProperty: string +) { + if (!previousValue) return undefined; + const previousMatch = previousValue[nextProperty]; + if (!previousMatch) return undefined; + const nextMatch = nextValue[nextProperty]; + const nextKeys = isObject(nextMatch) ? Object.keys(nextMatch) : []; + const prevKeys = isObject(previousMatch) ? Object.keys(previousMatch) : []; + const deletedKeys = prevKeys.filter( + (previousKey) => !nextKeys.includes(previousKey) + ); + const result = deletedKeys.map((deletedKey) => ({ + property: deletedKey, + value: previousMatch[deletedKey], + })); + if (result.length > 0) return result; + return undefined; +} + function getSubPropertiesDiff( previousValue: Record | undefined, nextValue: Record ): Subproperties[] { const subPropertiesDiff: Subproperties[] = []; - const prevSubValues = previousValue ? Object.entries(previousValue) : null; - let subDiff: Subproperties["subDiff"]; + let subDiff: Subproperties[]; Object.entries(nextValue).forEach(([nextSubProperty, nextSubValue]) => { - const previousMatch = getPreviousMatch(prevSubValues, nextSubProperty); + const previousMatch = getPreviousMatch(previousValue, nextSubProperty); + if (!!!previousMatch && !!nextSubProperty) { + return subPropertiesDiff.push({ + name: nextSubProperty, + previousValue: undefined, + currentValue: nextSubValue, + status: STATUS.ADDED, + }); + } if (isObject(nextSubValue)) { const data: Subproperties[] = getSubPropertiesDiff( previousMatch, @@ -88,15 +130,33 @@ function getSubPropertiesDiff( if (data && data.length > 0) { subDiff = data; } + const deletedProperties = getDeletedProperties( + previousValue, + nextValue, + nextSubProperty + ); + if (deletedProperties) { + deletedProperties.forEach((deletedProperty) => { + const deletedData = { + name: deletedProperty.property, + previousValue: deletedProperty.value, + currentValue: undefined, + status: STATUS.DELETED, + }; + if (subDiff) { + subDiff.push(deletedData); + } else { + subDiff = [deletedData]; + } + }); + } } if (previousMatch) { subPropertiesDiff.push({ name: nextSubProperty, previousValue: previousMatch, currentValue: nextSubValue, - status: isEqual(previousMatch, nextSubValue) - ? STATUS.EQUAL - : STATUS.UPDATED, + status: getValueStatus(previousMatch, nextSubValue), ...(!!subDiff && { subDiff }), }); } @@ -130,16 +190,11 @@ export function getObjectDiff( previousValue, nextValue ); - const _status = subPropertiesDiff.some( - (property) => property.status !== STATUS.EQUAL - ) - ? STATUS.UPDATED - : STATUS.EQUAL; return diff.push({ property: nextProperty, previousValue, currentValue: nextValue, - status: _status, + status: getPropertyStatus(subPropertiesDiff), subPropertiesDiff, }); } @@ -147,7 +202,7 @@ export function getObjectDiff( property: nextProperty, previousValue, currentValue: nextValue, - status: previousValue === nextValue ? STATUS.EQUAL : STATUS.UPDATED, + status: getValueStatus(previousValue, nextValue), }); }); return { diff --git a/test/object-diff.test.ts b/test/object-diff.test.ts index c561d9a..48cfe25 100644 --- a/test/object-diff.test.ts +++ b/test/object-diff.test.ts @@ -172,7 +172,7 @@ describe("getObjectDiff", () => { member: true, hobbies: { football: ["psg", "nantes"], - rugby: ["france"], + golf: ["st andrews"], }, }, }, @@ -206,7 +206,7 @@ describe("getObjectDiff", () => { member: true, hobbies: { football: ["psg", "nantes"], - rugby: ["france"], + golf: ["st andrews"], }, }, }, @@ -231,7 +231,7 @@ describe("getObjectDiff", () => { member: true, hobbies: { football: ["psg", "nantes"], - rugby: ["france"], + golf: ["st andrews"], }, }, status: "updated", @@ -250,7 +250,7 @@ describe("getObjectDiff", () => { }, currentValue: { football: ["psg", "nantes"], - rugby: ["france"], + golf: ["st andrews"], }, status: "updated", subDiff: [ @@ -260,11 +260,17 @@ describe("getObjectDiff", () => { currentValue: ["psg", "nantes"], status: "updated", }, + { + name: "golf", + previousValue: undefined, + currentValue: ["st andrews"], + status: "added", + }, { name: "rugby", previousValue: ["france"], - currentValue: ["france"], - status: "equal", + currentValue: undefined, + status: "deleted", }, ], },