glass-easel 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/.eslintignore +4 -0
  2. package/dist/glass_easel.all.js +2 -2
  3. package/dist/glass_easel.all.js.map +1 -1
  4. package/dist/glass_easel.d.ts +2546 -0
  5. package/dist/glass_easel.domlike.global.js +2 -2
  6. package/dist/glass_easel.domlike.global.js.map +1 -1
  7. package/guide/zh_CN/appendix/backend_protocol.md +164 -74
  8. package/jest.unit.config.js +7 -4
  9. package/package.json +7 -5
  10. package/rollup.config.ts +101 -0
  11. package/src/backend/backend_protocol.ts +26 -345
  12. package/src/backend/composed_backend_protocol.ts +10 -264
  13. package/src/backend/current_window_backend_context.ts +478 -0
  14. package/src/backend/domlike_backend_protocol.ts +9 -498
  15. package/src/backend/empty_backend.ts +265 -0
  16. package/src/backend/empty_composed_backend.ts +245 -0
  17. package/src/backend/index.ts +18 -0
  18. package/src/backend/suggested_backend_protocol.ts +62 -11
  19. package/src/behavior.ts +131 -442
  20. package/src/class_list.ts +328 -222
  21. package/src/component.ts +349 -184
  22. package/src/component_params.ts +54 -25
  23. package/src/component_space.ts +36 -15
  24. package/src/data_path.ts +22 -34
  25. package/src/data_proxy.ts +334 -27
  26. package/src/devtool.ts +89 -0
  27. package/src/element.ts +667 -285
  28. package/src/element_iterator.ts +9 -4
  29. package/src/event.ts +39 -20
  30. package/src/external_shadow_tree.ts +2 -2
  31. package/src/func_arr.ts +55 -101
  32. package/src/global_options.ts +18 -42
  33. package/src/index.ts +72 -73
  34. package/src/mutation_observer.ts +10 -7
  35. package/src/native_node.ts +79 -35
  36. package/src/node.ts +23 -30
  37. package/src/relation.ts +101 -12
  38. package/src/render.ts +5 -3
  39. package/src/selector.ts +9 -7
  40. package/src/shadow_root.ts +96 -240
  41. package/src/template_engine.ts +10 -7
  42. package/src/text_node.ts +43 -19
  43. package/src/tmpl/index.ts +38 -30
  44. package/src/tmpl/native_rendering.ts +14 -21
  45. package/src/tmpl/proc_gen_wrapper.ts +189 -151
  46. package/src/tmpl/proc_gen_wrapper_dom.ts +10 -8
  47. package/src/tmpl/range_list_diff.ts +30 -9
  48. package/src/trait_behaviors.ts +1 -1
  49. package/src/type_symbol.ts +36 -0
  50. package/src/virtual_node.ts +26 -13
  51. package/src/warning.ts +119 -0
  52. package/tests/base/composed_backend.ts +143 -142
  53. package/tests/base/env.ts +76 -3
  54. package/tests/base/match.ts +24 -22
  55. package/tests/base/shadow_backend.ts +917 -0
  56. package/tests/core/backend.test.ts +53 -1
  57. package/tests/core/behavior.test.ts +1 -1
  58. package/tests/core/data_update.test.ts +135 -0
  59. package/tests/core/misc.test.ts +91 -21
  60. package/tests/core/placeholder.test.ts +66 -25
  61. package/tests/core/slot.test.ts +161 -23
  62. package/tests/legacy/component.test.js +152 -78
  63. package/tests/legacy/data_path.test.js +19 -19
  64. package/tests/legacy/data_proxy.test.js +14 -7
  65. package/tests/legacy/external.test.js +8 -8
  66. package/tests/legacy/mutation_observer.test.js +7 -5
  67. package/tests/legacy/slot.test.js +2 -3
  68. package/tests/legacy/virtual.test.js +258 -25
  69. package/tests/tmpl/binding_map.test.ts +201 -195
  70. package/tests/tmpl/event.test.ts +138 -23
  71. package/tests/tmpl/expression.test.ts +15 -15
  72. package/tests/tmpl/lvalue.test.ts +101 -0
  73. package/tests/tmpl/structure.test.ts +486 -43
  74. package/tests/types/chaining.test.ts +27 -30
  75. package/tests/types/createElement.test.ts +5 -4
  76. package/tests/types/definition.test.ts +11 -14
  77. package/tsconfig.json +1 -3
  78. package/dist/glass_easel.all.d.ts +0 -1
  79. package/dist/glass_easel.domlike.global.d.ts +0 -1
  80. package/dist/index.d.ts +0 -1
  81. package/dist/index.js +0 -1
  82. package/dist/types/src/backend/backend_protocol.d.ts +0 -141
  83. package/dist/types/src/backend/backend_protocol.d.ts.map +0 -1
  84. package/dist/types/src/backend/composed_backend_protocol.d.ts +0 -102
  85. package/dist/types/src/backend/composed_backend_protocol.d.ts.map +0 -1
  86. package/dist/types/src/backend/domlike_backend_protocol.d.ts +0 -100
  87. package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +0 -1
  88. package/dist/types/src/backend/mode.d.ts +0 -65
  89. package/dist/types/src/backend/mode.d.ts.map +0 -1
  90. package/dist/types/src/backend/suggested_backend_protocol.d.ts +0 -30
  91. package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +0 -1
  92. package/dist/types/src/behavior.d.ts +0 -434
  93. package/dist/types/src/behavior.d.ts.map +0 -1
  94. package/dist/types/src/class_list.d.ts +0 -83
  95. package/dist/types/src/class_list.d.ts.map +0 -1
  96. package/dist/types/src/component.d.ts +0 -325
  97. package/dist/types/src/component.d.ts.map +0 -1
  98. package/dist/types/src/component_params.d.ts +0 -236
  99. package/dist/types/src/component_params.d.ts.map +0 -1
  100. package/dist/types/src/component_space.d.ts +0 -211
  101. package/dist/types/src/component_space.d.ts.map +0 -1
  102. package/dist/types/src/data_path.d.ts +0 -5
  103. package/dist/types/src/data_path.d.ts.map +0 -1
  104. package/dist/types/src/data_proxy.d.ts +0 -107
  105. package/dist/types/src/data_proxy.d.ts.map +0 -1
  106. package/dist/types/src/data_utils.d.ts +0 -3
  107. package/dist/types/src/data_utils.d.ts.map +0 -1
  108. package/dist/types/src/element.d.ts +0 -365
  109. package/dist/types/src/element.d.ts.map +0 -1
  110. package/dist/types/src/element_iterator.d.ts +0 -43
  111. package/dist/types/src/element_iterator.d.ts.map +0 -1
  112. package/dist/types/src/event.d.ts +0 -105
  113. package/dist/types/src/event.d.ts.map +0 -1
  114. package/dist/types/src/external_shadow_tree.d.ts +0 -20
  115. package/dist/types/src/external_shadow_tree.d.ts.map +0 -1
  116. package/dist/types/src/func_arr.d.ts +0 -39
  117. package/dist/types/src/func_arr.d.ts.map +0 -1
  118. package/dist/types/src/global_options.d.ts +0 -120
  119. package/dist/types/src/global_options.d.ts.map +0 -1
  120. package/dist/types/src/index.d.ts +0 -43
  121. package/dist/types/src/index.d.ts.map +0 -1
  122. package/dist/types/src/mutation_observer.d.ts +0 -79
  123. package/dist/types/src/mutation_observer.d.ts.map +0 -1
  124. package/dist/types/src/native_node.d.ts +0 -11
  125. package/dist/types/src/native_node.d.ts.map +0 -1
  126. package/dist/types/src/node.d.ts +0 -48
  127. package/dist/types/src/node.d.ts.map +0 -1
  128. package/dist/types/src/relation.d.ts +0 -46
  129. package/dist/types/src/relation.d.ts.map +0 -1
  130. package/dist/types/src/render.d.ts +0 -3
  131. package/dist/types/src/render.d.ts.map +0 -1
  132. package/dist/types/src/selector.d.ts +0 -32
  133. package/dist/types/src/selector.d.ts.map +0 -1
  134. package/dist/types/src/shadow_root.d.ts +0 -174
  135. package/dist/types/src/shadow_root.d.ts.map +0 -1
  136. package/dist/types/src/template_engine.d.ts +0 -56
  137. package/dist/types/src/template_engine.d.ts.map +0 -1
  138. package/dist/types/src/text_node.d.ts +0 -44
  139. package/dist/types/src/text_node.d.ts.map +0 -1
  140. package/dist/types/src/tmpl/index.d.ts +0 -19
  141. package/dist/types/src/tmpl/index.d.ts.map +0 -1
  142. package/dist/types/src/tmpl/native_rendering.d.ts +0 -45
  143. package/dist/types/src/tmpl/native_rendering.d.ts.map +0 -1
  144. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +0 -89
  145. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +0 -1
  146. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +0 -50
  147. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +0 -1
  148. package/dist/types/src/tmpl/range_list_diff.d.ts +0 -19
  149. package/dist/types/src/tmpl/range_list_diff.d.ts.map +0 -1
  150. package/dist/types/src/trait_behaviors.d.ts +0 -38
  151. package/dist/types/src/trait_behaviors.d.ts.map +0 -1
  152. package/dist/types/src/virtual_node.d.ts +0 -10
  153. package/dist/types/src/virtual_node.d.ts.map +0 -1
  154. package/dist/types/tests/backend/domlike.test.d.ts +0 -2
  155. package/dist/types/tests/backend/domlike.test.d.ts.map +0 -1
  156. package/dist/types/tests/base/composed_backend.d.ts +0 -123
  157. package/dist/types/tests/base/composed_backend.d.ts.map +0 -1
  158. package/dist/types/tests/base/env.d.ts +0 -30
  159. package/dist/types/tests/base/env.d.ts.map +0 -1
  160. package/dist/types/tests/base/match.d.ts +0 -9
  161. package/dist/types/tests/base/match.d.ts.map +0 -1
  162. package/dist/types/tests/core/backend.test.d.ts +0 -2
  163. package/dist/types/tests/core/backend.test.d.ts.map +0 -1
  164. package/dist/types/tests/core/behavior.test.d.ts +0 -2
  165. package/dist/types/tests/core/behavior.test.d.ts.map +0 -1
  166. package/dist/types/tests/core/component_space.test.d.ts +0 -2
  167. package/dist/types/tests/core/component_space.test.d.ts.map +0 -1
  168. package/dist/types/tests/core/data_update.test.d.ts +0 -2
  169. package/dist/types/tests/core/data_update.test.d.ts.map +0 -1
  170. package/dist/types/tests/core/misc.test.d.ts +0 -2
  171. package/dist/types/tests/core/misc.test.d.ts.map +0 -1
  172. package/dist/types/tests/core/placeholder.test.d.ts +0 -2
  173. package/dist/types/tests/core/placeholder.test.d.ts.map +0 -1
  174. package/dist/types/tests/core/slot.test.d.ts +0 -2
  175. package/dist/types/tests/core/slot.test.d.ts.map +0 -1
  176. package/dist/types/tests/core/trait_behaviors.test.d.ts +0 -2
  177. package/dist/types/tests/core/trait_behaviors.test.d.ts.map +0 -1
  178. package/dist/types/tests/tmpl/binding_map.test.d.ts +0 -2
  179. package/dist/types/tests/tmpl/binding_map.test.d.ts.map +0 -1
  180. package/dist/types/tests/tmpl/event.test.d.ts +0 -2
  181. package/dist/types/tests/tmpl/event.test.d.ts.map +0 -1
  182. package/dist/types/tests/tmpl/expression.test.d.ts +0 -2
  183. package/dist/types/tests/tmpl/expression.test.d.ts.map +0 -1
  184. package/dist/types/tests/tmpl/lvalue.test.d.ts +0 -2
  185. package/dist/types/tests/tmpl/lvalue.test.d.ts.map +0 -1
  186. package/dist/types/tests/tmpl/native_rendering.test.d.ts +0 -2
  187. package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +0 -1
  188. package/dist/types/tests/tmpl/structure.test.d.ts +0 -2
  189. package/dist/types/tests/tmpl/structure.test.d.ts.map +0 -1
  190. package/dist/types/tests/types/chaining.test.d.ts +0 -2
  191. package/dist/types/tests/types/chaining.test.d.ts.map +0 -1
  192. package/dist/types/tests/types/createElement.test.d.ts +0 -2
  193. package/dist/types/tests/types/createElement.test.d.ts.map +0 -1
  194. package/dist/types/tests/types/definition.test.d.ts +0 -2
  195. package/dist/types/tests/types/definition.test.d.ts.map +0 -1
  196. package/webpack.config.js +0 -275
  197. /package/src/backend/{mode.ts → shared.ts} +0 -0
