11import type { DiffConfig , DiffResult , PathHints } from '../types' ;
22import { ChangeType , REDACTED } from '../utils/constants' ;
3- import { getRawValue , getWrapper } from '../utils/fns' ;
3+ import { getRawValue , getWrapper , isPrimitive } from '../utils/fns' ;
44
55export function lastPathValue ( changeType : ChangeType , value : any ) {
66 return { deleted : changeType === ChangeType . REMOVE , value } ;
@@ -16,29 +16,55 @@ export function shouldRedactValue(key: any, config: DiffConfig) {
1616 return config . redactKeys ?. includes ?.( rawKey ) ;
1717}
1818
19- export function createReplacer ( config : DiffConfig ) {
19+ export function createReplacer ( config : DiffConfig , obj : any ) {
2020 const seen = new WeakSet ( ) ;
2121
2222 return function replacer ( k : any , v : any ) {
23+ // Redact the entire value if the key matches
24+ if ( shouldRedactValue ( k , config ) ) return REDACTED ;
25+
26+ // Returns circular if iterating over the same object from the replacer
27+ if ( k !== '' && v === obj ) return '[Circular]' ;
28+
2329 if ( v && typeof v === 'object' ) {
2430 if ( seen . has ( v ) ) return '[Circular]' ;
2531
2632 seen . add ( v ) ;
2733 }
2834
2935 if ( v instanceof Set ) {
30- return `Set ${ JSON . stringify ( [ ...v . values ( ) ] , replacer ) } ` ;
36+ const stringified = JSON . stringify ( [ ...v . values ( ) ] , replacer ) ;
37+
38+ seen . delete ( v ) ;
39+ return `Set ${ stringified } ` ;
3140 }
3241
3342 if ( v instanceof Map ) {
3443 const entries = [ ...v . entries ( ) ] ;
3544 const stringified = entries
36- . map (
37- ( [ key , value ] ) =>
38- `${ JSON . stringify ( key , replacer ) } : ${ JSON . stringify ( shouldRedactValue ( key , config ) ? REDACTED : value , replacer ) } `
39- )
45+ . map ( ( [ key , value ] ) => {
46+ if ( seen . has ( key ) ) {
47+ return `"[Circular]": ${ JSON . stringify ( value , replacer ) } ` ;
48+ }
49+
50+ if ( ! isPrimitive ( key ) ) {
51+ seen . add ( key ) ;
52+ }
53+
54+ const serializedValue = shouldRedactValue ( key , config )
55+ ? REDACTED
56+ : JSON . stringify ( value , replacer ) ;
57+ const serialized = `${ JSON . stringify ( key , replacer ) } : ${ serializedValue } ` ;
58+
59+ if ( ! isPrimitive ) {
60+ seen . delete ( key ) ;
61+ }
62+
63+ return serialized ;
64+ } )
4065 . join ( ', ' ) ;
4166
67+ seen . delete ( v ) ;
4268 return `Map (${ entries . length } ) { ${ stringified } }` ;
4369 }
4470
@@ -54,12 +80,13 @@ export function createReplacer(config: DiffConfig) {
5480 return `BigInt(${ v . toString ( ) } )` ;
5581 }
5682
57- return shouldRedactValue ( k , config ) ? REDACTED : v ;
83+ seen . delete ( v ) ;
84+ return v ;
5885 } ;
5986}
6087
6188export function stringify ( obj : any , config : DiffConfig ) {
62- return JSON . stringify ( obj , createReplacer ( config ) ) ;
89+ return JSON . stringify ( obj , createReplacer ( config , obj ) ) ;
6390}
6491
6592export function getObjectChangeResult (
@@ -92,13 +119,13 @@ export function getObjectChangeResult(
92119 const redactValue = shouldRedactValue ( key , config ) ;
93120 const rawValueInLhs = getRawValue ( valueInLhs ) ;
94121 const rawValueInRhs = getRawValue ( valueInRhs ) ;
95- const formattedValueInLhs = JSON . stringify (
122+ const formattedValueInLhs = stringify (
96123 redactValue ? REDACTED : rawValueInLhs ,
97- createReplacer ( config )
124+ config
98125 ) ;
99- const formattedValueInRhs = JSON . stringify (
126+ const formattedValueInRhs = stringify (
100127 redactValue ? REDACTED : rawValueInRhs ,
101- createReplacer ( config )
128+ config
102129 ) ;
103130
104131 let type = ChangeType . NOOP ;
@@ -126,18 +153,20 @@ export function getObjectChangeResult(
126153
127154 // If the type of change should be included in the results
128155 if ( includeDiffType ( type , config ) ) {
156+ const stringifiedParsedKey = stringify ( parsedKey , config ) ;
157+
129158 if ( type === ChangeType . UPDATE && ! config . showUpdatedOnly ) {
130159 result . push ( {
131160 type : ChangeType . REMOVE ,
132- str : `${ JSON . stringify ( parsedKey , createReplacer ( config ) ) } : ${ formattedValueInLhs } ,` ,
161+ str : `${ stringifiedParsedKey } : ${ formattedValueInLhs } ,` ,
133162 depth,
134163 path : [ ...path , lastPathValue ( ChangeType . REMOVE , valueInLhs ) ] ,
135164 } ) ;
136165 }
137166
138167 result . push ( {
139168 type,
140- str : `${ JSON . stringify ( parsedKey , createReplacer ( config ) ) } : ${ formattedValue } ,` ,
169+ str : `${ stringifiedParsedKey } : ${ formattedValue } ,` ,
141170 depth,
142171 path : [ ...path , lastPathValue ( type , pathValue ) ] ,
143172 } ) ;
0 commit comments