source: frontend/node_modules/immer/src/core/immerClass.ts

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.7 KB
Line 
1import {
2 IProduceWithPatches,
3 IProduce,
4 ImmerState,
5 Drafted,
6 isDraftable,
7 processResult,
8 Patch,
9 Objectish,
10 DRAFT_STATE,
11 Draft,
12 PatchListener,
13 isDraft,
14 isMap,
15 isSet,
16 createProxyProxy,
17 getPlugin,
18 die,
19 hasProxies,
20 enterScope,
21 revokeScope,
22 leaveScope,
23 usePatchesInScope,
24 getCurrentScope,
25 NOTHING,
26 freeze,
27 current
28} from "../internal"
29
30interface ProducersFns {
31 produce: IProduce
32 produceWithPatches: IProduceWithPatches
33}
34
35export class Immer implements ProducersFns {
36 useProxies_: boolean = hasProxies
37
38 autoFreeze_: boolean = true
39
40 constructor(config?: {useProxies?: boolean; autoFreeze?: boolean}) {
41 if (typeof config?.useProxies === "boolean")
42 this.setUseProxies(config!.useProxies)
43 if (typeof config?.autoFreeze === "boolean")
44 this.setAutoFreeze(config!.autoFreeze)
45 }
46
47 /**
48 * The `produce` function takes a value and a "recipe function" (whose
49 * return value often depends on the base state). The recipe function is
50 * free to mutate its first argument however it wants. All mutations are
51 * only ever applied to a __copy__ of the base state.
52 *
53 * Pass only a function to create a "curried producer" which relieves you
54 * from passing the recipe function every time.
55 *
56 * Only plain objects and arrays are made mutable. All other objects are
57 * considered uncopyable.
58 *
59 * Note: This function is __bound__ to its `Immer` instance.
60 *
61 * @param {any} base - the initial state
62 * @param {Function} recipe - function that receives a proxy of the base state as first argument and which can be freely modified
63 * @param {Function} patchListener - optional function that will be called with all the patches produced here
64 * @returns {any} a new state, or the initial state if nothing was modified
65 */
66 produce: IProduce = (base: any, recipe?: any, patchListener?: any) => {
67 // curried invocation
68 if (typeof base === "function" && typeof recipe !== "function") {
69 const defaultBase = recipe
70 recipe = base
71
72 const self = this
73 return function curriedProduce(
74 this: any,
75 base = defaultBase,
76 ...args: any[]
77 ) {
78 return self.produce(base, (draft: Drafted) => recipe.call(this, draft, ...args)) // prettier-ignore
79 }
80 }
81
82 if (typeof recipe !== "function") die(6)
83 if (patchListener !== undefined && typeof patchListener !== "function")
84 die(7)
85
86 let result
87
88 // Only plain objects, arrays, and "immerable classes" are drafted.
89 if (isDraftable(base)) {
90 const scope = enterScope(this)
91 const proxy = createProxy(this, base, undefined)
92 let hasError = true
93 try {
94 result = recipe(proxy)
95 hasError = false
96 } finally {
97 // finally instead of catch + rethrow better preserves original stack
98 if (hasError) revokeScope(scope)
99 else leaveScope(scope)
100 }
101 if (typeof Promise !== "undefined" && result instanceof Promise) {
102 return result.then(
103 result => {
104 usePatchesInScope(scope, patchListener)
105 return processResult(result, scope)
106 },
107 error => {
108 revokeScope(scope)
109 throw error
110 }
111 )
112 }
113 usePatchesInScope(scope, patchListener)
114 return processResult(result, scope)
115 } else if (!base || typeof base !== "object") {
116 result = recipe(base)
117 if (result === undefined) result = base
118 if (result === NOTHING) result = undefined
119 if (this.autoFreeze_) freeze(result, true)
120 if (patchListener) {
121 const p: Patch[] = []
122 const ip: Patch[] = []
123 getPlugin("Patches").generateReplacementPatches_(base, result, p, ip)
124 patchListener(p, ip)
125 }
126 return result
127 } else die(21, base)
128 }
129
130 produceWithPatches: IProduceWithPatches = (base: any, recipe?: any): any => {
131 // curried invocation
132 if (typeof base === "function") {
133 return (state: any, ...args: any[]) =>
134 this.produceWithPatches(state, (draft: any) => base(draft, ...args))
135 }
136
137 let patches: Patch[], inversePatches: Patch[]
138 const result = this.produce(base, recipe, (p: Patch[], ip: Patch[]) => {
139 patches = p
140 inversePatches = ip
141 })
142
143 if (typeof Promise !== "undefined" && result instanceof Promise) {
144 return result.then(nextState => [nextState, patches!, inversePatches!])
145 }
146 return [result, patches!, inversePatches!]
147 }
148
149 createDraft<T extends Objectish>(base: T): Draft<T> {
150 if (!isDraftable(base)) die(8)
151 if (isDraft(base)) base = current(base)
152 const scope = enterScope(this)
153 const proxy = createProxy(this, base, undefined)
154 proxy[DRAFT_STATE].isManual_ = true
155 leaveScope(scope)
156 return proxy as any
157 }
158
159 finishDraft<D extends Draft<any>>(
160 draft: D,
161 patchListener?: PatchListener
162 ): D extends Draft<infer T> ? T : never {
163 const state: ImmerState = draft && (draft as any)[DRAFT_STATE]
164 if (__DEV__) {
165 if (!state || !state.isManual_) die(9)
166 if (state.finalized_) die(10)
167 }
168 const {scope_: scope} = state
169 usePatchesInScope(scope, patchListener)
170 return processResult(undefined, scope)
171 }
172
173 /**
174 * Pass true to automatically freeze all copies created by Immer.
175 *
176 * By default, auto-freezing is enabled.
177 */
178 setAutoFreeze(value: boolean) {
179 this.autoFreeze_ = value
180 }
181
182 /**
183 * Pass true to use the ES2015 `Proxy` class when creating drafts, which is
184 * always faster than using ES5 proxies.
185 *
186 * By default, feature detection is used, so calling this is rarely necessary.
187 */
188 setUseProxies(value: boolean) {
189 if (value && !hasProxies) {
190 die(20)
191 }
192 this.useProxies_ = value
193 }
194
195 applyPatches<T extends Objectish>(base: T, patches: Patch[]): T {
196 // If a patch replaces the entire state, take that replacement as base
197 // before applying patches
198 let i: number
199 for (i = patches.length - 1; i >= 0; i--) {
200 const patch = patches[i]
201 if (patch.path.length === 0 && patch.op === "replace") {
202 base = patch.value
203 break
204 }
205 }
206 // If there was a patch that replaced the entire state, start from the
207 // patch after that.
208 if (i > -1) {
209 patches = patches.slice(i + 1)
210 }
211
212 const applyPatchesImpl = getPlugin("Patches").applyPatches_
213 if (isDraft(base)) {
214 // N.B: never hits if some patch a replacement, patches are never drafts
215 return applyPatchesImpl(base, patches)
216 }
217 // Otherwise, produce a copy of the base state.
218 return this.produce(base, (draft: Drafted) =>
219 applyPatchesImpl(draft, patches)
220 )
221 }
222}
223
224export function createProxy<T extends Objectish>(
225 immer: Immer,
226 value: T,
227 parent?: ImmerState
228): Drafted<T, ImmerState> {
229 // precondition: createProxy should be guarded by isDraftable, so we know we can safely draft
230 const draft: Drafted = isMap(value)
231 ? getPlugin("MapSet").proxyMap_(value, parent)
232 : isSet(value)
233 ? getPlugin("MapSet").proxySet_(value, parent)
234 : immer.useProxies_
235 ? createProxyProxy(value, parent)
236 : getPlugin("ES5").createES5Proxy_(value, parent)
237
238 const scope = parent ? parent.scope_ : getCurrentScope()
239 scope.drafts_.push(draft)
240 return draft
241}
Note: See TracBrowser for help on using the repository browser.