From a81096024ca1d6de75feb0b26a2f6a4aa5ae4bf7 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Sun, 15 Jan 2023 21:26:23 +0100 Subject: [PATCH] fix: produce results should never be frozen when returned from nested produces, to prevent 'hiding' drafts. Fixes #935 --- __tests__/regressions.js | 10 ++++++++-- src/core/finalize.ts | 3 ++- src/core/immerClass.ts | 2 -- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/__tests__/regressions.js b/__tests__/regressions.js index 8bc587e2..cfbf09dc 100644 --- a/__tests__/regressions.js +++ b/__tests__/regressions.js @@ -252,7 +252,7 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) { }) }) - test("Nested and chained produce calls throw 'Cannot perform 'get' on a proxy that has been revoked' error", () => { + test.only("Nested and chained produce calls throw 'Cannot perform 'get' on a proxy that has been revoked' error", () => { const state = { foo: { bar: { @@ -267,9 +267,15 @@ function runBaseTest(name, useProxies, autoFreeze, useListener) { draft.foo = produce(draft.foo, fooDraft => { /* another produce call makes this fail */ /* no actual mutation necessary to make this happen */ + // This happened before becouse the outer object is not modified, + // so assumed to be safely freezable by Immer, while it actually still + // contains a draft of bar, which wasn't retracted since we don't do that in nested + // producers, as it can still be modified outside a produce }) }) - JSON.stringify(newState) + expect(newState).toEqual({ + foo: {baz: "apple", bar: {baz: "banana"}} + }) }) }) } diff --git a/src/core/finalize.ts b/src/core/finalize.ts index 5e621b45..3d6d1135 100644 --- a/src/core/finalize.ts +++ b/src/core/finalize.ts @@ -161,7 +161,8 @@ function finalizeProperty( } function maybeFreeze(scope: ImmerScope, value: any, deep = false) { - if (scope.immer_.autoFreeze_ && scope.canAutoFreeze_) { + // we never freeze for a non-root scope; as it would prevent pruning for drafts inside wrapping objects + if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) { freeze(value, deep) } } diff --git a/src/core/immerClass.ts b/src/core/immerClass.ts index 59d2f494..93b0ef1c 100644 --- a/src/core/immerClass.ts +++ b/src/core/immerClass.ts @@ -88,8 +88,6 @@ export class Immer implements ProducersFns { // Only plain objects, arrays, and "immerable classes" are drafted. if (isDraftable(base)) { const scope = enterScope(this) - //when base is a draft,can't freeze - scope.canAutoFreeze_ = !isDraft(base) const proxy = createProxy(this, base, undefined) let hasError = true try {