package/src/data_proxy.ts CHANGED
@@ -1,21 +1,303 @@
1
- import { safeCallback, triggerWarning } from './func_arr'
2
- import { convertValueToType, PropertyDefinition } from './behavior'
1
+ import { type GeneralComponent } from './component'
2
+ import {
3
+ type ComponentInstance,
4
+ type DataList,
5
+ type DataWithPropertyValues,
6
+ type MethodList,
7
+ type PropertyList,
8
+ } from './component_params'
9
+ import { type DataPath, type MultiPaths } from './data_path'
3
10
  import { deepCopy, simpleDeepCopy } from './data_utils'
4
- import { DataPath, MultiPaths } from './data_path'
11
+ import { safeCallback } from './func_arr'
5
12
  import { DeepCopyKind } from './global_options'
6
13
  import { MutationObserverTarget } from './mutation_observer'
7
- import {
8
- DataList,
9
- PropertyList,
10
- MethodList,
11
- ComponentInstance,
12
- DataWithPropertyValues,
13
- } from './component_params'
14
- import { GeneralComponent } from './component'
14
+ import { triggerWarning } from './warning'
15
15
 
16
16
  // eslint-disable-next-line @typescript-eslint/unbound-method
17
17
  const hasOwnProperty = Object.prototype.hasOwnProperty
