source: node_modules/immer/src/core/finalize.ts

Last change on this file was a762898, checked in by istevanoska <ilinastevanoska@…>, 5 months ago

Added visualizations

  • Property mode set to 100644
File size: 5.7 KB
Line 
1import {
2 ImmerScope,
3 DRAFT_STATE,
4 isDraftable,
5 NOTHING,
6 PatchPath,
7 each,
8 has,
9 freeze,
10 ImmerState,
11 isDraft,
12 SetState,
13 set,
14 ArchType,
15 getPlugin,
16 die,
17 revokeScope,
18 isFrozen,
19 isMap
20} from "../internal"
21
22export function processResult(result: any, scope: ImmerScope) {
23 scope.unfinalizedDrafts_ = scope.drafts_.length
24 const baseDraft = scope.drafts_![0]
25 const isReplaced = result !== undefined && result !== baseDraft
26 if (isReplaced) {
27 if (baseDraft[DRAFT_STATE].modified_) {
28 revokeScope(scope)
29 die(4)
30 }
31 if (isDraftable(result)) {
32 // Finalize the result in case it contains (or is) a subset of the draft.
33 result = finalize(scope, result)
34 if (!scope.parent_) maybeFreeze(scope, result)
35 }
36 if (scope.patches_) {
37 getPlugin("Patches").generateReplacementPatches_(
38 baseDraft[DRAFT_STATE].base_,
39 result,
40 scope.patches_,
41 scope.inversePatches_!
42 )
43 }
44 } else {
45 // Finalize the base draft.
46 result = finalize(scope, baseDraft, [])
47 }
48 revokeScope(scope)
49 if (scope.patches_) {
50 scope.patchListener_!(scope.patches_, scope.inversePatches_!)
51 }
52 return result !== NOTHING ? result : undefined
53}
54
55function finalize(rootScope: ImmerScope, value: any, path?: PatchPath) {
56 // Don't recurse in tho recursive data structures
57 if (isFrozen(value)) return value
58
59 const useStrictIteration = rootScope.immer_.shouldUseStrictIteration()
60
61 const state: ImmerState = value[DRAFT_STATE]
62 // A plain object, might need freezing, might contain drafts
63 if (!state) {
64 each(
65 value,
66 (key, childValue) =>
67 finalizeProperty(rootScope, state, value, key, childValue, path),
68 useStrictIteration
69 )
70 return value
71 }
72 // Never finalize drafts owned by another scope.
73 if (state.scope_ !== rootScope) return value
74 // Unmodified draft, return the (frozen) original
75 if (!state.modified_) {
76 maybeFreeze(rootScope, state.base_, true)
77 return state.base_
78 }
79 // Not finalized yet, let's do that now
80 if (!state.finalized_) {
81 state.finalized_ = true
82 state.scope_.unfinalizedDrafts_--
83 const result = state.copy_
84 // Finalize all children of the copy
85 // For sets we clone before iterating, otherwise we can get in endless loop due to modifying during iteration, see #628
86 // To preserve insertion order in all cases we then clear the set
87 // And we let finalizeProperty know it needs to re-add non-draft children back to the target
88 let resultEach = result
89 let isSet = false
90 if (state.type_ === ArchType.Set) {
91 resultEach = new Set(result)
92 result.clear()
93 isSet = true
94 }
95 each(
96 resultEach,
97 (key, childValue) =>
98 finalizeProperty(
99 rootScope,
100 state,
101 result,
102 key,
103 childValue,
104 path,
105 isSet
106 ),
107 useStrictIteration
108 )
109 // everything inside is frozen, we can freeze here
110 maybeFreeze(rootScope, result, false)
111 // first time finalizing, let's create those patches
112 if (path && rootScope.patches_) {
113 getPlugin("Patches").generatePatches_(
114 state,
115 path,
116 rootScope.patches_,
117 rootScope.inversePatches_!
118 )
119 }
120 }
121 return state.copy_
122}
123
124function finalizeProperty(
125 rootScope: ImmerScope,
126 parentState: undefined | ImmerState,
127 targetObject: any,
128 prop: string | number,
129 childValue: any,
130 rootPath?: PatchPath,
131 targetIsSet?: boolean
132) {
133 if (childValue == null) {
134 return
135 }
136
137 if (typeof childValue !== "object" && !targetIsSet) {
138 return
139 }
140 const childIsFrozen = isFrozen(childValue)
141 if (childIsFrozen && !targetIsSet) {
142 return
143 }
144
145 if (process.env.NODE_ENV !== "production" && childValue === targetObject)
146 die(5)
147 if (isDraft(childValue)) {
148 const path =
149 rootPath &&
150 parentState &&
151 parentState!.type_ !== ArchType.Set && // Set objects are atomic since they have no keys.
152 !has((parentState as Exclude<ImmerState, SetState>).assigned_!, prop) // Skip deep patches for assigned keys.
153 ? rootPath!.concat(prop)
154 : undefined
155 // Drafts owned by `scope` are finalized here.
156 const res = finalize(rootScope, childValue, path)
157 set(targetObject, prop, res)
158 // Drafts from another scope must prevented to be frozen
159 // if we got a draft back from finalize, we're in a nested produce and shouldn't freeze
160 if (isDraft(res)) {
161 rootScope.canAutoFreeze_ = false
162 } else return
163 } else if (targetIsSet) {
164 targetObject.add(childValue)
165 }
166 // Search new objects for unfinalized drafts. Frozen objects should never contain drafts.
167 if (isDraftable(childValue) && !childIsFrozen) {
168 if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) {
169 // optimization: if an object is not a draft, and we don't have to
170 // deepfreeze everything, and we are sure that no drafts are left in the remaining object
171 // cause we saw and finalized all drafts already; we can stop visiting the rest of the tree.
172 // This benefits especially adding large data tree's without further processing.
173 // See add-data.js perf test
174 return
175 }
176 if (
177 parentState &&
178 parentState.base_ &&
179 parentState.base_[prop] === childValue &&
180 childIsFrozen
181 ) {
182 // Object is unchanged from base - no need to process further
183 return
184 }
185 finalize(rootScope, childValue)
186 // Immer deep freezes plain objects, so if there is no parent state, we freeze as well
187 // Per #590, we never freeze symbolic properties. Just to make sure don't accidentally interfere
188 // with other frameworks.
189 if (
190 (!parentState || !parentState.scope_.parent_) &&
191 typeof prop !== "symbol" &&
192 (isMap(targetObject)
193 ? targetObject.has(prop)
194 : Object.prototype.propertyIsEnumerable.call(targetObject, prop))
195 )
196 maybeFreeze(rootScope, childValue)
197 }
198}
199
200function maybeFreeze(scope: ImmerScope, value: any, deep = false) {
201 // we never freeze for a non-root scope; as it would prevent pruning for drafts inside wrapping objects
202 if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) {
203 freeze(value, deep)
204 }
205}
Note: See TracBrowser for help on using the repository browser.