From 5abdd7e5f775fd290790a0718f22a341c196d3d6 Mon Sep 17 00:00:00 2001 From: Michael Lin Date: Sat, 25 Nov 2023 14:28:33 +0800 Subject: [PATCH] fix(patch): filter no-referenced patches (#21) --- src/utils/draft.ts | 10 ++++-- src/utils/finalize.ts | 4 ++- test/immer-non-support.test.ts | 41 +++++++++++++++++++++--- test/index.test.ts | 51 +++++++++++++++++++++++++++++- test/json-patch/duplexSpec.test.ts | 1 + 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/src/utils/draft.ts b/src/utils/draft.ts index 1a16b3fe..565ce35a 100644 --- a/src/utils/draft.ts +++ b/src/utils/draft.ts @@ -42,13 +42,19 @@ export function isDraftable(value: any, options?: { mark?: Mark }) { export function getPath( target: ProxyDraft, path: any[] = [] -): (string | number | object)[] { - if (Object.hasOwnProperty.call(target, 'key')) +): (string | number | object)[] | null { + if (Object.hasOwnProperty.call(target, 'key')) { + // check if the parent is a draft and the original value is not equal to the current value + const proxyDraft = getProxyDraft(get(target.parent!.copy, target.key!)); + if (proxyDraft !== null && proxyDraft?.original !== target.original) { + return null; + } path.push( target.parent!.type === DraftType.Set ? Array.from(target.parent!.setMap!.keys()).indexOf(target.key) : target.key ); + } if (target.parent) { return getPath(target.parent, path); } diff --git a/src/utils/finalize.ts b/src/utils/finalize.ts index 2b094486..1e512654 100644 --- a/src/utils/finalize.ts +++ b/src/utils/finalize.ts @@ -91,7 +91,9 @@ export function finalizePatches( if (shouldFinalize) { if (patches && inversePatches) { const basePath = getPath(target); - generatePatches(target, basePath, patches, inversePatches); + if (basePath) { + generatePatches(target, basePath, patches, inversePatches); + } } target.finalized = true; } diff --git a/test/immer-non-support.test.ts b/test/immer-non-support.test.ts index 975ef03f..5a8474dd 100644 --- a/test/immer-non-support.test.ts +++ b/test/immer-non-support.test.ts @@ -312,7 +312,7 @@ test('circular reference', () => { } }); -test('#18 - set: assigning a non-draft with the same key', () => { +test('#18 - set: assigning a non-draft with the same key - 1', () => { const baseState = { array: [ { @@ -345,7 +345,7 @@ test('#18 - set: assigning a non-draft with the same key', () => { // @ts-ignore expect(Array.from(created[0].array[0].one)[0].three).toBe(2); expect(apply(baseState, created[1])).toEqual(created[0]); - // expect(apply(created[0], created[2])).toEqual(baseState); + expect(apply(created[0], created[2])).toEqual(baseState); enablePatches(); // @ts-ignore @@ -369,7 +369,40 @@ test('#18 - set: assigning a non-draft with the same key', () => { }).toThrowError(); // @ts-ignore - // expect(applyPatches(baseState, produced[1])).toEqual(produced[0]); + expect(() => applyPatches(baseState, produced[1])).toThrowError(); // @ts-ignore - // expect(applyPatches(produced[0], produced[2])).toEqual(baseState); + expect(applyPatches(produced[0], produced[2])).toEqual(baseState); +}); + +test('#18 - set: assigning a non-draft with the same key - 2', () => { + const baseState = { c: [{ a: 1 }, { a: 1 }] }; + enablePatches(); + // @ts-ignore + const produced = produceWithPatches(baseState, (draft) => { + const f = draft.c.pop(); + // @ts-ignore + f.a = 2; + // @ts-ignore + draft.c = new Set([draft.c[0], f]); + }); + // @ts-ignore + expect(() => applyPatches(baseState, produced[1])).toThrowError(); + // @ts-ignore + expect(applyPatches(produced[0], produced[2])).toEqual(baseState); + + const created = create( + baseState, + (draft) => { + const f = draft.c.pop(); + // @ts-ignore + f.a = 2; + // @ts-ignore + draft.c = new Set([draft.c[0], f]); + }, + { + enablePatches: true, + } + ); + expect(apply(baseState, created[1])).toEqual(created[0]); + expect(apply(created[0], created[2])).toEqual(baseState); }); diff --git a/test/index.test.ts b/test/index.test.ts index e964d86b..4c037753 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -3059,7 +3059,56 @@ test('#18 - set: assigning a non-draft with the same key - deep6', () => { // @ts-ignore expect(apply(baseState, created[1])).toEqual(created[0]); // @ts-ignore - // expect(apply(created[0], created[2])).toEqual(baseState); + expect(apply(created[0], created[2])).toEqual(baseState); + expect(created).toMatchInlineSnapshot(` + [ + { + "array": [ + { + "one": Set { + { + "three": 2, + }, + }, + }, + ], + }, + [ + { + "op": "replace", + "path": [ + "array", + ], + "value": [ + { + "one": Set { + { + "three": 2, + }, + }, + }, + ], + }, + ], + [ + { + "op": "replace", + "path": [ + "array", + ], + "value": [ + { + "one": { + "two": { + "three": 3, + }, + }, + }, + ], + }, + ], + ] + `); }); test('#18 - set: assigning a non-draft with the same key - deep3', () => { diff --git a/test/json-patch/duplexSpec.test.ts b/test/json-patch/duplexSpec.test.ts index de2aa4b9..ec4ba3cc 100644 --- a/test/json-patch/duplexSpec.test.ts +++ b/test/json-patch/duplexSpec.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable prefer-arrow-callback */ // @ts-nocheck import { create, apply } from '../../src';