18
18
 
19
+ export const enum NormalizedPropertyType {
20
+ Invalid = 'invalid',
21
+ Any = 'any',
22
+ String = 'string',
23
+ Number = 'number',
24
+ Boolean = 'boolean',
25
+ Object = 'object',
26
+ Array = 'array',
27
+ Function = 'function',
28
+ }
29
+
30
+ export type PropertyDefinition = {
31
+ type: NormalizedPropertyType
32
+ optionalTypes: NormalizedPropertyType[] | null
33
+ value: unknown
34
+ default: (() => unknown) | undefined
35
+ observer: ((newValue: unknown, oldValue: unknown) => void) | null
36
+ comparer: ((newValue: unknown, oldValue: unknown) => boolean) | null
37
+ reflectIdPrefix: boolean
38
+ }
39
+
40
+ export const shallowMerge = (dest: { [key: string]: unknown }, src: { [key: string]: unknown }) => {
41
+ const keys = Object.keys(src)
42
+ for (let i = 0; i < keys.length; i += 1) {
43
+ const key = keys[i]!
44
+ if (Object.prototype.hasOwnProperty.call(dest, key)) {
45
+ if (key[0] === '_') {
46
+ triggerWarning(`data field "${key}" from different behaviors is overriding or merging.`)
47
+ }
48
+ if (
49
+ typeof dest[key] === 'object' &&
50
+ typeof src[key] === 'object' &&
51
+ src[key] !== null &&
52
+ !Array.isArray(src[key])
53
+ ) {
54
+ if (Array.isArray(dest[key])) {
55
+ dest[key] = (dest[key] as DataValue[]).slice()
56
+ } else {
57
+ const oldDest = dest[key] as { [key: string]: DataValue }
58
+ const newDest = {} as { [key: string]: DataValue }
59
+ const subKeys = Object.keys(oldDest)
60
+ for (let i = 0; i < subKeys.length; i += 1) {
61
+ const subKey = subKeys[i]!
62
+ newDest[subKey] = oldDest[subKey]
63
+ }
64
+ dest[key] = newDest
65
+ }
66
+ shallowMerge(
67
+ dest[key] as { [key: string]: unknown },
68
+ src[key] as { [key: string]: unknown },
69
+ )
70
+ } else {
71
+ dest[key] = src[key]
72
+ }
73
+ } else {
74
+ dest[key] = src[key]
75
+ }
76
+ }
77
+ }
78
+
79
+ export const normalizePropertyTypeShortHand = (propDef: unknown): PropertyDefinition | null => {
80
+ if (propDef === NormalizedPropertyType.String || propDef === String) {
81
+ return {
82
+ type: NormalizedPropertyType.String,
83
+ optionalTypes: null,
84
+ value: '',
85
+ default: undefined,
86
+ observer: null,
87
+ comparer: null,
88
+ reflectIdPrefix: false,
89
+ }
90
+ }
91
+ if (propDef === NormalizedPropertyType.Number || propDef === Number) {
92
+ return {
93
+ type: NormalizedPropertyType.Number,
94
+ optionalTypes: null,
95
+ value: 0,
96
+ default: undefined,
97
+ observer: null,
98
+ comparer: null,
99
+ reflectIdPrefix: false,
100
+ }
101
+ }
102
+ if (propDef === NormalizedPropertyType.Boolean || propDef === Boolean) {
103
+ return {
104
+ type: NormalizedPropertyType.Boolean,
105
+ optionalTypes: null,
106
+ value: false,
107
+ default: undefined,
108
+ observer: null,
109
+ comparer: null,
110
+ reflectIdPrefix: false,
111
+ }
112
+ }
113
+ if (propDef === NormalizedPropertyType.Object || propDef === Object) {
114
+ return {
115
+ type: NormalizedPropertyType.Object,
116
+ optionalTypes: null,
117
+ value: null,
118
+ default: undefined,
119
+ observer: null,
120
+ comparer: null,
121
+ reflectIdPrefix: false,
122
+ }
123
+ }
124
+ if (propDef === NormalizedPropertyType.Array || propDef === Array) {
125
+ return {
126
+ type: NormalizedPropertyType.Array,
127
+ optionalTypes: null,
128
+ value: [],
129
+ default: undefined,
130
+ observer: null,
131
+ comparer: null,
132
+ reflectIdPrefix: false,
133
+ }
134
+ }
135
+ if (propDef === NormalizedPropertyType.Function || propDef === Function) {
136
+ return {
137
+ type: NormalizedPropertyType.Function,
138
+ optionalTypes: null,
139
+ value() {
140
+ /* empty */
141
+ },
142
+ default: undefined,
143
+ observer: null,
144
+ comparer: null,
145
+ reflectIdPrefix: false,
146
+ }
147
+ }
148
+ if (propDef === NormalizedPropertyType.Any || propDef === null || propDef === undefined) {
149
+ return {
150
+ type: NormalizedPropertyType.Any,
151
+ optionalTypes: null,
152
+ value: null,
153
+ default: undefined,
154
+ observer: null,
155
+ comparer: null,
156
+ reflectIdPrefix: false,
157
+ }
158
+ }
159
+ return null
160
+ }
161
+
162
+ export const normalizePropertyType = (t: unknown): NormalizedPropertyType => {
163
+ if (t === NormalizedPropertyType.String || t === String) {
164
+ return NormalizedPropertyType.String
165
+ }
166
+ if (t === NormalizedPropertyType.Number || t === Number) {
167
+ return NormalizedPropertyType.Number
168
+ }
169
+ if (t === NormalizedPropertyType.Boolean || t === Boolean) {
170
+ return NormalizedPropertyType.Boolean
171
+ }
172
+ if (t === NormalizedPropertyType.Object || t === Object) {
173
+ return NormalizedPropertyType.Object
174
+ }
175
+ if (t === NormalizedPropertyType.Array || t === Array) {
176
+ return NormalizedPropertyType.Array
177
+ }
178
+ if (t === NormalizedPropertyType.Function || t === Function) {
179
+ return NormalizedPropertyType.Function
180
+ }
181
+ if (t === NormalizedPropertyType.Any || t === null || t === undefined) {
182
+ return NormalizedPropertyType.Any
183
+ }
184
+ return NormalizedPropertyType.Invalid
185
+ }
186
+
187
+ export const convertValueToType = (
188
+ value: unknown,
189
+ propName: string,
190
+ prop: PropertyDefinition,
191
+ component: GeneralComponent | null,
192
+ ): unknown => {
193
+ const type = prop.type
194
+ const defaultFn =
195
+ prop.default === undefined
196
+ ? undefined
197
+ : () =>
198
+ safeCallback(
199
+ `Property "${propName}" Default`,
200
+ prop.default!,
201
+ null,
202
+ [],
203
+ component || undefined,
204
+ )
205
+ // try match optional types
206
+ const optionalTypes = prop.optionalTypes
207
+ if (optionalTypes) {
208
+ for (let i = 0; i < optionalTypes.length; i += 1) {
209
+ if (matchTypeWithValue(optionalTypes[i]!, value)) {
210
+ return value
211
+ }
212
+ }
213
+ }
214
+ // for string
215
+ if (type === NormalizedPropertyType.String) {
216
+ if (value === null || value === undefined) {
217
+ triggerWarning(
218
+ `property "${propName}" received type-incompatible value: expected <String> but get null value. Used default value instead.`,
219
+ )
220
+ return defaultFn === undefined ? '' : defaultFn()
221
+ }
222
+ if (typeof value === 'object') {
223
+ triggerWarning(
224
+ `property "${propName}" received type-incompatible value: expected <String> but got object-typed value. Force converted.`,
225
+ )
226
+ }
227
+ return String(value)
228
+ }
229
+ // for number
230
+ if (type === NormalizedPropertyType.Number) {
231
+ // eslint-disable-next-line no-restricted-globals
232
+ if (isFinite(value as number)) return Number(value)
233
+ if (typeof value === 'number') {
234
+ triggerWarning(
235
+ `property "${propName}" received type-incompatible value: expected <Number> but got NaN or Infinity. Used default value instead.`,
236
+ )
237
+ } else {
238
+ triggerWarning(
239
+ `property "${propName}" received type-incompatible value: expected <Number> but got non-number value. Used default value instead.`,
240
+ )
241
+ }
242
+ return defaultFn === undefined ? 0 : defaultFn()
243
+ }
244
+ // for boolean
245
+ if (type === NormalizedPropertyType.Boolean) {
246
+ return !!value
247
+ }
248
+ // for array
249
+ if (type === NormalizedPropertyType.Array) {
250
+ if (Array.isArray(value)) return value as unknown
251
+ triggerWarning(
252
+ `property "${propName}" received type-incompatible value: expected <Array> but got non-array value. Used default value instead.`,
253
+ )
254
+ return defaultFn === undefined ? [] : defaultFn()
255
+ }
256
+ // for object
257
+ if (type === NormalizedPropertyType.Object) {
258
+ if (typeof value === 'object') return value
259
+ triggerWarning(
260
+ `property "${propName}" received type-incompatible value: expected <Object> but got non-object value. Used default value instead.`,
261
+ )
262
+ return defaultFn === undefined ? null : defaultFn()
263
+ }
264
+ // for function
265
+ if (type === NormalizedPropertyType.Function) {
266
+ if (typeof value === 'function') return value
267
+ triggerWarning(
268
+ `property "${propName}" received type-incompatible value: expected <Function> but got non-function value. Used default value instead.`,
269
+ )
270
+ // eslint-disable-next-line func-names
271
+ return defaultFn === undefined
272
+ ? function () {
273
+ /* empty */
274
+ }
275
+ : defaultFn()
276
+ }
277
+ // for any-typed, just return the value and avoid undefined
278
+ if (value === undefined) return defaultFn === undefined ? null : defaultFn()
279
+ return value
280
+ }
281
+
282
+ export const matchTypeWithValue = (type: NormalizedPropertyType, value: any) => {
283
+ if (type === NormalizedPropertyType.String) {
284
+ if (typeof value !== 'string') return false
285
+ } else if (type === NormalizedPropertyType.Number) {
286
+ if (!Number.isFinite(value)) return false
287
+ } else if (type === NormalizedPropertyType.Boolean) {
288
+ if (typeof value !== 'boolean') return false
289
+ } else if (type === NormalizedPropertyType.Object) {
290
+ if (typeof value !== 'object' || Array.isArray(value)) return false
291
+ } else if (type === NormalizedPropertyType.Array) {
292
+ if (typeof value !== 'object' || !Array.isArray(value)) return false
293
+ } else if (type === NormalizedPropertyType.Function) {
294
+ if (typeof value !== 'function') return false
295
+ } else if (value === undefined) {
296
+ return false
297
+ }
298
+ return true
299
+ }
300
+
19
301
  export const enum DeepCopyStrategy {
20
302
  None,
21
303
  Simple,
@@ -28,7 +310,8 @@ export type DataObserver = (...values: unknown[]) => void
28
310
 
29
311
  export type DataChange = DataReplace | DataSplice
30
312
  // for replace
31
- export type DataReplace = [DataPath, DataValue, undefined, undefined]
313
+ export type DataReplace = [DataPath, DataValue, RequireComparer, undefined]
314
+ export type RequireComparer = true | undefined
32
315
  // for splice, numbers are index, removal count
33
316
  export type DataSplice = [DataPath, DataValue[], number, number]
34
317
 
@@ -346,7 +629,7 @@ export class DataGroup<
346
629
  data = simpleDeepCopy(newData)
347
630
  }
