glass-easel 0.1.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 (237) hide show
  1. package/README.md +40 -0
  2. package/dist/glass_easel.all.d.ts +1 -0
  3. package/dist/glass_easel.all.js +2 -0
  4. package/dist/glass_easel.all.js.map +1 -0
  5. package/dist/glass_easel.domlike.global.d.ts +1 -0
  6. package/dist/glass_easel.domlike.global.js +2 -0
  7. package/dist/glass_easel.domlike.global.js.map +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/types/src/backend/backend_protocol.d.ts +119 -0
  11. package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
  12. package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
  13. package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
  14. package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
  15. package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
  16. package/dist/types/src/backend/mode.d.ts +46 -0
  17. package/dist/types/src/backend/mode.d.ts.map +1 -0
  18. package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
  19. package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
  20. package/dist/types/src/behavior.d.ts +428 -0
  21. package/dist/types/src/behavior.d.ts.map +1 -0
  22. package/dist/types/src/class_list.d.ts +79 -0
  23. package/dist/types/src/class_list.d.ts.map +1 -0
  24. package/dist/types/src/component.d.ts +291 -0
  25. package/dist/types/src/component.d.ts.map +1 -0
  26. package/dist/types/src/component_params.d.ts +239 -0
  27. package/dist/types/src/component_params.d.ts.map +1 -0
  28. package/dist/types/src/component_space.d.ts +164 -0
  29. package/dist/types/src/component_space.d.ts.map +1 -0
  30. package/dist/types/src/data_path.d.ts +5 -0
  31. package/dist/types/src/data_path.d.ts.map +1 -0
  32. package/dist/types/src/data_proxy.d.ts +107 -0
  33. package/dist/types/src/data_proxy.d.ts.map +1 -0
  34. package/dist/types/src/data_utils.d.ts +3 -0
  35. package/dist/types/src/data_utils.d.ts.map +1 -0
  36. package/dist/types/src/element.d.ts +275 -0
  37. package/dist/types/src/element.d.ts.map +1 -0
  38. package/dist/types/src/element_iterator.d.ts +43 -0
  39. package/dist/types/src/element_iterator.d.ts.map +1 -0
  40. package/dist/types/src/event.d.ts +104 -0
  41. package/dist/types/src/event.d.ts.map +1 -0
  42. package/dist/types/src/external_shadow_tree.d.ts +20 -0
  43. package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
  44. package/dist/types/src/func_arr.d.ts +39 -0
  45. package/dist/types/src/func_arr.d.ts.map +1 -0
  46. package/dist/types/src/global_options.d.ts +111 -0
  47. package/dist/types/src/global_options.d.ts.map +1 -0
  48. package/dist/types/src/index.d.ts +43 -0
  49. package/dist/types/src/index.d.ts.map +1 -0
  50. package/dist/types/src/mutation_observer.d.ts +79 -0
  51. package/dist/types/src/mutation_observer.d.ts.map +1 -0
  52. package/dist/types/src/native_node.d.ts +8 -0
  53. package/dist/types/src/native_node.d.ts.map +1 -0
  54. package/dist/types/src/node.d.ts +49 -0
  55. package/dist/types/src/node.d.ts.map +1 -0
  56. package/dist/types/src/relation.d.ts +47 -0
  57. package/dist/types/src/relation.d.ts.map +1 -0
  58. package/dist/types/src/render.d.ts +3 -0
  59. package/dist/types/src/render.d.ts.map +1 -0
  60. package/dist/types/src/selector.d.ts +32 -0
  61. package/dist/types/src/selector.d.ts.map +1 -0
  62. package/dist/types/src/shadow_root.d.ts +136 -0
  63. package/dist/types/src/shadow_root.d.ts.map +1 -0
  64. package/dist/types/src/template_engine.d.ts +18 -0
  65. package/dist/types/src/template_engine.d.ts.map +1 -0
  66. package/dist/types/src/text_node.d.ts +32 -0
  67. package/dist/types/src/text_node.d.ts.map +1 -0
  68. package/dist/types/src/tmpl/index.d.ts +18 -0
  69. package/dist/types/src/tmpl/index.d.ts.map +1 -0
  70. package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
  71. package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
  72. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
  73. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
  74. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
  75. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
  76. package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
  77. package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
  78. package/dist/types/src/trait_behaviors.d.ts +38 -0
  79. package/dist/types/src/trait_behaviors.d.ts.map +1 -0
  80. package/dist/types/src/virtual_node.d.ts +10 -0
  81. package/dist/types/src/virtual_node.d.ts.map +1 -0
  82. package/dist/types/tests/backend/domlike.test.d.ts +2 -0
  83. package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
  84. package/dist/types/tests/base/env.d.ts +29 -0
  85. package/dist/types/tests/base/env.d.ts.map +1 -0
  86. package/dist/types/tests/base/match.d.ts +9 -0
  87. package/dist/types/tests/base/match.d.ts.map +1 -0
  88. package/dist/types/tests/core/backend.test.d.ts +2 -0
  89. package/dist/types/tests/core/backend.test.d.ts.map +1 -0
  90. package/dist/types/tests/core/behavior.test.d.ts +2 -0
  91. package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
  92. package/dist/types/tests/core/component_space.test.d.ts +2 -0
  93. package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
  94. package/dist/types/tests/core/data_update.test.d.ts +2 -0
  95. package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
  96. package/dist/types/tests/core/misc.test.d.ts +2 -0
  97. package/dist/types/tests/core/misc.test.d.ts.map +1 -0
  98. package/dist/types/tests/core/placeholder.test.d.ts +2 -0
  99. package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
  100. package/dist/types/tests/core/slot.test.d.ts +2 -0
  101. package/dist/types/tests/core/slot.test.d.ts.map +1 -0
  102. package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
  103. package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
  104. package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
  105. package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
  106. package/dist/types/tests/tmpl/event.test.d.ts +2 -0
  107. package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
  108. package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
  109. package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
  110. package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
  111. package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
  112. package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
  113. package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
  114. package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
  115. package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
  116. package/dist/types/tests/types/chaining.test.d.ts +2 -0
  117. package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
  118. package/dist/types/tests/types/createElement.test.d.ts +2 -0
  119. package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
  120. package/dist/types/tests/types/definition.test.d.ts +2 -0
  121. package/dist/types/tests/types/definition.test.d.ts.map +1 -0
  122. package/guide/zh_CN/advanced/binding_map_update.md +32 -0
  123. package/guide/zh_CN/advanced/build_args.md +28 -0
  124. package/guide/zh_CN/advanced/component_filter.md +70 -0
  125. package/guide/zh_CN/advanced/component_space.md +124 -0
  126. package/guide/zh_CN/advanced/custom_backend.md +53 -0
  127. package/guide/zh_CN/advanced/error_listener.md +32 -0
  128. package/guide/zh_CN/advanced/external_component.md +73 -0
  129. package/guide/zh_CN/advanced/template_engine.md +61 -0
  130. package/guide/zh_CN/appendix/backend_protocol.md +501 -0
  131. package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
  132. package/guide/zh_CN/basic/beginning.md +94 -0
  133. package/guide/zh_CN/basic/component.md +156 -0
  134. package/guide/zh_CN/basic/event.md +169 -0
  135. package/guide/zh_CN/basic/lifetime.md +66 -0
  136. package/guide/zh_CN/basic/method.md +62 -0
  137. package/guide/zh_CN/basic/template.md +135 -0
  138. package/guide/zh_CN/data_management/advanced_update.md +170 -0
  139. package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
  140. package/guide/zh_CN/data_management/data_observer.md +154 -0
  141. package/guide/zh_CN/data_management/property_early_init.md +31 -0
  142. package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
  143. package/guide/zh_CN/index.md +93 -0
  144. package/guide/zh_CN/interaction/behavior.md +52 -0
  145. package/guide/zh_CN/interaction/component_path.md +37 -0
  146. package/guide/zh_CN/interaction/generic.md +73 -0
  147. package/guide/zh_CN/interaction/placeholder.md +40 -0
  148. package/guide/zh_CN/interaction/relation.md +151 -0
  149. package/guide/zh_CN/interaction/slot.md +137 -0
  150. package/guide/zh_CN/interaction/template_import.md +94 -0
  151. package/guide/zh_CN/interaction/trait_behavior.md +117 -0
  152. package/guide/zh_CN/styling/external_class.md +46 -0
  153. package/guide/zh_CN/styling/style_isolation.md +54 -0
  154. package/guide/zh_CN/styling/virtual_host.md +52 -0
  155. package/guide/zh_CN/tree/element_iterator.md +54 -0
  156. package/guide/zh_CN/tree/mutation_observer.md +52 -0
  157. package/guide/zh_CN/tree/node_tree.md +142 -0
  158. package/guide/zh_CN/tree/node_tree_modification.md +78 -0
  159. package/guide/zh_CN/tree/selector.md +66 -0
  160. package/jest.config.js +6 -0
  161. package/jest.dts.config.js +9 -0
  162. package/jest.unit.config.js +14 -0
  163. package/package.json +28 -0
  164. package/src/backend/backend_protocol.ts +313 -0
  165. package/src/backend/composed_backend_protocol.ts +252 -0
  166. package/src/backend/domlike_backend_protocol.ts +370 -0
  167. package/src/backend/mode.ts +51 -0
  168. package/src/backend/suggested_backend_protocol.ts +83 -0
  169. package/src/behavior.ts +1655 -0
  170. package/src/bootstrap_dom_dev.js +22 -0
  171. package/src/class_list.ts +376 -0
  172. package/src/component.ts +1309 -0
  173. package/src/component_params.ts +461 -0
  174. package/src/component_space.ts +547 -0
  175. package/src/data_path.ts +225 -0
  176. package/src/data_proxy.ts +670 -0
  177. package/src/data_utils.ts +50 -0
  178. package/src/element.ts +1966 -0
  179. package/src/element_iterator.ts +158 -0
  180. package/src/event.ts +401 -0
  181. package/src/external_shadow_tree.ts +27 -0
  182. package/src/func_arr.ts +198 -0
  183. package/src/global_options.ts +242 -0
  184. package/src/index.ts +187 -0
  185. package/src/mutation_observer.ts +252 -0
  186. package/src/native_node.ts +74 -0
  187. package/src/node.ts +174 -0
  188. package/src/relation.ts +380 -0
  189. package/src/render.ts +25 -0
  190. package/src/selector.ts +218 -0
  191. package/src/shadow_root.ts +766 -0
  192. package/src/template_engine.ts +45 -0
  193. package/src/text_node.ts +149 -0
  194. package/src/tmpl/index.ts +199 -0
  195. package/src/tmpl/native_rendering.ts +175 -0
  196. package/src/tmpl/proc_gen_wrapper.ts +954 -0
  197. package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
  198. package/src/tmpl/range_list_diff.ts +443 -0
  199. package/src/trait_behaviors.ts +51 -0
  200. package/src/virtual_node.ts +51 -0
  201. package/tests/backend/domlike.test.ts +254 -0
  202. package/tests/base/env.ts +78 -0
  203. package/tests/base/match.ts +185 -0
  204. package/tests/core/backend.test.ts +144 -0
  205. package/tests/core/behavior.test.ts +546 -0
  206. package/tests/core/component_space.test.ts +212 -0
  207. package/tests/core/data_update.test.ts +461 -0
  208. package/tests/core/misc.test.ts +339 -0
  209. package/tests/core/placeholder.test.ts +180 -0
  210. package/tests/core/slot.test.ts +1495 -0
  211. package/tests/core/trait_behaviors.test.ts +153 -0
  212. package/tests/legacy/README.md +3 -0
  213. package/tests/legacy/behavior.test.js +293 -0
  214. package/tests/legacy/component.test.js +1247 -0
  215. package/tests/legacy/data_path.test.js +149 -0
  216. package/tests/legacy/data_proxy.test.js +759 -0
  217. package/tests/legacy/element_iterator.test.js +148 -0
  218. package/tests/legacy/event.test.js +849 -0
  219. package/tests/legacy/external.test.js +510 -0
  220. package/tests/legacy/extra_info.test.js +109 -0
  221. package/tests/legacy/generics.test.js +176 -0
  222. package/tests/legacy/mutation_observer.test.js +210 -0
  223. package/tests/legacy/relation.test.js +517 -0
  224. package/tests/legacy/selector.test.js +263 -0
  225. package/tests/legacy/slot.test.js +915 -0
  226. package/tests/legacy/virtual.test.js +394 -0
  227. package/tests/tmpl/binding_map.test.ts +208 -0
  228. package/tests/tmpl/event.test.ts +206 -0
  229. package/tests/tmpl/expression.test.ts +429 -0
  230. package/tests/tmpl/lvalue.test.ts +160 -0
  231. package/tests/tmpl/native_rendering.test.ts +155 -0
  232. package/tests/tmpl/structure.test.ts +998 -0
  233. package/tests/types/chaining.test.ts +614 -0
  234. package/tests/types/createElement.test.ts +82 -0
  235. package/tests/types/definition.test.ts +442 -0
  236. package/tsconfig.json +11 -0
  237. package/webpack.config.js +270 -0
