1 |
|
2 | import {
|
3 | ImmerState,
|
4 | AnyMap,
|
5 | AnySet,
|
6 | MapState,
|
7 | SetState,
|
8 | DRAFT_STATE,
|
9 | getCurrentScope,
|
10 | latest,
|
11 | isDraftable,
|
12 | createProxy,
|
13 | loadPlugin,
|
14 | markChanged,
|
15 | die,
|
16 | ArchType,
|
17 | each
|
18 | } from "../internal"
|
19 |
|
20 | export function enableMapSet() {
|
21 | class DraftMap extends Map {
|
22 | [DRAFT_STATE]: MapState
|
23 |
|
24 | constructor(target: AnyMap, parent?: ImmerState) {
|
25 | super()
|
26 | this[DRAFT_STATE] = {
|
27 | type_: ArchType.Map,
|
28 | parent_: parent,
|
29 | scope_: parent ? parent.scope_ : getCurrentScope()!,
|
30 | modified_: false,
|
31 | finalized_: false,
|
32 | copy_: undefined,
|
33 | assigned_: undefined,
|
34 | base_: target,
|
35 | draft_: this as any,
|
36 | isManual_: false,
|
37 | revoked_: false
|
38 | }
|
39 | }
|
40 |
|
41 | get size(): number {
|
42 | return latest(this[DRAFT_STATE]).size
|
43 | }
|
44 |
|
45 | has(key: any): boolean {
|
46 | return latest(this[DRAFT_STATE]).has(key)
|
47 | }
|
48 |
|
49 | set(key: any, value: any) {
|
50 | const state: MapState = this[DRAFT_STATE]
|
51 | assertUnrevoked(state)
|
52 | if (!latest(state).has(key) || latest(state).get(key) !== value) {
|
53 | prepareMapCopy(state)
|
54 | markChanged(state)
|
55 | state.assigned_!.set(key, true)
|
56 | state.copy_!.set(key, value)
|
57 | state.assigned_!.set(key, true)
|
58 | }
|
59 | return this
|
60 | }
|
61 |
|
62 | delete(key: any): boolean {
|
63 | if (!this.has(key)) {
|
64 | return false
|
65 | }
|
66 |
|
67 | const state: MapState = this[DRAFT_STATE]
|
68 | assertUnrevoked(state)
|
69 | prepareMapCopy(state)
|
70 | markChanged(state)
|
71 | if (state.base_.has(key)) {
|
72 | state.assigned_!.set(key, false)
|
73 | } else {
|
74 | state.assigned_!.delete(key)
|
75 | }
|
76 | state.copy_!.delete(key)
|
77 | return true
|
78 | }
|
79 |
|
80 | clear() {
|
81 | const state: MapState = this[DRAFT_STATE]
|
82 | assertUnrevoked(state)
|
83 | if (latest(state).size) {
|
84 | prepareMapCopy(state)
|
85 | markChanged(state)
|
86 | state.assigned_ = new Map()
|
87 | each(state.base_, key => {
|
88 | state.assigned_!.set(key, false)
|
89 | })
|
90 | state.copy_!.clear()
|
91 | }
|
92 | }
|
93 |
|
94 | forEach(cb: (value: any, key: any, self: any) => void, thisArg?: any) {
|
95 | const state: MapState = this[DRAFT_STATE]
|
96 | latest(state).forEach((_value: any, key: any, _map: any) => {
|
97 | cb.call(thisArg, this.get(key), key, this)
|
98 | })
|
99 | }
|
100 |
|
101 | get(key: any): any {
|
102 | const state: MapState = this[DRAFT_STATE]
|
103 | assertUnrevoked(state)
|
104 | const value = latest(state).get(key)
|
105 | if (state.finalized_ || !isDraftable(value)) {
|
106 | return value
|
107 | }
|
108 | if (value !== state.base_.get(key)) {
|
109 | return value
|
110 | }
|
111 |
|
112 | const draft = createProxy(value, state)
|
113 | prepareMapCopy(state)
|
114 | state.copy_!.set(key, draft)
|
115 | return draft
|
116 | }
|
117 |
|
118 | keys(): IterableIterator<any> {
|
119 | return latest(this[DRAFT_STATE]).keys()
|
120 | }
|
121 |
|
122 | values(): IterableIterator<any> {
|
123 | const iterator = this.keys()
|
124 | return {
|
125 | [Symbol.iterator]: () => this.values(),
|
126 | next: () => {
|
127 | const r = iterator.next()
|
128 |
|
129 | if (r.done) return r
|
130 | const value = this.get(r.value)
|
131 | return {
|
132 | done: false,
|
133 | value
|
134 | }
|
135 | }
|
136 | } as any
|
137 | }
|
138 |
|
139 | entries(): IterableIterator<[any, any]> {
|
140 | const iterator = this.keys()
|
141 | return {
|
142 | [Symbol.iterator]: () => this.entries(),
|
143 | next: () => {
|
144 | const r = iterator.next()
|
145 |
|
146 | if (r.done) return r
|
147 | const value = this.get(r.value)
|
148 | return {
|
149 | done: false,
|
150 | value: [r.value, value]
|
151 | }
|
152 | }
|
153 | } as any
|
154 | }
|
155 |
|
156 | [Symbol.iterator]() {
|
157 | return this.entries()
|
158 | }
|
159 | }
|
160 |
|
161 | function proxyMap_<T extends AnyMap>(target: T, parent?: ImmerState): T {
|
162 |
|
163 | return new DraftMap(target, parent)
|
164 | }
|
165 |
|
166 | function prepareMapCopy(state: MapState) {
|
167 | if (!state.copy_) {
|
168 | state.assigned_ = new Map()
|
169 | state.copy_ = new Map(state.base_)
|
170 | }
|
171 | }
|
172 |
|
173 | class DraftSet extends Set {
|
174 | [DRAFT_STATE]: SetState
|
175 | constructor(target: AnySet, parent?: ImmerState) {
|
176 | super()
|
177 | this[DRAFT_STATE] = {
|
178 | type_: ArchType.Set,
|
179 | parent_: parent,
|
180 | scope_: parent ? parent.scope_ : getCurrentScope()!,
|
181 | modified_: false,
|
182 | finalized_: false,
|
183 | copy_: undefined,
|
184 | base_: target,
|
185 | draft_: this,
|
186 | drafts_: new Map(),
|
187 | revoked_: false,
|
188 | isManual_: false
|
189 | }
|
190 | }
|
191 |
|
192 | get size(): number {
|
193 | return latest(this[DRAFT_STATE]).size
|
194 | }
|
195 |
|
196 | has(value: any): boolean {
|
197 | const state: SetState = this[DRAFT_STATE]
|
198 | assertUnrevoked(state)
|
199 |
|
200 | if (!state.copy_) {
|
201 | return state.base_.has(value)
|
202 | }
|
203 | if (state.copy_.has(value)) return true
|
204 | if (state.drafts_.has(value) && state.copy_.has(state.drafts_.get(value)))
|
205 | return true
|
206 | return false
|
207 | }
|
208 |
|
209 | add(value: any): any {
|
210 | const state: SetState = this[DRAFT_STATE]
|
211 | assertUnrevoked(state)
|
212 | if (!this.has(value)) {
|
213 | prepareSetCopy(state)
|
214 | markChanged(state)
|
215 | state.copy_!.add(value)
|
216 | }
|
217 | return this
|
218 | }
|
219 |
|
220 | delete(value: any): any {
|
221 | if (!this.has(value)) {
|
222 | return false
|
223 | }
|
224 |
|
225 | const state: SetState = this[DRAFT_STATE]
|
226 | assertUnrevoked(state)
|
227 | prepareSetCopy(state)
|
228 | markChanged(state)
|
229 | return (
|
230 | state.copy_!.delete(value) ||
|
231 | (state.drafts_.has(value)
|
232 | ? state.copy_!.delete(state.drafts_.get(value))
|
233 | : false)
|
234 | )
|
235 | }
|
236 |
|
237 | clear() {
|
238 | const state: SetState = this[DRAFT_STATE]
|
239 | assertUnrevoked(state)
|
240 | if (latest(state).size) {
|
241 | prepareSetCopy(state)
|
242 | markChanged(state)
|
243 | state.copy_!.clear()
|
244 | }
|
245 | }
|
246 |
|
247 | values(): IterableIterator<any> {
|
248 | const state: SetState = this[DRAFT_STATE]
|
249 | assertUnrevoked(state)
|
250 | prepareSetCopy(state)
|
251 | return state.copy_!.values()
|
252 | }
|
253 |
|
254 | entries(): IterableIterator<[any, any]> {
|
255 | const state: SetState = this[DRAFT_STATE]
|
256 | assertUnrevoked(state)
|
257 | prepareSetCopy(state)
|
258 | return state.copy_!.entries()
|
259 | }
|
260 |
|
261 | keys(): IterableIterator<any> {
|
262 | return this.values()
|
263 | }
|
264 |
|
265 | [Symbol.iterator]() {
|
266 | return this.values()
|
267 | }
|
268 |
|
269 | forEach(cb: any, thisArg?: any) {
|
270 | const iterator = this.values()
|
271 | let result = iterator.next()
|
272 | while (!result.done) {
|
273 | cb.call(thisArg, result.value, result.value, this)
|
274 | result = iterator.next()
|
275 | }
|
276 | }
|
277 | }
|
278 | function proxySet_<T extends AnySet>(target: T, parent?: ImmerState): T {
|
279 |
|
280 | return new DraftSet(target, parent)
|
281 | }
|
282 |
|
283 | function prepareSetCopy(state: SetState) {
|
284 | if (!state.copy_) {
|
285 |
|
286 | state.copy_ = new Set()
|
287 | state.base_.forEach(value => {
|
288 | if (isDraftable(value)) {
|
289 | const draft = createProxy(value, state)
|
290 | state.drafts_.set(value, draft)
|
291 | state.copy_!.add(draft)
|
292 | } else {
|
293 | state.copy_!.add(value)
|
294 | }
|
295 | })
|
296 | }
|
297 | }
|
298 |
|
299 | function assertUnrevoked(state: any ) {
|
300 | if (state.revoked_) die(3, JSON.stringify(latest(state)))
|
301 | }
|
302 |
|
303 | loadPlugin("MapSet", {proxyMap_, proxySet_})
|
304 | }
|