348
631
  }
349
- this._$pendingChanges.push([[propName], data, undefined, undefined])
632
+ this._$pendingChanges.push([[propName], data, true, undefined])
350
633
  return true
351
634
  }
352
635
 
@@ -412,8 +695,8 @@ export class DataGroup<
412
695
  // apply changes and collect changing information
413
696
  for (let i = 0; i < changes.length; i += 1) {
414
697
  const change = changes[i]!
415
- const [path, newData, spliceIndex, spliceDel] = change
416
- const isSplice = spliceDel !== undefined
698
+ const [path, newData, maybeSpliceIndex, maybeSpliceDel] = change
699
+ const isSplice = maybeSpliceDel !== undefined
417
700
  const propName = String(path[0])
418
701
  const excluded = pureDataPattern ? pureDataPattern.test(propName) : false
419
702
  const prop: PropertyDefinition | undefined = propFields[propName]
@@ -426,10 +709,11 @@ export class DataGroup<
426
709
  if (isSplice) {
427
710
  if (Array.isArray(oldData)) {
428
711
  const c = change as DataSplice
712
+ const spliceIndex = maybeSpliceIndex as number
429
713
  normalizedSpliceIndex =
430
- spliceIndex! >= 0 && spliceIndex! < oldData.length ? spliceIndex! : oldData.length
714
+ spliceIndex >= 0 && spliceIndex < oldData.length ? spliceIndex : oldData.length
431
715
  c[2] = normalizedSpliceIndex
432
- oldData.splice(normalizedSpliceIndex, spliceDel, ...(newData as typeof oldData))
716
+ oldData.splice(normalizedSpliceIndex, maybeSpliceDel, ...(newData as typeof oldData))
433
717
  } else {
434
718
  triggerWarning(
435
719
  `An array splice change cannot be applied to a non-array value (on path "${path.join(
@@ -439,7 +723,12 @@ export class DataGroup<
439
723
  }
440
724
  filteredData = oldData
441
725
  } else {
442
- filteredData = convertValueToType(newData, propName, prop)
726
+ filteredData = convertValueToType(
727
+ newData,
728
+ propName,
729
+ prop,
730
+ this._$comp as GeneralComponent | null,
731
+ )
443
732
  }
444
733
  if (!excluded) {
445
734
  if (this.innerData) {
@@ -454,7 +743,11 @@ export class DataGroup<
454
743
  } else {
455
744
  change[1] = inserts = simpleDeepCopy(newData as unknown[])
456
745
  }
457
- ;(innerNewData as DataValue[]).splice(normalizedSpliceIndex, spliceDel!, ...inserts)
746
+ ;(innerNewData as DataValue[]).splice(
747
+ normalizedSpliceIndex,
748
+ maybeSpliceDel!,
749
+ ...inserts,
750
+ )
458
751
  } else if (dataDeepCopy === DeepCopyStrategy.SimpleWithRecursion) {
459
752
  change[1] = innerNewData = deepCopy(filteredData, true)
460
753
  } else {
@@ -489,16 +782,25 @@ export class DataGroup<
489
782
  }
490
783
  }
491
784
  }
492
- changed = prop.comparer
493
- ? !!safeCallback(
785
+ let comparerResult: boolean
786
+ if (!isSplice && maybeSpliceIndex === true) {
787
+ if (prop.comparer) {
788
+ change[2] = undefined
789
+ comparerResult = !!safeCallback(
494
790
  'Property Comparer',
495
791
  prop.comparer,
496
792
  comp!,
497
793
  [newData, oldData],
498
794
  comp?.general(),
499
795
  )
500
- : oldData !== filteredData
501
- if (!excluded && changed) {
796
+ } else {
797
+ comparerResult = oldData !== filteredData
798
+ }
799
+ changed = comparerResult
800
+ } else {
801
+ comparerResult = oldData !== filteredData
802
+ }
803
+ if (!excluded && comparerResult) {
502
804
  propChanges.push({
503
805
  propName,
504
806
  prop,
@@ -540,10 +842,11 @@ export class DataGroup<
540
842
  const oldData = (curData as DataList)[curSlice as string]
541
843
  if (Array.isArray(oldData)) {
542
844
  const c = change as DataSplice
845
+ const spliceIndex = maybeSpliceIndex as number
543
846
  normalizedSpliceIndex =
544
- spliceIndex! >= 0 && spliceIndex! < oldData.length ? spliceIndex! : oldData.length
847
+ spliceIndex >= 0 && spliceIndex < oldData.length ? spliceIndex : oldData.length
545
848
  c[2] = normalizedSpliceIndex
546
- oldData.splice(normalizedSpliceIndex, spliceDel, ...(newData as typeof oldData))
849
+ oldData.splice(normalizedSpliceIndex, maybeSpliceDel, ...(newData as typeof oldData))
547
850
  } else {
548
851
  triggerWarning(
549
852
  `An array splice change cannot be applied to a non-array value (on path "${path.join(
@@ -594,7 +897,11 @@ export class DataGroup<
594
897
  } else {
595
898
  change[1] = inserts = simpleDeepCopy(newData as unknown[])
596
899
  }
597
- ;(innerNewData as DataValue[]).splice(normalizedSpliceIndex, spliceDel!, ...inserts)
900
+ ;(innerNewData as DataValue[]).splice(
901
+ normalizedSpliceIndex,
902
+ maybeSpliceDel!,
903
+ ...inserts,
904
+ )
598
905
  } else if (dataDeepCopy === DeepCopyStrategy.SimpleWithRecursion) {
599
906
  change[1] = innerNewData = deepCopy(newData, true)
600
907
  } else {
@@ -650,7 +957,7 @@ export class DataGroup<
650
957
  prop.observer,
651
958
  comp.getMethodCaller() as any,
652
959
  [newValue, oldValue],
653
- comp as unknown as GeneralComponent,
960
+ comp,
654
961
  )
655
962
  }
656
963
  if (comp._$mutationObserverTarget) {
package/src/devtool.ts ADDED
@@ -0,0 +1,89 @@
1
+ import { type AnyComponent, type GeneralComponent } from './component'
2
+ import { ENV, globalOptions } from './global_options'
3
+
4
+ export type DevtoolInterface = {
5
+ now: () => number
6
+ addTimelineEvent: (time: number, type: string, data?: Record<string, unknown>) => void
7
+ addTimelineComponentEvent: (
8
+ time: number,
9
+ type: string,
10
+ component: GeneralComponent,
11
+ data?: Record<string, unknown>,
12
+ ) => void
13
+ addTimelinePerformanceMeasureStart: (
14
+ time: number,
15
+ type: string,
16
+ component: GeneralComponent | string | null,
17
+ data?: Record<string, unknown>,
18
+ ) => void
19
+ addTimelinePerformanceMeasureEnd: (time: number) => void
20
+ addTimelineBackendWaterfall: (
21
+ type: string,
22
+ times: [
23
+ number, // pending time
24
+ number, // start time
25
+ number, // end time
26
+ ],
27
+ data?: Record<string, unknown>,
28
+ ) => void
29
+ }
30
+
31
+ let defaultDevtools: DevtoolInterface | null | undefined
32
+
33
+ export const getDevtool = (): DevtoolInterface | null | undefined => {
34
+ if (!ENV.DEV) return null
35
+ if (defaultDevtools === undefined) {
36
+ defaultDevtools =
37
+ globalOptions.devtool ||
38
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
39
+ ((globalThis as any).__glass_easel_devtool__ as DevtoolInterface | undefined)
40
+ }
41
+ return defaultDevtools
42
+ }
43
+
44
+ export const performanceMeasureStart = (
45
+ type: string,
46
+ comp: AnyComponent | null = null,
47
+ data?: Record<string, unknown>,
48
+ ) => {
49
+ getDevtool()?.addTimelinePerformanceMeasureStart(getDevtool()!.now(), type, comp, data)
50
+ }
51
+
52
+ export const performanceMeasureEnd = () => {
53
+ getDevtool()?.addTimelinePerformanceMeasureEnd(getDevtool()!.now())
54
+ }
55
+
56
+ export const performanceMeasureRenderWaterfall = (
57
+ type: string,
58
+ waterfallType: string,
59
+ comp: AnyComponent,
60
+ render: () => void,
61
+ ) => {
62
+ const devtool = getDevtool()
63
+ if (!devtool) {
64
+ render()
65
+ return
66
+ }
67
+
68
+ const pendingTimestamp = devtool.now()
69
+ const nodeTreeContext = comp.getBackendContext()
70
+ if (nodeTreeContext) {
71
+ const measureId = nodeTreeContext.performanceTraceStart?.()
72
+ performanceMeasureStart(type, comp)
73
+ render()
74
+ performanceMeasureEnd()
75
+ nodeTreeContext.performanceTraceEnd?.(measureId!, ({ startTimestamp, endTimestamp }) => {
76
+ devtool.addTimelineBackendWaterfall(waterfallType, [
77
+ pendingTimestamp,
78
+ startTimestamp,
79
+ endTimestamp,
80
+ ])
81
+ })
82
+ } else {
83
+ render()
84
+ }
85
+ }
86
+
87
+ export const addTimelineEvent = (type: string, data?: Record<string, unknown>) => {
88
+ getDevtool()?.addTimelineEvent(getDevtool()!.now(), type, data)
89
+ }