source: node_modules/reselect/src/autotrackMemoize/proxy.ts

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 5.1 KB
RevLine 
[d24f17c]1// Original source:
2// - https://github.com/simonihmig/tracked-redux/blob/master/packages/tracked-redux/src/-private/proxy.ts
3
4import type { Node, Tag } from './tracking'
5import {
6 consumeCollection,
7 consumeTag,
8 createTag,
9 dirtyCollection,
10 dirtyTag
11} from './tracking'
12
13export const REDUX_PROXY_LABEL = Symbol()
14
15let nextId = 0
16
17const proto = Object.getPrototypeOf({})
18
19class ObjectTreeNode<T extends Record<string, unknown>> implements Node<T> {
20 proxy: T = new Proxy(this, objectProxyHandler) as unknown as T
21 tag = createTag()
22 tags = {} as Record<string, Tag>
23 children = {} as Record<string, Node>
24 collectionTag = null
25 id = nextId++
26
27 constructor(public value: T) {
28 this.value = value
29 this.tag.value = value
30 }
31}
32
33const objectProxyHandler = {
34 get(node: Node, key: string | symbol): unknown {
35 function calculateResult() {
36 const { value } = node
37
38 const childValue = Reflect.get(value, key)
39
40 if (typeof key === 'symbol') {
41 return childValue
42 }
43
44 if (key in proto) {
45 return childValue
46 }
47
48 if (typeof childValue === 'object' && childValue !== null) {
49 let childNode = node.children[key]
50
51 if (childNode === undefined) {
52 childNode = node.children[key] = createNode(childValue)
53 }
54
55 if (childNode.tag) {
56 consumeTag(childNode.tag)
57 }
58
59 return childNode.proxy
60 } else {
61 let tag = node.tags[key]
62
63 if (tag === undefined) {
64 tag = node.tags[key] = createTag()
65 tag.value = childValue
66 }
67
68 consumeTag(tag)
69
70 return childValue
71 }
72 }
73 const res = calculateResult()
74 return res
75 },
76
77 ownKeys(node: Node): ArrayLike<string | symbol> {
78 consumeCollection(node)
79 return Reflect.ownKeys(node.value)
80 },
81
82 getOwnPropertyDescriptor(
83 node: Node,
84 prop: string | symbol
85 ): PropertyDescriptor | undefined {
86 return Reflect.getOwnPropertyDescriptor(node.value, prop)
87 },
88
89 has(node: Node, prop: string | symbol): boolean {
90 return Reflect.has(node.value, prop)
91 }
92}
93
94class ArrayTreeNode<T extends Array<unknown>> implements Node<T> {
95 proxy: T = new Proxy([this], arrayProxyHandler) as unknown as T
96 tag = createTag()
97 tags = {}
98 children = {}
99 collectionTag = null
100 id = nextId++
101
102 constructor(public value: T) {
103 this.value = value
104 this.tag.value = value
105 }
106}
107
108const arrayProxyHandler = {
109 get([node]: [Node], key: string | symbol): unknown {
110 if (key === 'length') {
111 consumeCollection(node)
112 }
113
114 return objectProxyHandler.get(node, key)
115 },
116
117 ownKeys([node]: [Node]): ArrayLike<string | symbol> {
118 return objectProxyHandler.ownKeys(node)
119 },
120
121 getOwnPropertyDescriptor(
122 [node]: [Node],
123 prop: string | symbol
124 ): PropertyDescriptor | undefined {
125 return objectProxyHandler.getOwnPropertyDescriptor(node, prop)
126 },
127
128 has([node]: [Node], prop: string | symbol): boolean {
129 return objectProxyHandler.has(node, prop)
130 }
131}
132
133export function createNode<T extends Array<unknown> | Record<string, unknown>>(
134 value: T
135): Node<T> {
136 if (Array.isArray(value)) {
137 return new ArrayTreeNode(value)
138 }
139
140 return new ObjectTreeNode(value) as Node<T>
141}
142
143const keysMap = new WeakMap<
144 Array<unknown> | Record<string, unknown>,
145 Set<string>
146>()
147
148export function updateNode<T extends Array<unknown> | Record<string, unknown>>(
149 node: Node<T>,
150 newValue: T
151): void {
152 const { value, tags, children } = node
153
154 node.value = newValue
155
156 if (
157 Array.isArray(value) &&
158 Array.isArray(newValue) &&
159 value.length !== newValue.length
160 ) {
161 dirtyCollection(node)
162 } else {
163 if (value !== newValue) {
164 let oldKeysSize = 0
165 let newKeysSize = 0
166 let anyKeysAdded = false
167
168 for (const _key in value) {
169 oldKeysSize++
170 }
171
172 for (const key in newValue) {
173 newKeysSize++
174 if (!(key in value)) {
175 anyKeysAdded = true
176 break
177 }
178 }
179
180 const isDifferent = anyKeysAdded || oldKeysSize !== newKeysSize
181
182 if (isDifferent) {
183 dirtyCollection(node)
184 }
185 }
186 }
187
188 for (const key in tags) {
189 const childValue = (value as Record<string, unknown>)[key]
190 const newChildValue = (newValue as Record<string, unknown>)[key]
191
192 if (childValue !== newChildValue) {
193 dirtyCollection(node)
194 dirtyTag(tags[key], newChildValue)
195 }
196
197 if (typeof newChildValue === 'object' && newChildValue !== null) {
198 delete tags[key]
199 }
200 }
201
202 for (const key in children) {
203 const childNode = children[key]
204 const newChildValue = (newValue as Record<string, unknown>)[key]
205
206 const childValue = childNode.value
207
208 if (childValue === newChildValue) {
209 continue
210 } else if (typeof newChildValue === 'object' && newChildValue !== null) {
211 updateNode(childNode, newChildValue as Record<string, unknown>)
212 } else {
213 deleteNode(childNode)
214 delete children[key]
215 }
216 }
217}
218
219function deleteNode(node: Node): void {
220 if (node.tag) {
221 dirtyTag(node.tag, null)
222 }
223 dirtyCollection(node)
224 for (const key in node.tags) {
225 dirtyTag(node.tags[key], null)
226 }
227 for (const key in node.children) {
228 deleteNode(node.children[key])
229 }
230}
Note: See TracBrowser for help on using the repository browser.