@@ -0,0 +1,1655 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
2
+
3
+ import {
4
+ ComponentParams,
5
+ DataList,
6
+ PropertyList,
7
+ MethodList,
8
+ ComponentInstance,
9
+ PropertyListItem,
10
+ ComponentMethod,
11
+ RelationParams,
12
+ PropertyType,
13
+ TraitRelationParams,
14
+ DataWithPropertyValues,
15
+ PropertyOption,
16
+ PropertyTypeToValueType,
17
+ TaggedMethod,
18
+ UnTaggedMethod,
19
+ ChainingFilterFunc,
20
+ ChainingFilterType,
21
+ SetDataSetter,
22
+ DeepReadonly,
23
+ Empty,
24
+ NewFieldList,
25
+ ObserverDataPathStrings,
26
+ GetFromObserverPathString,
27
+ IsNever,
28
+ } from './component_params'
29
+ import {
30
+ FuncArr,
31
+ triggerWarning,
32
+ } from './func_arr'
33
+ import {
34
+ ComponentOptions,
35
+ getDefaultComponentSpace,
36
+ } from './global_options'
37
+ import {
38
+ MultiPaths,
39
+ parseMultiPaths,
40
+ } from './data_path'
41
+ import {
42
+ DataValue,
43
+ DataGroupObserverTree,
44
+ } from './data_proxy'
45
+ import {
46
+ ComponentDefinition,
47
+ GeneralComponentDefinition,
48
+ LifetimeFuncs,
49
+ Lifetimes,
50
+ PageLifetimeFuncs,
51
+ } from './component'
52
+ import {
53
+ RelationDefinition,
54
+ RelationType,
55
+ RelationListener,
56
+ RelationFailedListener,
57
+ } from './relation'
58
+ import {
59
+ normalizeUrl,
60
+ ComponentSpace,
61
+ ComponentWaitingList,
62
+ } from './component_space'
63
+ import { TraitBehavior } from './trait_behaviors'
64
+ import { simpleDeepCopy } from './data_utils'
65
+ import { EventListener } from './event'
66
+
67
+ export const enum NormalizedPropertyType {
68
+ Invalid,
69
+ Any,
70
+ String,
71
+ Number,
72
+ Boolean,
73
+ Object,
74
+ Array,
75
+ Function,
76
+ }
77
+
78
+ export type PropertyDefinition = {
79
+ type: NormalizedPropertyType
80
+ optionalTypes: NormalizedPropertyType[] | null
81
+ value: unknown | undefined
82
+ default: (() => unknown) | undefined
83
+ observer: ((newValue: unknown, oldValue: unknown) => void) | null
84
+ reflectIdPrefix: boolean
85
+ }
86
+
87
+ export type ComponentDefinitionWithPlaceholder = {
88
+ final: GeneralComponentDefinition | null,
89
+ placeholder: string | null,
90
+ waiting: ComponentWaitingList | null,
91
+ }
92
+
93
+ type ResolveBehaviorBuilder<B, TChainingFilter extends ChainingFilterType> =
94
+ IsNever<TChainingFilter> extends false
95
+ ? TChainingFilter extends ChainingFilterType
96
+ ? Omit<B, TChainingFilter['remove']> & TChainingFilter['add']
97
+ : B
98
+ : B
99
+
100
+ const shallowMerge = (dest: { [key: string]: unknown }, src: { [key: string]: unknown }) => {
101
+ const keys = Object.keys(src)
102
+ for (let i = 0; i < keys.length; i += 1) {
103
+ const key = keys[i]!
104
+ if (Object.prototype.hasOwnProperty.call(dest, key)) {
105
+ if (key[0] === '_') {
106
+ triggerWarning(`data field "${key}" from different behaviors is overriding or merging.`)
107
+ }
108
+ if (
109
+ typeof dest[key] === 'object'
110
+ && typeof src[key] === 'object'
111
+ && src[key] !== null
112
+ && !Array.isArray(src[key])
113
+ ) {
114
+ if (Array.isArray(dest[key])) {
115
+ dest[key] = (dest[key] as DataValue[]).slice()
116
+ } else {
117
+ const oldDest = dest[key] as { [key: string]: DataValue }
118
+ const newDest = {} as { [key: string]: DataValue }
119
+ const subKeys = Object.keys(oldDest)
120
+ for (let i = 0; i < subKeys.length; i += 1) {
121
+ const subKey = subKeys[i]!
122
+ newDest[subKey] = oldDest[subKey]
123
+ }
124
+ dest[key] = newDest
125
+ }
126
+ shallowMerge(
127
+ dest[key] as { [key: string]: unknown },
128
+ src[key] as { [key: string]: unknown },
129
+ )
130
+ } else {
131
+ dest[key] = src[key]
132
+ }
133
+ } else {
134
+ dest[key] = src[key]
135
+ }
136
+ }
137
+ }
138
+
139
+ const normalizePropertyTypeShortHand = (
140
+ propDef: unknown,
141
+ ): PropertyDefinition | null => {
142
+ if (propDef === String) {
143
+ return {
144
+ type: NormalizedPropertyType.String,
145
+ optionalTypes: null,
146
+ value: '',
147
+ default: undefined,
148
+ observer: null,
149
+ reflectIdPrefix: false,
150
+ }
151
+ }
152
+ if (propDef === Number) {
153
+ return {
154
+ type: NormalizedPropertyType.Number,
155
+ optionalTypes: null,
156
+ value: 0,
157
+ default: undefined,
158
+ observer: null,
159
+ reflectIdPrefix: false,
160
+ }
161
+ }
162
+ if (propDef === Boolean) {
163
+ return {
164
+ type: NormalizedPropertyType.Boolean,
165
+ optionalTypes: null,
166
+ value: false,
167
+ default: undefined,
168
+ observer: null,
169
+ reflectIdPrefix: false,
170
+ }
171
+ }
172
+ if (propDef === Object) {
173
+ return {
174
+ type: NormalizedPropertyType.Object,
175
+ optionalTypes: null,
176
+ value: null,
177
+ default: undefined,
178
+ observer: null,
179
+ reflectIdPrefix: false,
180
+ }
181
+ }
182
+ if (propDef === Array) {
183
+ return {
184
+ type: NormalizedPropertyType.Array,
185
+ optionalTypes: null,
186
+ value: [],
187
+ default: undefined,
188
+ observer: null,
189
+ reflectIdPrefix: false,
190
+ }
191
+ }
192
+ if (propDef === Function) {
193
+ return {
194
+ type: NormalizedPropertyType.Function,
195
+ optionalTypes: null,
196
+ value() { /* empty */ },
197
+ default: undefined,
198
+ observer: null,
199
+ reflectIdPrefix: false,
200
+ }
201
+ }
202
+ if (propDef === null || propDef === undefined) {
203
+ return {
204
+ type: NormalizedPropertyType.Any,
205
+ optionalTypes: null,
206
+ value: null,
207
+ default: undefined,
208
+ observer: null,
209
+ reflectIdPrefix: false,
210
+ }
211
+ }
212
+ return null
213
+ }
214
+
215
+ const normalizePropertyType = (t: unknown): NormalizedPropertyType => {
216
+ if (t === String) {
217
+ return NormalizedPropertyType.String
218
+ }
219
+ if (t === Number) {
220
+ return NormalizedPropertyType.Number
221
+ }
222
+ if (t === Boolean) {
223
+ return NormalizedPropertyType.Boolean
224
+ }
225
+ if (t === Object) {
226
+ return NormalizedPropertyType.Object
227
+ }
228
+ if (t === Array) {
229
+ return NormalizedPropertyType.Array
230
+ }
231
+ if (t === Function) {
232
+ return NormalizedPropertyType.Function
233
+ }
234
+ if (t === null || t === undefined) {
235
+ return NormalizedPropertyType.Any
236
+ }
237
+ return NormalizedPropertyType.Invalid
238
+ }
239
+
240
+ export const convertValueToType = (
241
+ value: unknown,
242
+ propName: string,
243
+ prop: PropertyDefinition,
244
+ ): unknown => {
245
+ const type = prop.type
246
+ const defaultFn = prop.default
247
+ // try match optional types
248
+ const optionalTypes = prop.optionalTypes
249
+ if (optionalTypes) {
250
+ for (let i = 0; i < optionalTypes.length; i += 1) {
251
+ if (matchTypeWithValue(optionalTypes[i]!, value)) {
252
+ return value
253
+ }
254
+ }
255
+ }
256
+ // for string
257
+ if (type === NormalizedPropertyType.String) {
258
+ if (value === null || value === undefined) {
259
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <String> but get null value. Used default value instead.`)
260
+ return defaultFn === undefined ? '' : defaultFn()
261
+ }
262
+ if (typeof value === 'object') {
263
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <String> but got object-typed value. Force converted.`)
264
+ }
265
+ return String(value)
266
+ }
267
+ // for number
268
+ if (type === NormalizedPropertyType.Number) {
269
+ // eslint-disable-next-line no-restricted-globals
270
+ if (isFinite(value as number)) return Number(value)
271
+ if (typeof value === 'number') {
272
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <Number> but got NaN or Infinity. Used default value instead.`)
273
+ } else {
274
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <Number> but got non-number value. Used default value instead.`)
275
+ }
276
+ return defaultFn === undefined ? 0 : defaultFn()
277
+ }
278
+ // for boolean
279
+ if (type === NormalizedPropertyType.Boolean) {
280
+ return !!value
281
+ }
282
+ // for array
283
+ if (type === NormalizedPropertyType.Array) {
284
+ if (Array.isArray(value)) return value as unknown
285
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <Array> but got non-array value. Used default value instead.`)
286
+ return defaultFn === undefined ? [] : defaultFn()
287
+ }
288
+ // for object
289
+ if (type === NormalizedPropertyType.Object) {
290
+ if (typeof value === 'object') return value
291
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <Object> but got non-object value. Used default value instead.`)
292
+ return defaultFn === undefined ? null : defaultFn()
293
+ }
294
+ // for function
295
+ if (type === NormalizedPropertyType.Function) {
296
+ if (typeof value === 'function') return value
297
+ triggerWarning(`property "${propName}" received type-incompatible value: expected <Function> but got non-function value. Used default value instead.`)
298
+ // eslint-disable-next-line func-names
299
+ return defaultFn === undefined ? function () { /* empty */ } : defaultFn()
300
+ }
301
+ // for any-typed, just return the value and avoid undefined
302
+ if (value === undefined) return defaultFn === undefined ? null : defaultFn()
303
+ return value
304
+ }
305
+
306
+ export const matchTypeWithValue = (type: NormalizedPropertyType, value: any) => {
307
+ if (type === NormalizedPropertyType.String) {
308
+ if (typeof value !== 'string') return false
309
+ } else if (type === NormalizedPropertyType.Number) {
310
+ if (!Number.isFinite(value)) return false
311
+ } else if (type === NormalizedPropertyType.Boolean) {
312
+ if (typeof value !== 'boolean') return false
313
+ } else if (type === NormalizedPropertyType.Object) {
314
+ if (typeof value !== 'object' || Array.isArray(value)) return false
315
+ } else if (type === NormalizedPropertyType.Array) {
316
+ if (typeof value !== 'object' || !Array.isArray(value)) return false
317
+ } else if (type === NormalizedPropertyType.Function) {
318
+ if (typeof value !== 'function') return false
319
+ } else if (value === undefined) {
320
+ return false
321
+ }
322
+ return true
323
+ }
324
+
325
+ export const normalizeRelation = <TOut extends { [key: string]: any }>(
326
+ is: string,
327
+ key: string,
328
+ relation: RelationParams | TraitRelationParams<TOut>,
329
+ ): RelationDefinition | null => {
330
+ const checkRelationFunc = (f: unknown): RelationListener | null => {
331
+ if (typeof f === 'function') {
332
+ return f as RelationListener
333
+ }
334
+ if (f !== undefined) {
335
+ triggerWarning(`the "${key}" relation listener is not a function (when preparing behavior "${is}").`)
336
+ }
337
+ return null
338
+ }
339
+ let type: RelationType
340
+ if (relation.type === 'parent') {
341
+ type = RelationType.ParentComponent
342
+ } else if (relation.type === 'child') {
343
+ type = RelationType.ChildComponent
344
+ } else if (relation.type === 'parent-common-node') {
345
+ type = RelationType.ParentNonVirtualNode
346
+ } else if (relation.type === 'child-common-node') {
347
+ type = RelationType.ChildNonVirtualNode
348
+ } else if (relation.type === 'ancestor') {
349
+ type = RelationType.Ancestor
350
+ } else if (relation.type === 'descendant') {
351
+ type = RelationType.Descendant
352
+ } else {
353
+ const type = relation.type as string
354
+ triggerWarning(`the "${key}" relation has an invalid relation type "${type}" (when preparing behavior "${is}").`)
355
+ return null
356
+ }
357
+ let target: string
358
+ | GeneralBehavior
359
+ | TraitBehavior<{ [key: string]: unknown }, { [key: string]: unknown }>
360
+ let domain: string | null = null
361
+ if (relation.target instanceof ComponentDefinition) {
362
+ target = relation.target.behavior as GeneralBehavior
363
+ } else if (relation.target instanceof Behavior || relation.target instanceof TraitBehavior) {
364
+ target = relation.target
365
+ } else {
366
+ const { domain: d, absPath } = normalizeUrl(relation.target || key, is)
367
+ target = absPath
368
+ domain = d
369
+ }
370
+ if (!target) {
371
+ triggerWarning(`the target of relation "${key}" is not a valid behavior or component (when preparing behavior "${is}").`)
372
+ return null
373
+ }
374
+ return {
375
+ target,
376
+ domain,
377
+ type,
378
+ linked: checkRelationFunc(relation.linked),
379
+ linkChanged: checkRelationFunc(relation.linkChanged),
380
+ unlinked: checkRelationFunc(relation.unlinked),
381
+ linkFailed: checkRelationFunc(relation.linkFailed) as RelationFailedListener,
382
+ }
383
+ }
384
+
385
+ export interface RelationHandler<TTarget, TOut> {
386
+ list(): TTarget[]
387
+ listAsTrait: TOut extends never
388
+ ? undefined
389
+ : () => TOut[]
390
+ }
391
+
392
+ export interface RelationInit {
393
+ (def: RelationParams): RelationHandler<any, never>
394
+ <TOut extends { [key: string]: any }>(
395
+ def: TraitRelationParams<TOut>,
396
+ ): RelationHandler<unknown, TOut>
397
+ }
398
+
399
+ export interface BuilderContext<
400
+ TPrevData extends DataList,
401
+ TProperty extends PropertyList,
402
+ TMethodCaller
403
+ > extends ThisType<TMethodCaller> {
404
+ self: TMethodCaller;
405
+ data: DeepReadonly<DataWithPropertyValues<TPrevData, TProperty>>;
406
+ setData: (newData: Partial<SetDataSetter<TPrevData>>) => void;
407
+ implement: <TIn extends { [x: string]: any }>(
408
+ traitBehavior: TraitBehavior<TIn, any>,
409
+ impl: TIn
410
+ ) => void;
411
+ relation<TOut extends { [key: string]: any }>
412
+ (def: TraitRelationParams<TOut>): RelationHandler<any, TOut>
413
+ relation(def: RelationParams): RelationHandler<any, never>
414
+ observer<
415
+ P extends ObserverDataPathStrings<
416
+ DataWithPropertyValues<TPrevData, TProperty>
417
+ >,
418
+ V = DeepReadonly<
419
+ GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P>
420
+ >
421
+ >(
422
+ paths: P,
423
+ func: (newValue: V) => void,
424
+ ): void;
425
+ observer<
426
+ P extends ObserverDataPathStrings<
427
+ DataWithPropertyValues<TPrevData, TProperty>
428
+ >[],
429
+ V = {
430
+ [K in keyof P]: DeepReadonly<
431
+ GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P[K]>
432
+ >
433
+ }
434
+ >(
435
+ paths: readonly [...P],
436
+ func: (...newValues: V extends any[] ? V : never) => void,
437
+ ): void;
438
+ lifetime: <L extends keyof Lifetimes>(name: L, func: Lifetimes[L]) => void;
439
+ pageLifetime: (name: string, func: (...args: any[]) => void) => void;
440
+ method: <Fn extends ComponentMethod>(func: Fn) => TaggedMethod<Fn>;
441
+ listener: <T>(func: EventListener<T>) => TaggedMethod<EventListener<T>>;
442
+ }
443
+
444
+ export type GeneralBehaviorBuilder = BehaviorBuilder<
445
+ Record<string, any>,
446
+ Record<string, any>,
447
+ Record<string, any>,
448
+ Record<string, any>,
449
+ never,
450
+ never
451
+ >
452
+
453
+ export class BehaviorBuilder<
454
+ TPrevData extends DataList = Empty,
455
+ TData extends DataList = Empty,
456
+ TProperty extends PropertyList = Empty,
457
+ TMethod extends MethodList = Empty,
458
+ TChainingFilter extends ChainingFilterType = never,
459
+ TPendingChainingFilter extends ChainingFilterType = never,
460
+ > {
461
+ /** @internal */
462
+ _$ownerSpace: ComponentSpace
463
+ /** @internal */
464
+ _$is: string | undefined
465
+ /** @internal */
466
+ _$behaviors: (string | GeneralBehavior)[] = []
467
+ /** @internal */
468
+ _$chainingFilter?: (chain: GeneralBehaviorBuilder) => any
469
+ /** @internal */
470
+ _$options?: ComponentOptions
471
+ /** @internal */
472
+ _$traitBehaviors: { traitBehavior: TraitBehavior<any, any>, impl: any }[] = []
473
+ /** @internal */
474
+ _$template?: { [key: string]: unknown }
475
+ /** @internal */
476
+ _$using?: { [alias: string]: string | GeneralComponentDefinition }
477
+ /** @internal */
478
+ _$placeholders?: { [alias: string]: string }
479
+ /** @internal */
480
+ _$generics?: { [alias: string]: { default?: string | GeneralComponentDefinition } | true }
481
+ /** @internal */
482
+ _$externalClasses?: string[]
483
+ /** @internal */
484
+ _$staticData: { [field: string]: any } | undefined = undefined
485
+ /** @internal */
486
+ _$data: (() => { [field: string]: any })[] = []
487
+ /** @internal */
488
+ _$properties?: { name: string, def: PropertyListItem<PropertyType, unknown> }[]
489
+ /** @internal */
490
+ _$methods: { name: string, func: ComponentMethod }[] = []
491
+ /** @internal */
492
+ _$observers?: {
493
+ dataPaths: MultiPaths,
494
+ func: ComponentMethod | string,
495
+ once: boolean,
496
+ }[]
497
+ /** @internal */
498
+ _$lifetimes: { name: string, func: ComponentMethod, once: boolean }[] = []
499
+ /** @internal */
500
+ _$pageLifetimes?: { name: string, func: ComponentMethod, once: boolean }[] = []
501
+ /** @internal */
502
+ _$listeners?: {
503
+ [name: string]: ComponentMethod | string,
504
+ }
505
+ /** @internal */
506
+ _$relations?: { name: string, rel: RelationParams }[]
507
+ /** @internal */
508
+ _$init: ((this: any, ctx: any) => any)[] = []
509
+ /** @internal */
510
+ _$methodCallerInit?: (
511
+ this: ComponentInstance<TData, TProperty, TMethod>
512
+ ) => any
513
+
514
+ /** @internal */
515
+ constructor(
516
+ is: string | undefined,
517
+ ownerSpace: ComponentSpace,
518
+ ) {
519
+ this._$is = is
520
+ this._$ownerSpace = ownerSpace
521
+ }
522
+
523
+ /**
524
+ * Set a front-most init function
525
+ *
526
+ * It should return the method caller (the `this` value for various callbacks)
527
+ * that will be used in future.
528
+ */
529
+ methodCallerInit(
530
+ func: (this: ComponentInstance<TData, TProperty, TMethod>) => any,
531
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
532
+ this._$methodCallerInit = func
533
+ return this as any
534
+ }
535
+
536
+ /**
537
+ * Add a behavior
538
+ *
539
+ * If the behavior contains a chaining filter, the chaining filter is called.
540
+ */
541
+ behavior<
542
+ UData extends DataList,
543
+ UProperty extends PropertyList,
544
+ UMethod extends MethodList,
545
+ UChainingFilter extends ChainingFilterType
546
+ >(
547
+ behavior: Behavior<UData, UProperty, UMethod, UChainingFilter>,
548
+ ): ResolveBehaviorBuilder<
549
+ BehaviorBuilder<
550
+ TPrevData,
551
+ TData & UData,
552
+ TProperty & UProperty,
553
+ TMethod & UMethod,
554
+ UChainingFilter,
555
+ TPendingChainingFilter
556
+ >,
557
+ UChainingFilter
558
+ > {
559
+ this._$behaviors.push(behavior as GeneralBehavior)
560
+ if (behavior._$chainingFilter) {
561
+ return behavior._$chainingFilter(this as any) as any
562
+ }
563
+ return this as any
564
+ }
565
+
566
+ /**
567
+ * Set the chaining filter
568
+ *
569
+ * The chaining filter is the definition filter for chaining API.
570
+ * It SHOULD return another chainable object for further chaining.
571
+ */
572
+ chainingFilter<
573
+ TAddedFields extends { [key: string]: any },
574
+ TRemovedFields extends string = never,
575
+ >(
576
+ func: ChainingFilterFunc<TAddedFields, TRemovedFields>,
577
+ ): ResolveBehaviorBuilder<
578
+ BehaviorBuilder<TPrevData, TData, TProperty, TMethod, TChainingFilter, {
579
+ add: TAddedFields,
580
+ remove: TRemovedFields,
581
+ }>,
582
+ TChainingFilter
583
+ > {
584
+ this._$chainingFilter = func
585
+ return this as any
586
+ }
587
+
588
+ /**
589
+ * Set component options
590
+ *
591
+ * The options will be merged with previous settings.
592
+ */
593
+ options(options: ComponentOptions): ResolveBehaviorBuilder<this, TChainingFilter> {
594
+ const oldOptions = this._$options
595
+ if (oldOptions) {
596
+ this._$options = { ...oldOptions, ...options }
597
+ } else {
598
+ this._$options = options
599
+ }
600
+ return this as any
601
+ }
602
+
603
+ /**
604
+ * Implement a trait behavior
605
+ */
606
+ implement<TIn extends { [key: string]: any }>(
607
+ traitBehavior: TraitBehavior<TIn, any>,
608
+ impl: TIn,
609
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
610
+ this._$traitBehaviors.push({ traitBehavior, impl })
611
+ return this as any
612
+ }
613
+
614
+ /**
615
+ * Set the compiled template object
616
+ */
617
+ template(template: { [key: string]: unknown }): ResolveBehaviorBuilder<this, TChainingFilter> {
618
+ this._$template = template
619
+ return this as any
620
+ }
621
+
622
+ /**
623
+ * Add other components to the component using list
624
+ */
625
+ usingComponents<T extends Record<string, string | ComponentDefinition<any, any, any>>>(
626
+ list: T,
627
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
628
+ if (this._$using) this._$using = { ...this._$using, ...list }
629
+ else this._$using = list
630
+ return this as any
631
+ }
632
+
633
+ /**
634
+ * Add some placeholders
635
+ *
636
+ * The alias SHOULD be in the using list, otherwise it will be ignored.
637
+ */
638
+ placeholders(
639
+ list: Record<string, string>,
640
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
641
+ if (this._$placeholders) this._$placeholders = { ...this._$placeholders, ...list }
642
+ else this._$placeholders = list
643
+ return this as any
644
+ }
645
+
646
+ /**
647
+ * Add other generics
648
+ *
649
+ * The alias SHOULD NOT be in the using list, otherwise it will be ignored.
650
+ */
651
+ generics(
652
+ list: Record<string, { default?: string | GeneralComponentDefinition } | true>,
653
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
654
+ if (this._$generics) this._$generics = { ...this._$generics, ...list }
655
+ else this._$generics = list
656
+ return this as any
657
+ }
658
+
659
+ /**
660
+ * Add external classes
661
+ */
662
+ externalClasses(list: string[]): ResolveBehaviorBuilder<this, TChainingFilter> {
663
+ if (this._$externalClasses) this._$externalClasses = this._$externalClasses.concat(list)
664
+ else this._$externalClasses = list
665
+ return this as any
666
+ }
667
+
668
+ /**
669
+ * Add some template data fields
670
+ *
671
+ * It does not support raw data, but a `gen` function which returns the new data fields.
672
+ * The `gen` function executes once during component creation.
673
+ */
674
+ data<T extends DataList>(
675
+ gen: (() => NewFieldList<DataWithPropertyValues<TData, TProperty>, T>),
676
+ ): ResolveBehaviorBuilder<
677
+ BehaviorBuilder<T, TData & T, TProperty, TMethod, TChainingFilter, TPendingChainingFilter>,
678
+ TChainingFilter
679
+ > {
680
+ this._$data.push(gen)
681
+ return this as any
682
+ }
683
+
684
+ /**
685
+ * Set the static template data fields
686
+ *
687
+ * The data will be cloned once during component creation.
688
+ * If called multiple times, the static data will be overwritten but not merged!
689
+ * Usually, the `data()` method is preferred.
690
+ */
691
+ staticData<T extends DataList>(
692
+ data: NewFieldList<DataWithPropertyValues<TData, TProperty>, T>,
693
+ ): ResolveBehaviorBuilder<
694
+ BehaviorBuilder<T, TData & T, TProperty, TMethod, TChainingFilter, TPendingChainingFilter>,
695
+ TChainingFilter
696
+ > {
697
+ this._$staticData = data
698
+ return this as any
699
+ }
700
+
701
+ /**
702
+ * Add a single property
703
+ *
704
+ * The property name should be different from other properties.
705
+ */
706
+ property<
707
+ N extends string,
708
+ T extends PropertyType,
709
+ V extends PropertyTypeToValueType<T>,
710
+ >(
711
+ name: N,
712
+ def: N extends keyof (TData & TProperty) ? never : PropertyListItem<T, V>,
713
+ ): ResolveBehaviorBuilder<
714
+ BehaviorBuilder<
715
+ TPrevData,
716
+ TData,
717
+ TProperty & Record<N, unknown extends V ? T : PropertyOption<T, V>>,
718
+ TMethod,
719
+ TChainingFilter,
720
+ TPendingChainingFilter
721
+ >,
722
+ TChainingFilter
723
+ > {
724
+ if (!this._$properties) this._$properties = []
725
+ this._$properties.push({ name, def: def as PropertyListItem<PropertyType, unknown> })
726
+ return this as any
727
+ }
728
+
729
+ /**
730
+ * Add a single public method
731
+ *
732
+ * The public method can be used as an event handler, and can be visited in component instance.
733
+ */
734
+ methods<
735
+ T extends MethodList,
736
+ >(
737
+ funcs: T & ThisType<ComponentInstance<TData, TProperty, TMethod & T>>,
738
+ ): ResolveBehaviorBuilder<
739
+ BehaviorBuilder<
740
+ TPrevData,
741
+ TData,
742
+ TProperty,
743
+ TMethod & T,
744
+ TChainingFilter,
745
+ TPendingChainingFilter
746
+ >,
747
+ TChainingFilter
748
+ > {
749
+ const keys = Object.keys(funcs)
750
+ for (let i = 0; i < keys.length; i += 1) {
751
+ const name = keys[i]!
752
+ const func = funcs[name]!
753
+ this._$methods.push({ name, func })
754
+ }
755
+ return this as any
756
+ }
757
+
758
+ /**
759
+ * Add a data observer
760
+ */
761
+ observer<
762
+ P extends ObserverDataPathStrings<
763
+ DataWithPropertyValues<TPrevData, TProperty>
764
+ >,
765
+ V = DeepReadonly<
766
+ GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P>
767
+ >
768
+ >(
769
+ paths: P,
770
+ func: (this: ComponentInstance<TData, TProperty, TMethod>, newValue: V) => void,
771
+ once?: boolean,
772
+ ): ResolveBehaviorBuilder<this, TChainingFilter>;
773
+ observer<
774
+ P extends ObserverDataPathStrings<
775
+ DataWithPropertyValues<TPrevData, TProperty>
776
+ >[],
777
+ V = {
778
+ [K in keyof P]: DeepReadonly<
779
+ GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P[K]>
780
+ >
781
+ }
782
+ >(
783
+ paths: readonly [...P],
784
+ func: (
785
+ this: ComponentInstance<TData, TProperty, TMethod>,
786
+ ...newValues: V extends any[] ? V : never
787
+ ) => void,
788
+ once?: boolean,
789
+ ): ResolveBehaviorBuilder<this, TChainingFilter>;
790
+ observer(
791
+ paths: string | readonly string[],
792
+ func: (this: ComponentInstance<TData, TProperty, TMethod>, ...args: any[]) => any,
793
+ once = false,
794
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
795
+ if (!this._$observers) this._$observers = []
796
+ this._$observers.push({ dataPaths: parseMultiPaths(paths as string | string[]), func, once })
797
+ return this as any
798
+ }
799
+
800
+ /**
801
+ * Add a lifetime callback
802
+ */
803
+ lifetime<L extends keyof Lifetimes>(
804
+ name: L,
805
+ func: (
806
+ this: ComponentInstance<TData, TProperty, TMethod>,
807
+ ...args: Parameters<Lifetimes[L]>
808
+ ) => ReturnType<Lifetimes[L]>,
809
+ once = false,
810
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
811
+ this._$lifetimes.push({ name, func, once })
812
+ return this as any
813
+ }
814
+
815
+ /**
816
+ * Add a page-lifetime callback
817
+ */
818
+ pageLifetime(
819
+ name: string,
820
+ func: (this: ComponentInstance<TData, TProperty, TMethod>, ...args: any[]) => any,
821
+ once = false,
822
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
823
+ if (!this._$pageLifetimes) this._$pageLifetimes = []
824
+ this._$pageLifetimes.push({ name, func, once })
825
+ return this as any
826
+ }
827
+
828
+ /**
829
+ * Add a relation
830
+ */
831
+ relation(
832
+ name: string,
833
+ rel: RelationParams & ThisType<ComponentInstance<TData, TProperty, TMethod>>,
834
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
835
+ if (!this._$relations) this._$relations = []
836
+ this._$relations.push({ name, rel })
837
+ return this as any
838
+ }
839
+
840
+ /**
841
+ * Execute a function while component instance creation
842
+ *
843
+ * A `BuilderContext` is provided to tweak the component creation progress.
844
+ * The return value is used as the "export" value of the behavior,
845
+ * which can be imported by other behaviors.
846
+ */
847
+ init<TExport extends Record<string, TaggedMethod<(...args: any[]) => any>> | void>(
848
+ func: (
849
+ this: ComponentInstance<TData, TProperty, TMethod>,
850
+ builderContext: BuilderContext<
851
+ TPrevData,
852
+ TProperty,
853
+ ComponentInstance<TData, TProperty, TMethod>
854
+ >,
855
+ ) => TExport,
856
+ // eslint-disable-next-line function-paren-newline
857
+ ): ResolveBehaviorBuilder<
858
+ BehaviorBuilder<
859
+ TPrevData,
860
+ TData,
861
+ TProperty,
862
+ TMethod & (TExport extends void ? Empty : {
863
+ [K in keyof TExport]: UnTaggedMethod<TExport[K]>
864
+ }),
865
+ TChainingFilter,
866
+ TPendingChainingFilter
867
+ >,
868
+ TChainingFilter
869
+ > {
870
+ this._$init.push(func)
871
+ return this as any
872
+ }
873
+
874
+ /**
875
+ * Apply a classic-style definition
876
+ */
877
+ definition<
878
+ TNewData extends DataList = Empty,
879
+ TNewProperty extends PropertyList = Empty,
880
+ TNewMethod extends MethodList = Empty,
881
+ >(
882
+ def:
883
+ ComponentParams<TNewData, TNewProperty, TNewMethod> &
884
+ ThisType<ComponentInstance<TData & TNewData, TProperty & TNewProperty, TMethod & TNewMethod>>,
885
+ ): ResolveBehaviorBuilder<
886
+ BehaviorBuilder<
887
+ TPrevData,
888
+ TData & TNewData,
889
+ TProperty & TNewProperty,
890
+ TMethod & TNewMethod,
891
+ TChainingFilter,
892
+ TPendingChainingFilter
893
+ >,
894
+ TChainingFilter
895
+ > {
896
+ if (def.behaviors) this._$behaviors.push(...def.behaviors)
897
+ if (def.options) this.options(def.options)
898
+ if (def.template) this.template(def.template)
899
+ if (def.using) this.usingComponents(def.using)
900
+ if (def.placeholders) this.placeholders(def.placeholders)
901
+ if (def.generics) this.generics(def.generics)
902
+ if (def.externalClasses) this.externalClasses(def.externalClasses)
903
+ const rawData = def.data
904
+ if (rawData !== undefined) {
905
+ if (typeof rawData === 'function') {
906
+ this._$data.push(rawData as () => DataList)
907
+ } else {
908
+ this._$staticData = rawData
909
+ }
910
+ }
911
+ const rawProperties = def.properties
912
+ if (rawProperties !== undefined) {
913
+ if (!this._$properties) this._$properties = []
914
+ const keys = Object.keys(rawProperties)
915
+ for (let i = 0; i < keys.length; i += 1) {
916
+ const name = keys[i]!
917
+ const def = rawProperties[name]! as PropertyList
918
+ this._$properties.push({ name, def })
919
+ }
920
+ }
921
+ const rawMethods = def.methods
922
+ if (rawMethods !== undefined) {
923
+ const keys = Object.keys(rawMethods)
924
+ for (let i = 0; i < keys.length; i += 1) {
925
+ const name = keys[i]!
926
+ const func = rawMethods[name]!
927
+ this._$methods.push({ name, func })
928
+ }
929
+ }
930
+ const rawObservers = def.observers
931
+ if (rawObservers !== undefined) {
932
+ if (!this._$observers) this._$observers = []
933
+ if (Array.isArray(rawObservers)) {
934
+ for (let i = 0; i < rawObservers.length; i += 1) {
935
+ const { fields, observer } = rawObservers[i]!
936
+ this._$observers.push({ dataPaths: parseMultiPaths(fields ?? '**'), func: observer, once: false })
937
+ }
938
+ } else {
939
+ const keys = Object.keys(rawObservers)
940
+ for (let i = 0; i < keys.length; i += 1) {
941
+ const fields = keys[i]!
942
+ const observer = rawObservers[fields]!
943
+ this._$observers.push({ dataPaths: parseMultiPaths(fields), func: observer, once: false })
944
+ }
945
+ }
946
+ }
947
+ const rawLifetimes = def.lifetimes
948
+ if (rawLifetimes?.created === undefined && typeof def.created === 'function') {
949
+ this._$lifetimes.push({ name: 'created', func: def.created, once: true })
950
+ }
951
+ if (rawLifetimes?.attached === undefined && typeof def.attached === 'function') {
952
+ this._$lifetimes.push({ name: 'attached', func: def.attached, once: true })
953
+ }
954
+ if (rawLifetimes?.moved === undefined && typeof def.moved === 'function') {
955
+ this._$lifetimes.push({ name: 'moved', func: def.moved, once: true })
956
+ }
957
+ if (rawLifetimes?.detached === undefined && typeof def.detached === 'function') {
958
+ this._$lifetimes.push({ name: 'detached', func: def.detached, once: true })
959
+ }
960
+ if (rawLifetimes?.ready === undefined && typeof def.ready === 'function') {
961
+ this._$lifetimes.push({ name: 'ready', func: def.ready, once: true })
962
+ }
963
+ if (rawLifetimes) {
964
+ const keys = Object.keys(rawLifetimes)
965
+ for (let i = 0; i < keys.length; i += 1) {
966
+ const name = keys[i]!
967
+ const func = rawLifetimes[name]!
968
+ this._$lifetimes.push({ name, func, once: true })
969
+ }
970
+ }
971
+ const rawPageLifetimes = def.pageLifetimes
972
+ if (rawPageLifetimes) {
973
+ if (!this._$pageLifetimes) this._$pageLifetimes = []
974
+ const keys = Object.keys(rawPageLifetimes)
975
+ for (let i = 0; i < keys.length; i += 1) {
976
+ const name = keys[i]!
977
+ const func = rawPageLifetimes[name]!
978
+ this._$pageLifetimes.push({ name, func, once: true })
979
+ }
980
+ }
981
+ this._$listeners = def.listeners
982
+ const rawRelations = def.relations
983
+ if (rawRelations) {
984
+ if (!this._$relations) this._$relations = []
985
+ const keys = Object.keys(rawRelations)
986
+ for (let i = 0; i < keys.length; i += 1) {
987
+ const name = keys[i]!
988
+ const rel = rawRelations[name]!
989
+ this._$relations.push({ name, rel })
990
+ }
991
+ }
992
+ return this as any
993
+ }
994
+
995
+ /**
996
+ * Finish build, generate a behavior, and register it in the component space
997
+ */
998
+ registerBehavior(): Behavior<TData, TProperty, TMethod, TPendingChainingFilter> {
999
+ const is = this._$is
1000
+ const behavior = new Behavior(this)
1001
+ if (is !== undefined) {
1002
+ this._$ownerSpace._$registerBehavior(is, behavior as unknown as GeneralBehavior)
1003
+ }
1004
+ return behavior as any
1005
+ }
1006
+
1007
+ /**
1008
+ * Finish build, generate a component definition, and register it in the component space
1009
+ */
1010
+ registerComponent(): ComponentDefinition<TData, TProperty, TMethod> {
1011
+ const is = this._$is
1012
+ const behavior = new Behavior(this)
1013
+ const compDef = new ComponentDefinition(behavior)
1014
+ if (is !== undefined) {
1015
+ this._$ownerSpace._$registerComponent(is, compDef as unknown as GeneralComponentDefinition)
1016
+ }
1017
+ return compDef
1018
+ }
1019
+ }
1020
+
1021
+ /**
1022
+ * Common mixin-like behavior
1023
+ *
1024
+ * Each component definition contains a single *root* behavior.
1025
+ * A behavior can mixin other behaviors.
1026
+ */
1027
+ export class Behavior<
1028
+ TData extends DataList,
1029
+ TProperty extends PropertyList,
1030
+ TMethod extends MethodList,
1031
+ TChainingFilter extends ChainingFilterType,
1032
+ > {
1033
+ /** @internal */
1034
+ private _$unprepared: boolean
1035
+ is: string
1036
+ ownerSpace: ComponentSpace
1037
+ /** @internal */
1038
+ private _$builder: BehaviorBuilder<
1039
+ any,
1040
+ TData,
1041
+ TProperty,
1042
+ TMethod,
1043
+ any,
1044
+ TChainingFilter
1045
+ >
1046
+ /** @internal */
1047
+ private _$flatAncestors: Set<GeneralBehavior>
1048
+ /** @internal */
1049
+ _$chainingFilter?: (chain: GeneralBehaviorBuilder) => TChainingFilter
1050
+ /** @internal */
1051
+ _$options?: ComponentOptions
1052
+ /** @internal */
1053
+ _$traitBehaviors?: { traitBehavior: TraitBehavior<any, any>, impl: any }[]
1054
+ /** @internal */
1055
+ _$template?: { [key: string]: unknown }
1056
+ /** @internal */
1057
+ _$using: { [alias: string]: ComponentDefinitionWithPlaceholder }
1058
+ /** @internal */
1059
+ _$generics?: string[]
1060
+ /** @internal */
1061
+ _$genericDefaults?: { [alias: string]: null | GeneralComponentDefinition }
1062
+ /** @internal */
1063
+ _$externalClasses?: string[]
1064
+ /** @internal */
1065
+ _$staticData?: DataList
1066
+ /** @internal */
1067
+ _$data: (() => { [field: string]: any })[]
1068
+ /** @internal */
1069
+ _$propertyMap: { [name: string]: PropertyDefinition }
1070
+ /** @internal */
1071
+ _$methodMap: { [name: string]: ComponentMethod }
1072
+ /** @internal */
1073
+ _$observers: { dataPaths: MultiPaths, observer: ComponentMethod, once: boolean }[]
1074
+ /** @internal */
1075
+ _$lifetimes: { name: string, func: ComponentMethod, once: boolean }[]
1076
+ /** @internal */
1077
+ _$pageLifetimes?: { name: string, func: ComponentMethod, once: boolean }[]
1078
+ /** @internal */
1079
+ _$listeners?: { id: string, ev: string, listener: ComponentMethod }[]
1080
+ /** @internal */
1081
+ _$relationMap?: { [name: string]: RelationDefinition }
1082
+ /** @internal */
1083
+ _$init: ((this: any, ctx: any) => any)[]
1084
+ /** @internal */
1085
+ _$methodCallerInit?: (this: ComponentInstance<TData, TProperty, TMethod>) =>
1086
+ ComponentInstance<TData, TProperty, TMethod>
1087
+
1088
+ /**
1089
+ * Create a behavior with classic-style definition
1090
+ */
1091
+ static create<
1092
+ TData extends DataList,
1093
+ TProperty extends PropertyList,
1094
+ TMethod extends MethodList,
1095
+ >(
1096
+ def: ComponentParams<
1097
+ TData,
1098
+ TProperty,
1099
+ TMethod
1100
+ > & ThisType<ComponentInstance<TData, TProperty, TMethod>>,
1101
+ ownerSpace?: ComponentSpace,
1102
+ ) {
1103
+ return new BehaviorBuilder(def.is, ownerSpace || getDefaultComponentSpace())
1104
+ .definition(def)
1105
+ .registerBehavior()
1106
+ }
1107
+
1108
+ /** @internal */
1109
+ constructor(
1110
+ builder: BehaviorBuilder<
1111
+ any,
1112
+ TData,
1113
+ TProperty,
1114
+ TMethod,
1115
+ any,
1116
+ TChainingFilter
1117
+ >,
1118
+ ) {
1119
+ this._$unprepared = true
1120
+ this.is = builder._$is || ''
1121
+ this.ownerSpace = builder._$ownerSpace
1122
+ this._$builder = builder
1123
+ this._$flatAncestors = new Set()
1124
+ this._$chainingFilter = builder._$chainingFilter
1125
+ this._$options = builder._$options
1126
+ this._$traitBehaviors = undefined
1127
+ this._$template = builder._$template
1128
+ this._$using = Object.create(null) as { [alias: string]: ComponentDefinitionWithPlaceholder }
1129
+ this._$generics = undefined
1130
+ this._$genericDefaults = undefined
1131
+ this._$externalClasses = builder._$externalClasses
1132
+ this._$staticData = undefined
1133
+ this._$data = []
1134
+ this._$propertyMap = Object.create(null) as
1135
+ { [name: string]: PropertyDefinition }
1136
+ this._$methodMap = Object.create(null) as { [name: string]: ComponentMethod }
1137
+ this._$observers = []
1138
+ this._$lifetimes = []
1139
+ this._$pageLifetimes = undefined
1140
+ this._$listeners = undefined
1141
+ this._$relationMap = undefined
1142
+ this._$init = []
1143
+ this._$methodCallerInit = builder._$methodCallerInit
1144
+ }
1145
+
1146
+ general(): GeneralBehavior {
1147
+ return this as unknown as GeneralBehavior
1148
+ }
1149
+
1150
+ /**
1151
+ * List all component dependencies (recursively)
1152
+ *
1153
+ * This method will prepare the underlying behavior.
1154
+ */
1155
+ getComponentDependencies(
1156
+ genericTargets?: { [name: string]: GeneralComponentDefinition },
1157
+ ): Set<GeneralComponentDefinition> {
1158
+ const ret: Set<GeneralComponentDefinition> = new Set()
1159
+ const rec = function (this: void, beh: GeneralBehavior) {
1160
+ if (beh._$unprepared) beh.prepare()
1161
+ const keys = Object.keys(beh._$using)
1162
+ for (let i = 0; i < keys.length; i += 1) {
1163
+ const k = keys[i]!
1164
+ const v = beh._$using[k]!
1165
+ const dep = v.final
1166
+ if (dep && !ret.has(dep)) {
1167
+ ret.add(dep)
1168
+ rec(dep.behavior as GeneralBehavior)
1169
+ }
1170
+ }
1171
+ }
1172
+ rec(this.general())
1173
+ if (genericTargets) {
1174
+ const list = Object.values(genericTargets)
1175
+ for (let i = 0; i < list.length; i += 1) {
1176
+ const dep = list[i]!
1177
+ if (!ret.has(dep)) {
1178
+ ret.add(dep)
1179
+ rec(dep.behavior as GeneralBehavior)
1180
+ }
1181
+ }
1182
+ }
1183
+ return ret
1184
+ }
1185
+
1186
+ /** Same as `prepare` method in the prototype (for backward compatibility) */
1187
+ static prepare<
1188
+ TData extends DataList,
1189
+ TProperty extends PropertyList,
1190
+ TMethod extends MethodList,
1191
+ TChainingFilter extends ChainingFilterType = never,
1192
+ >(behavior: Behavior<TData, TProperty, TMethod, TChainingFilter>) {
1193
+ behavior.prepare()
1194
+ }
1195
+
1196
+ /**
1197
+ * Execute the prepare phase (an optimization phase) of this behavior
1198
+ *
1199
+ * Every behavior needs this phase for better future performance.
1200
+ * However, this phase requires a little time for execution,
1201
+ * and requires its all dependent behaviors created.
1202
+ * If a dependent behavior is not prepared, then its prepare phase is also executed.
1203
+ */
1204
+ prepare() {
1205
+ if (!this._$unprepared) return
1206
+ this._$unprepared = false
1207
+ const is = this.is
1208
+ const space = this.ownerSpace
1209
+ const builder = this._$builder
1210
+
1211
+ // mixin dependent behaviors
1212
+ const flatAncestors = this._$flatAncestors
1213
+ if (Array.isArray(builder._$behaviors)) {
1214
+ for (let i = 0; i < builder._$behaviors.length; i += 1) {
1215
+ const parentName = builder._$behaviors[i]!
1216
+ let parent: GeneralBehavior | null = null
1217
+ if (parentName instanceof Behavior) {
1218
+ parent = parentName
1219
+ } else {
1220
+ const parentNameStr = String(parentName)
1221
+ if (space) {
1222
+ parent = space.getBehaviorByUrl(parentNameStr, is)
1223
+ }
1224
+ if (!parent) {
1225
+ triggerWarning(`behavior "${parentNameStr}" is not found (when preparing behavior "${is}").`)
1226
+ }
1227
+ }
1228
+ if (!parent) continue
1229
+ if (parent._$unprepared) Behavior.prepare(parent)
1230
+
1231
+ // merge trait behaviors
1232
+ const traitBehaviors = parent._$traitBehaviors
1233
+ if (traitBehaviors !== undefined) {
1234
+ if (!this._$traitBehaviors) this._$traitBehaviors = []
1235
+ this._$traitBehaviors.push(...traitBehaviors)
1236
+ }
1237
+
1238
+ // merge static data
1239
+ const staticData = parent._$staticData
1240
+ if (staticData) {
1241
+ if (!this._$staticData) this._$staticData = {}
1242
+ shallowMerge(this._$staticData, staticData)
1243
+ }
1244
+
1245
+ // merge methods
1246
+ Object.assign(this._$methodMap, parent._$methodMap)
1247
+
1248
+ // merge properties
1249
+ Object.assign(this._$propertyMap, parent._$propertyMap)
1250
+
1251
+ // merge dynamic data
1252
+ this._$data.push(...parent._$data)
1253
+
1254
+ // merge observers
1255
+ const observers = parent._$observers
1256
+ for (let i = 0; i < observers.length; i += 1) {
1257
+ const item = observers[i]!
1258
+ if (item.once) {
1259
+ if (this._$observers.indexOf(item as any) >= 0) continue
1260
+ }
1261
+ this._$observers.push(item as any)
1262
+ }
1263
+
1264
+ // merge lifetimes
1265
+ const lifetimes = parent._$lifetimes
1266
+ for (let i = 0; i < lifetimes.length; i += 1) {
1267
+ const item = lifetimes[i]!
1268
+ if (item.once) {
1269
+ if (this._$lifetimes.indexOf(item as any) >= 0) continue
1270
+ }
1271
+ this._$lifetimes.push(item as any)
1272
+ }
1273
+
1274
+ // merge page lifetimes
1275
+ const pageLifetimes = parent._$pageLifetimes
1276
+ if (pageLifetimes !== undefined) {
1277
+ if (this._$pageLifetimes) {
1278
+ for (let i = 0; i < pageLifetimes.length; i += 1) {
1279
+ const item = pageLifetimes[i]!
1280
+ if (item.once) {
1281
+ if (this._$pageLifetimes.indexOf(item as any) >= 0) continue
1282
+ }
1283
+ this._$pageLifetimes.push(item as any)
1284
+ }
1285
+ } else {
1286
+ this._$pageLifetimes = pageLifetimes.slice() as any[]
1287
+ }
1288
+ }
1289
+
1290
+ // merge legacy listeners
1291
+ const listeners = parent._$listeners as any[] | undefined
1292
+ if (listeners !== undefined) {
1293
+ if (!this._$listeners) this._$listeners = []
1294
+ this._$listeners.push(...listeners)
1295
+ }
1296
+
1297
+ // merge relations
1298
+ const relations = parent._$relationMap
1299
+ if (relations !== undefined) {
1300
+ if (!this._$relationMap) {
1301
+ this._$relationMap = Object.create(null) as { [name: string]: RelationDefinition }
1302
+ }
1303
+ Object.assign(this._$relationMap, relations)
1304
+ }
1305
+
1306
+ // merge init
1307
+ this._$init.push(...parent._$init)
1308
+
1309
+ // construct flat ancestors
1310
+ parent._$flatAncestors.forEach((p) => {
1311
+ flatAncestors.add(p)
1312
+ })
1313
+ }
1314
+ }
1315
+
1316
+ // init trait behaviors
1317
+ const traitBehaviors = builder._$traitBehaviors
1318
+ if (traitBehaviors !== undefined) {
1319
+ if (!this._$traitBehaviors) this._$traitBehaviors = []
1320
+ this._$traitBehaviors.push(...traitBehaviors)
1321
+ }
1322
+
1323
+ // init using
1324
+ if (typeof builder._$using === 'object' && builder._$using !== null) {
1325
+ const hasPlaceholders = typeof builder._$placeholders === 'object' && builder._$placeholders !== null
1326
+ const keys = Object.keys(builder._$using)
1327
+ for (let i = 0; i < keys.length; i += 1) {
1328
+ const k = keys[i]!
1329
+ const v = builder._$using[k]
1330
+ let placeholder = null
1331
+ if (hasPlaceholders) {
1332
+ placeholder = (
1333
+ builder._$placeholders as { [name: string]: string }
1334
+ )[k] ?? null
1335
+ }
1336
+ if (v instanceof ComponentDefinition) {
1337
+ this._$using[k] = {
1338
+ final: v,
1339
+ placeholder,
1340
+ waiting: null,
1341
+ }
1342
+ } else if (space) {
1343
+ const path = String(v)
1344
+ const final = space.getComponentByUrlWithoutDefault(path, is)
1345
+ if (final) {
1346
+ this._$using[k] = {
1347
+ final,
1348
+ placeholder: null,
1349
+ waiting: null,
1350
+ }
1351
+ } else if (placeholder !== null) {
1352
+ const p: ComponentDefinitionWithPlaceholder = {
1353
+ final: null,
1354
+ placeholder,
1355
+ waiting: null,
1356
+ }
1357
+ const wl = space._$componentWaitingList(path, is)
1358
+ if (wl) {
1359
+ wl.add((c) => {
1360
+ p.final = c
1361
+ p.placeholder = null
1362
+ p.waiting = null
1363
+ })
1364
+ p.waiting = wl
1365
+ }
1366
+ this._$using[k] = p
1367
+ } else {
1368
+ this._$using[k] = {
1369
+ final: space.getDefaultComponent(),
1370
+ placeholder: null,
1371
+ waiting: null,
1372
+ }
1373
+ }
1374
+ } else {
1375
+ triggerWarning(`cannot find component "${String(v)}" (when preparing behavior "${is}").`)
1376
+ }
1377
+ }
1378
+ }
1379
+
1380
+ // init generics
1381
+ if (typeof builder._$generics === 'object' && builder._$generics !== null) {
1382
+ const generics = this._$generics = [] as string[]
1383
+ const genericDefaults = Object.create(null) as {
1384
+ [alias: string]: null | GeneralComponentDefinition
1385
+ }
1386
+ this._$genericDefaults = genericDefaults
1387
+ const genericKeys = Object.keys(builder._$generics)
1388
+ for (let i = 0; i < genericKeys.length; i += 1) {
1389
+ const k = genericKeys[i]!
1390
+ const genericDef = builder._$generics[k]
1391
+ let defaultComp: GeneralComponentDefinition | null = null
1392
+ const d = genericDef === true ? undefined : genericDef?.default
1393
+ if (d !== undefined) {
1394
+ if (d instanceof ComponentDefinition) {
1395
+ defaultComp = d
1396
+ } else if (space) {
1397
+ defaultComp = space.getComponentByUrl(String(d), is)
1398
+ } else {
1399
+ triggerWarning(`cannot define generic "${k}" without a default implementor (when preparing behavior "${is}").`)
1400
+ }
1401
+ }
1402
+ generics.push(k)
1403
+ genericDefaults[k] = defaultComp
1404
+ }
1405
+ }
1406
+
1407
+ // init static data
1408
+ const staticData = builder._$staticData
1409
+ if (staticData) {
1410
+ if (!this._$staticData) {
1411
+ this._$staticData = staticData
1412
+ } else {
1413
+ shallowMerge(this._$staticData, staticData)
1414
+ }
1415
+ }
1416
+
1417
+ // init methods (methods should be initialized before others with function refs)
1418
+ const methods = builder._$methods
1419
+ for (let i = 0; i < methods.length; i += 1) {
1420
+ const { name, func } = methods[i]!
1421
+ this._$methodMap[name] = func
1422
+ }
1423
+
1424
+ // init properties
1425
+ const properties = builder._$properties
1426
+ if (properties !== undefined) {
1427
+ const initValueFuncs: { name: string, func: () => any }[] = []
1428
+ for (let i = 0; i < properties.length; i += 1) {
1429
+ const { name, def } = properties[i]!
1430
+ const shortHandDef = normalizePropertyTypeShortHand(def)
1431
+ let d: PropertyDefinition
1432
+ if (shortHandDef !== null) {
1433
+ d = shortHandDef
1434
+ } else {
1435
+ const propDef = def as PropertyOption<any, unknown>
1436
+ let type = normalizePropertyType(propDef.type)
1437
+ let optionalTypes: NormalizedPropertyType[] | null = null
1438
+ if (Array.isArray(propDef.optionalTypes)) {
1439
+ optionalTypes = propDef.optionalTypes.map(normalizePropertyType)
1440
+ if (optionalTypes.length > 0) {
1441
+ if (type === NormalizedPropertyType.Invalid || type === NormalizedPropertyType.Any) {
1442
+ type = optionalTypes[0]!
1443
+ }
1444
+ }
1445
+ }
1446
+ if (type === NormalizedPropertyType.Invalid) {
1447
+ triggerWarning(`the type of property "${name}" is illegal (when preparing behavior "${is}").`)
1448
+ }
1449
+ let value: DataValue = propDef.value
1450
+ if (propDef.value === undefined) {
1451
+ if (type === NormalizedPropertyType.String) value = ''
1452
+ else if (type === NormalizedPropertyType.Number) value = 0
1453
+ else if (type === NormalizedPropertyType.Boolean) value = false
1454
+ else if (type === NormalizedPropertyType.Array) value = []
1455
+ else value = null
1456
+ }
1457
+ let observer: ((newValue: any, oldValue: any) => void) | null
1458
+ if (typeof propDef.observer === 'function') {
1459
+ observer = propDef.observer
1460
+ } else if (propDef.observer) {
1461
+ observer = this._$methodMap[propDef.observer] || null
1462
+ } else {
1463
+ observer = null
1464
+ if (propDef.observer !== undefined) {
1465
+ triggerWarning(`the observer of property "${name}" is not a function (when preparing behavior "${is}").`)
1466
+ }
1467
+ }
1468
+ const reflectIdPrefix = !!propDef.reflectIdPrefix
1469
+ d = {
1470
+ type,
1471
+ optionalTypes,
1472
+ value,
1473
+ default: propDef.default,
1474
+ observer,
1475
+ reflectIdPrefix,
1476
+ }
1477
+ }
1478
+ this._$propertyMap[name] = d
1479
+ initValueFuncs.push({
1480
+ name,
1481
+ func: d.default === undefined ? () => simpleDeepCopy(d.value) : d.default,
1482
+ })
1483
+ }
1484
+ this._$data.push(() => {
1485
+ const ret: DataList = {}
1486
+ for (let i = 0; i < initValueFuncs.length; i += 1) {
1487
+ const { name, func } = initValueFuncs[i]!
1488
+ ret[name] = func()
1489
+ }
1490
+ return ret
1491
+ })
1492
+ }
1493
+
1494
+ // init dynamic data
1495
+ this._$data.push(...builder._$data)
1496
+
1497
+ // observers
1498
+ const observers = builder._$observers
1499
+ if (observers !== undefined) {
1500
+ for (let i = 0; i < observers.length; i += 1) {
1501
+ const { dataPaths, func, once } = observers[i]!
1502
+ const observer = typeof func === 'function' ? func : this._$methodMap[func]
1503
+ if (typeof observer === 'function') {
1504
+ this._$observers.push({ dataPaths, observer, once })
1505
+ } else {
1506
+ triggerWarning(`the "${String(observer)}" observer is not a function (when preparing behavior "${is}").`)
1507
+ }
1508
+ }
1509
+ }
1510
+
1511
+ // lifetimes
1512
+ const lifetimes = builder._$lifetimes
1513
+ this._$lifetimes.push(...lifetimes)
1514
+
1515
+ // page lifetimes
1516
+ const pageLifetimes = builder._$pageLifetimes
1517
+ if (pageLifetimes) {
1518
+ if (!this._$pageLifetimes) this._$pageLifetimes = []
1519
+ this._$pageLifetimes.push(...pageLifetimes)
1520
+ }
1521
+
1522
+ // listeners
1523
+ const listeners = builder._$listeners
1524
+ if (listeners !== undefined) {
1525
+ const keys = Object.keys(listeners)
1526
+ if (keys.length > 0) {
1527
+ this._$listeners = []
1528
+ for (let i = 0; i < keys.length; i += 1) {
1529
+ const k = keys[i]!
1530
+ const v = listeners[k]!
1531
+ const listener = typeof v === 'function' ? v : this._$methodMap[v]
1532
+ if (listener) {
1533
+ const dot = k.indexOf('.')
1534
+ let id: string
1535
+ let ev: string
1536
+ if (dot >= 0) {
1537
+ id = k.slice(0, dot)
1538
+ ev = k.slice(dot + 1)
1539
+ } else {
1540
+ id = ''
1541
+ ev = k
1542
+ }
1543
+ this._$listeners.push({ id, ev, listener })
1544
+ } else {
1545
+ triggerWarning(`the "${k}" listener is not a function or a method name (when preparing behavior "${is}").`)
1546
+ }
1547
+ }
1548
+ }
1549
+ }
1550
+
1551
+ // relations
1552
+ const relations = builder._$relations
1553
+ if (relations !== undefined) {
1554
+ if (!this._$relationMap) {
1555
+ this._$relationMap = Object.create(null) as { [name: string]: RelationDefinition }
1556
+ }
1557
+ for (let i = 0; i < relations.length; i += 1) {
1558
+ const { name: key, rel: relation } = relations[i]!
1559
+ if (relation === undefined || relation === null) continue
1560
+ const rel = normalizeRelation(is, key, relation)
1561
+ if (rel) this._$relationMap[key] = rel
1562
+ }
1563
+ }
1564
+
1565
+ // init funcs
1566
+ this._$init.push(...builder._$init)
1567
+
1568
+ flatAncestors.add(this as unknown as GeneralBehavior)
1569
+ }
1570
+
1571
+ /**
1572
+ * Get the template content
1573
+ *
1574
+ * This method is usually used by the template engine.
1575
+ */
1576
+ getTemplate() {
1577
+ return this._$template
1578
+ }
1579
+
1580
+ /** Check whether the `other` behavior is a dependent behavior of this behavior */
1581
+ hasBehavior(other: string | GeneralBehavior): boolean {
1582
+ if (this._$unprepared) this.prepare()
1583
+ if (other instanceof Behavior) {
1584
+ return this._$flatAncestors.has(other)
1585
+ }
1586
+ if (this.ownerSpace) {
1587
+ const b = this.ownerSpace.getBehaviorByUrl(other, this.is)
1588
+ if (!b) {
1589
+ return false
1590
+ }
1591
+ return this._$flatAncestors.has(b)
1592
+ }
1593
+ return false
1594
+ }
1595
+
1596
+ /**
1597
+ * Get the methods
1598
+ *
1599
+ * Only valid after `prepare` .
1600
+ */
1601
+ getMethods(): TMethod {
1602
+ return this._$methodMap as TMethod
1603
+ }
1604
+
1605
+ /** @internal */
1606
+ _$generateObserverTree(): DataGroupObserverTree {
1607
+ const ot = new DataGroupObserverTree(this._$propertyMap)
1608
+ const observers = this._$observers
1609
+ for (let i = 0; i < observers.length; i += 1) {
1610
+ const { dataPaths, observer } = observers[i]!
1611
+ ot.addObserver(observer, dataPaths)
1612
+ }
1613
+ return ot
1614
+ }
1615
+
1616
+ /** @internal */
1617
+ _$getAllLifetimeFuncs(): LifetimeFuncs {
1618
+ const ret = Object.create(null) as LifetimeFuncs
1619
+ const lifetimes = this._$lifetimes
1620
+ for (let i = 0; i < lifetimes.length; i += 1) {
1621
+ const { name, func } = lifetimes[i]!
1622
+ if (ret[name]) {
1623
+ ret[name]!.add(func)
1624
+ } else {
1625
+ const fa = ret[name] = new FuncArr()
1626
+ fa.add(func)
1627
+ }
1628
+ }
1629
+ return ret
1630
+ }
1631
+
1632
+ /** @internal */
1633
+ _$getAllPageLifetimeFuncs(): PageLifetimeFuncs {
1634
+ const ret = Object.create(null) as PageLifetimeFuncs
1635
+ const pageLifetimes = this._$pageLifetimes
1636
+ if (!pageLifetimes) return ret
1637
+ for (let i = 0; i < pageLifetimes.length; i += 1) {
1638
+ const { name, func } = pageLifetimes[i]!
1639
+ if (ret[name]) {
1640
+ ret[name]!.add(func)
1641
+ } else {
1642
+ const fa = ret[name] = new FuncArr()
1643
+ fa.add(func)
1644
+ }
1645
+ }
1646
+ return ret
1647
+ }
1648
+ }
1649
+
1650
+ export type GeneralBehavior = Behavior<
1651
+ Record<string, any>,
1652
+ Record<string, any>,
1653
+ Record<string, any>,
1654
+ any
1655
+ >