diff --git a/src/index.test.ts b/src/index.test.ts index 249bbb0..e99e28c 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1431,3 +1431,57 @@ test('#310 fixes backwards compat', () => { }, }); }); + +describe('root references in shared objects (#361)', () => { + for (const dedupe of [false, true]) { + for (const featuredFirst of [false, true]) { + test(`dedupe=${dedupe}, featuredFirst=${featuredFirst}`, () => { + const instance = new SuperJSON({ dedupe }); + const user: any = { name: 'ann' }; + const post = { author: user }; + if (featuredFirst) { + user.featured = post; + user.posts = [post]; + } else { + user.posts = [post]; + user.featured = post; + } + + const encoded = instance.serialize(user); + for (const back of [ + instance.deserialize(encoded), + instance.parse(instance.stringify(user)), + ]) { + expect(back.featured).toBe(back.posts[0]); + expect(back.posts[0].author).toBe(back); + expect(back.name).toBe('ann'); + } + expect(user.posts[0].author).toBe(user); + }); + } + } +}); + +test('root references in shared array elements', () => { + const root: any[] = []; + const child = { root }; + root.push([child], child); + + const back = SuperJSON.parse(SuperJSON.stringify(root)); + expect(back[0][0]).toBe(back[1]); + expect(back[1].root).toBe(back); +}); + +test('root references with legacy referential equality metadata', () => { + const back = SuperJSON.deserialize({ + json: { + posts: [{ author: null }], + featured: { author: null }, + }, + meta: { + referentialEqualities: [['posts.0.author'], { featured: ['posts.0'] }], + }, + }); + expect(back.featured).toBe(back.posts[0]); + expect(back.posts[0].author).toBe(back); +}); diff --git a/src/plainer.ts b/src/plainer.ts index 8e9059f..a73f475 100644 --- a/src/plainer.ts +++ b/src/plainer.ts @@ -96,6 +96,11 @@ export function applyReferentialEqualityAnnotations( if (isArray(annotations)) { const [root, other] = annotations; + if (other) { + forEach(other, apply); + } + + // Restore root references after shared objects have their final identity. root.forEach(identicalPath => { plain = setDeep( plain, @@ -103,10 +108,6 @@ export function applyReferentialEqualityAnnotations( () => plain ); }); - - if (other) { - forEach(other, apply); - } } else { forEach(annotations, apply); }