glass-easel-miniprogram-adapter 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 (50) hide show
  1. package/README.md +49 -0
  2. package/dist/glass_easel_miniprogram_adapter.d.ts +1 -0
  3. package/dist/glass_easel_miniprogram_adapter.js +2 -0
  4. package/dist/glass_easel_miniprogram_adapter.js.map +1 -0
  5. package/dist/types/src/backend.d.ts +51 -0
  6. package/dist/types/src/backend.d.ts.map +1 -0
  7. package/dist/types/src/behavior.d.ts +35 -0
  8. package/dist/types/src/behavior.d.ts.map +1 -0
  9. package/dist/types/src/component.d.ts +160 -0
  10. package/dist/types/src/component.d.ts.map +1 -0
  11. package/dist/types/src/index.d.ts +58 -0
  12. package/dist/types/src/index.d.ts.map +1 -0
  13. package/dist/types/src/selector_query.d.ts +98 -0
  14. package/dist/types/src/selector_query.d.ts.map +1 -0
  15. package/dist/types/src/space.d.ts +369 -0
  16. package/dist/types/src/space.d.ts.map +1 -0
  17. package/dist/types/src/types.d.ts +77 -0
  18. package/dist/types/src/types.d.ts.map +1 -0
  19. package/dist/types/src/utils.d.ts +2 -0
  20. package/dist/types/src/utils.d.ts.map +1 -0
  21. package/dist/types/tests/base/env.d.ts +3 -0
  22. package/dist/types/tests/base/env.d.ts.map +1 -0
  23. package/dist/types/tests/data_update.test.d.ts +2 -0
  24. package/dist/types/tests/data_update.test.d.ts.map +1 -0
  25. package/dist/types/tests/env.test.d.ts +2 -0
  26. package/dist/types/tests/env.test.d.ts.map +1 -0
  27. package/dist/types/tests/selector.test.d.ts +2 -0
  28. package/dist/types/tests/selector.test.d.ts.map +1 -0
  29. package/dist/types/tests/space.test.d.ts +2 -0
  30. package/dist/types/tests/space.test.d.ts.map +1 -0
  31. package/dist/types/tests/trait_behavior.test.d.ts +2 -0
  32. package/dist/types/tests/trait_behavior.test.d.ts.map +1 -0
  33. package/jest.config.js +13 -0
  34. package/package.json +32 -0
  35. package/src/backend.ts +106 -0
  36. package/src/behavior.ts +101 -0
  37. package/src/component.ts +476 -0
  38. package/src/index.ts +82 -0
  39. package/src/selector_query.ts +356 -0
  40. package/src/space.ts +1514 -0
  41. package/src/types.ts +85 -0
  42. package/src/utils.ts +3 -0
  43. package/tests/base/env.ts +20 -0
  44. package/tests/data_update.test.ts +92 -0
  45. package/tests/env.test.ts +141 -0
  46. package/tests/selector.test.ts +407 -0
  47. package/tests/space.test.ts +953 -0
  48. package/tests/trait_behavior.test.ts +141 -0
  49. package/tsconfig.json +11 -0
  50. package/webpack.config.js +78 -0
package/src/space.ts ADDED
@@ -0,0 +1,1514 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
2
+
3
+ import * as glassEasel from 'glass-easel'
4
+ import { MiniProgramEnv } from '.'
5
+ import {
6
+ ComponentStaticConfig,
7
+ ComponentDefinitionOptions,
8
+ StyleIsolation,
9
+ BehaviorDefinition,
10
+ ComponentDefinition,
11
+ PageDefinition,
12
+ utils as typeUtils,
13
+ } from './types'
14
+ import {
15
+ guid,
16
+ } from './utils'
17
+ import {
18
+ Behavior,
19
+ ComponentType,
20
+ DefinitionFilter,
21
+ GeneralBehavior,
22
+ TraitBehavior,
23
+ } from './behavior'
24
+ import {
25
+ AllData,
26
+ Component,
27
+ ComponentProto,
28
+ GeneralComponent,
29
+ } from './component'
30
+
31
+ // The page constructor
32
+ export interface PageConstructor {
33
+ <
34
+ TData extends DataList,
35
+ TNewExtraFields extends { [k: PropertyKey]: any },
36
+ >(
37
+ definition: PageDefinition<
38
+ TData,
39
+ TNewExtraFields
40
+ > & ThisType<Component<TData, Empty, TNewExtraFields, undefined>>,
41
+ ): void
42
+ }
43
+
44
+ // The component constructor
45
+ export interface ComponentConstructor {
46
+ (): ComponentBuilder
47
+ <
48
+ TData extends DataList,
49
+ TProperty extends PropertyList,
50
+ TMethod extends MethodList,
51
+ TComponentExport,
52
+ >(
53
+ definition: ComponentDefinition<
54
+ TData,
55
+ TProperty,
56
+ TMethod,
57
+ TComponentExport
58
+ > & ThisType<Component<TData, TProperty, TMethod, TComponentExport>>,
59
+ ): void
60
+ }
61
+
62
+ // The behavior constructor
63
+ export interface BehaviorConstructor {
64
+ (): BehaviorBuilder
65
+ <
66
+ TData extends DataList,
67
+ TProperty extends PropertyList,
68
+ TMethod extends MethodList,
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
+ TComponentExport,
71
+ >(
72
+ definition: BehaviorDefinition<
73
+ TData,
74
+ TProperty,
75
+ TMethod
76
+ >,
77
+ ): Behavior<TData, TProperty, TMethod, never>
78
+ trait<TIn extends { [key: string]: any }>(): TraitBehavior<TIn, TIn>
79
+ trait<
80
+ TIn extends { [key: string]: any },
81
+ TOut extends { [key: string]: any }
82
+ >(trans: (impl: TIn) => TOut): TraitBehavior<TIn, TOut>
83
+ }
84
+
85
+ /* The component registration environment */
86
+ export interface ComponentEnv {
87
+ Page: PageConstructor,
88
+ Component: ComponentConstructor,
89
+ Behavior: BehaviorConstructor,
90
+ }
91
+
92
+ type Empty = typeUtils.Empty
93
+ type DataList = typeUtils.DataList
94
+ type PropertyList = typeUtils.PropertyList
95
+ type PropertyType = typeUtils.PropertyType
96
+ type PropertyTypeToValueType<T extends PropertyType> = typeUtils.PropertyTypeToValueType<T>
97
+ type MethodList = typeUtils.MethodList
98
+ type ChainingFilterType = typeUtils.ChainingFilterType
99
+ type ComponentMethod = typeUtils.ComponentMethod
100
+ type TaggedMethod<Fn extends ComponentMethod> = typeUtils.TaggedMethod<Fn>
101
+ type ChainingFilterFunc<
102
+ TAddedFields extends { [key: string]: any },
103
+ TRemovedFields extends string = never
104
+ > = typeUtils.ChainingFilterFunc<TAddedFields, TRemovedFields>
105
+
106
+ export interface RelationHandler<TTarget, TOut> {
107
+ list(): TTarget[]
108
+ listAsTrait: TOut extends never
109
+ ? undefined
110
+ : () => TOut[]
111
+ }
112
+
113
+ export type TraitRelationParams<TOut extends { [key: string]: any }> = {
114
+ target: TraitBehavior<any, TOut>;
115
+ type:
116
+ | 'ancestor'
117
+ | 'descendant'
118
+ | 'parent'
119
+ | 'child'
120
+ | 'parent-common-node'
121
+ | 'child-common-node';
122
+ linked?: (target: GeneralComponent) => void;
123
+ linkChanged?: (target: GeneralComponent) => void;
124
+ unlinked?: (target: GeneralComponent) => void;
125
+ linkFailed?: (target: GeneralComponent) => void;
126
+ }
127
+
128
+ export type RelationParams = {
129
+ target?: string | ComponentType<any, any, any, any> | GeneralBehavior | TraitBehavior<any>;
130
+ type: 'ancestor' | 'descendant' | 'parent' | 'child' | 'parent-common-node' | 'child-common-node';
131
+ linked?: (target: GeneralComponent) => void;
132
+ linkChanged?: (target: GeneralComponent) => void;
133
+ unlinked?: (target: GeneralComponent) => void;
134
+ linkFailed?: (target: GeneralComponent) => void;
135
+ }
136
+
137
+ export type Lifetimes = {
138
+ created: () => void,
139
+ attached: () => void,
140
+ moved: () => void,
141
+ detached: () => void,
142
+ ready: () => void,
143
+ }
144
+
145
+ export interface BuilderContext<
146
+ TPrevData extends DataList,
147
+ TProperty extends PropertyList,
148
+ TMethodCaller
149
+ > extends ThisType<TMethodCaller> {
150
+ self: TMethodCaller;
151
+ data: typeUtils.DeepReadonly<typeUtils.DataWithPropertyValues<TPrevData, TProperty>>;
152
+ setData: (
153
+ newData: Partial<typeUtils.SetDataSetter<TPrevData>>, callback?: () => void,
154
+ ) => void;
155
+ implement: <TIn extends { [x: string]: any }>(
156
+ traitBehavior: TraitBehavior<TIn, any>,
157
+ impl: TIn
158
+ ) => void;
159
+ relation<TOut extends { [key: string]: any }>
160
+ (def: TraitRelationParams<TOut> & ThisType<TMethodCaller>): RelationHandler<any, TOut>
161
+ relation(def: RelationParams & ThisType<TMethodCaller>): RelationHandler<any, never>
162
+ observer<
163
+ P extends typeUtils.ObserverDataPathStrings<
164
+ typeUtils.DataWithPropertyValues<TPrevData, TProperty>
165
+ >,
166
+ V = typeUtils.DeepReadonly<
167
+ typeUtils. GetFromObserverPathString<typeUtils.DataWithPropertyValues<TPrevData, TProperty>, P>
168
+ >
169
+ >(
170
+ paths: P,
171
+ func: (newValue: V) => void,
172
+ ): void;
173
+ observer<
174
+ P extends typeUtils.ObserverDataPathStrings<
175
+ typeUtils.DataWithPropertyValues<TPrevData, TProperty>
176
+ >[],
177
+ V = {
178
+ [K in keyof P]: typeUtils.DeepReadonly<
179
+ typeUtils.GetFromObserverPathString<
180
+ typeUtils.DataWithPropertyValues<TPrevData, TProperty>,
181
+ P[K]
182
+ >
183
+ >
184
+ }
185
+ >(
186
+ paths: readonly [...P],
187
+ func: (...newValues: V extends any[] ? V : never) => void,
188
+ ): void;
189
+ lifetime: <L extends keyof Lifetimes>(name: L, func: Lifetimes[L]) => void;
190
+ pageLifetime: (name: string, func: (...args: any[]) => void) => void;
191
+ method: <Fn extends ComponentMethod>(func: Fn) => TaggedMethod<Fn>;
192
+ }
193
+
194
+ /**
195
+ * A component space with mini-program code manager
196
+ */
197
+ export class CodeSpace {
198
+ /** @internal */
199
+ _$env: MiniProgramEnv
200
+ private _$isMainSpace: boolean
201
+ private space: glassEasel.ComponentSpace
202
+ private styleScopeManager: glassEasel.StyleScopeManager
203
+ private staticConfigMap: { [path: string]: ComponentStaticConfig }
204
+ private styleSheetMap: { [path: string]: { url: string, styleScopeName: string | undefined } }
205
+ private compiledTemplateMap: { [path: string]: glassEasel.template.ComponentTemplate }
206
+ private waitingAliasMap: { [is: string]: string[] }
207
+ /** @internal */
208
+ _$sharedStyleScope: glassEasel.StyleScopeId
209
+ /** @internal */
210
+ _$styleIsolationMap: { [path: string]: StyleIsolation }
211
+ /** @internal */
212
+ _$overallBehavior: glassEasel.GeneralBehavior | null = null
213
+
214
+ /** @internal */
215
+ constructor(
216
+ env: MiniProgramEnv,
217
+ isMainSpace: boolean,
218
+ publicComponents: { [alias: string]: string },
219
+ globalCodeSpace?: CodeSpace,
220
+ ) {
221
+ this._$env = env
222
+ this._$isMainSpace = isMainSpace
223
+ this.space = new glassEasel.ComponentSpace(
224
+ '',
225
+ globalCodeSpace?.space,
226
+ globalCodeSpace?.space.styleScopeManager,
227
+ )
228
+ this.styleScopeManager = this.space.styleScopeManager
229
+ this.staticConfigMap = Object.create(null) as { [path: string]: ComponentStaticConfig }
230
+ this.styleSheetMap = Object.create(null) as
231
+ { [path: string]: { url: string, styleScopeName: string } }
232
+ this.compiledTemplateMap = Object.create(null) as
233
+ { [path: string]: glassEasel.template.ComponentTemplate }
234
+ this.waitingAliasMap = Object.create(null) as { [path: string]: string[] }
235
+ Object.keys(publicComponents).forEach((alias) => {
236
+ const is = publicComponents[alias]!
237
+ if (this.waitingAliasMap[is]) this.waitingAliasMap[is]!.push(alias)
238
+ else this.waitingAliasMap[is] = [alias]
239
+ })
240
+ this._$sharedStyleScope = this.styleScopeManager.register('')
241
+ this._$styleIsolationMap = Object.create(null) as { [path: string]: StyleIsolation }
242
+ }
243
+
244
+ isMainSpace(): boolean {
245
+ return this._$isMainSpace
246
+ }
247
+
248
+ /**
249
+ * Get the underlying component space
250
+ */
251
+ getComponentSpace(): glassEasel.ComponentSpace {
252
+ return this.space
253
+ }
254
+
255
+ /**
256
+ * Add a behavior for all components
257
+ *
258
+ * Must be called before any component registration so that the component will use this behavior.
259
+ * Set to `null` to disable this behavior.
260
+ */
261
+ setOverallBehavior(behavior: glassEasel.GeneralBehavior) {
262
+ this._$overallBehavior = behavior
263
+ }
264
+
265
+ /**
266
+ * Import another component space as a plugin (must be in the same environment)
267
+ *
268
+ * Components from the imported `codeSpace` can be used with URLs.
269
+ * If `domainAlias` is provided, the URL format is `plugin://DOMAIN_ALIAS/COMPONENT_SPACE_EXPORT_ALIAS` .
270
+ * If `privateImport` is enabled, another URL format is `plugin-private://DOMAIN/COMPONENT_IS` .
271
+ */
272
+ importCodeSpace(id: string, domainAlias?: string, privateImport = false) {
273
+ const cs = this._$env.getCodeSpace(id)
274
+ if (!cs) {
275
+ throw new Error(`There is no space "${id}" in the environment`)
276
+ }
277
+ if (domainAlias) this.space.importSpace(`plugin://${domainAlias}`, cs.space, false)
278
+ if (privateImport) this.space.importSpace(`plugin-private://${id}`, cs.space, true)
279
+ }
280
+
281
+ /**
282
+ * Add a style sheet URL with style scope name
283
+ *
284
+ * The URL should be recognized by the target backend.
285
+ * The `path` should not contain the `.wxss` suffix.
286
+ */
287
+ addStyleSheet(path: string, styleSheetUrl: string, styleScopeName?: string) {
288
+ this.styleSheetMap[path] = { url: styleSheetUrl, styleScopeName }
289
+ }
290
+
291
+ /**
292
+ * Get a style sheet URL
293
+ *
294
+ * The URL should be recognized by the target backend.
295
+ * The `path` should not contain the `.wxss` suffix.
296
+ */
297
+ getStyleSheet(path: string): string | undefined {
298
+ return this.styleSheetMap[path]?.url
299
+ }
300
+
301
+ getStyleScopeName(path: string): string | undefined {
302
+ return this.styleSheetMap[path]?.styleScopeName
303
+ }
304
+
305
+ /**
306
+ * Add a compiled template
307
+ *
308
+ * The content is the execution result of the generated string of the template compiler.
309
+ * The `path` should not contain the `.wxml` suffix.
310
+ */
311
+ addCompiledTemplate(path: string, content: glassEasel.template.ComponentTemplate) {
312
+ this.compiledTemplateMap[path] = content
313
+ }
314
+
315
+ /**
316
+ * Get a compiled template
317
+ *
318
+ * The content is the execution result of the generated string of the template compiler.
319
+ * The `path` should not contain the `.wxml` suffix.
320
+ */
321
+ getCompiledTemplate(path: string): glassEasel.template.ComponentTemplate | undefined {
322
+ return this.compiledTemplateMap[path]
323
+ }
324
+
325
+ /**
326
+ * Add a static JSON config
327
+ *
328
+ * The `path` should not contain the `.json` suffix.
329
+ */
330
+ addComponentStaticConfig(path: string, content: ComponentStaticConfig) {
331
+ this.staticConfigMap[path] = content
332
+ }
333
+
334
+ /**
335
+ * Get a static JSON config
336
+ *
337
+ * The `path` should not contain the `.json` suffix.
338
+ */
339
+ getComponentStaticConfig(path: string): ComponentStaticConfig | undefined {
340
+ return this.staticConfigMap[path]
341
+ }
342
+
343
+ /** @internal */
344
+ prepareComponentOptions(
345
+ is: string,
346
+ options?: ComponentDefinitionOptions,
347
+ ): [glassEasel.ComponentOptions, StyleIsolation] {
348
+ // accept some fields from static JSON config
349
+ const staticConfig = this.staticConfigMap[is]
350
+ const pureDataPatternString = staticConfig?.pureDataPattern
351
+ const pureDataPattern = pureDataPatternString
352
+ ? new RegExp(pureDataPatternString)
353
+ : options?.pureDataPattern
354
+
355
+ // calculate style scope
356
+ const addGlobalClass = staticConfig?.addGlobalClass
357
+ let styleIsolation = staticConfig?.styleIsolation
358
+ if (styleIsolation === undefined) {
359
+ if (staticConfig?.component) {
360
+ styleIsolation = addGlobalClass ? StyleIsolation.ApplyShared : StyleIsolation.Isolated
361
+ } else {
362
+ styleIsolation = StyleIsolation.Shared
363
+ }
364
+ }
365
+ let styleScope: glassEasel.StyleScopeId | undefined
366
+ let extraStyleScope: glassEasel.StyleScopeId | undefined
367
+ if (
368
+ styleIsolation === StyleIsolation.ApplyShared
369
+ || styleIsolation === StyleIsolation.PageApplyShared
370
+ ) {
371
+ styleScope = this.styleScopeManager.register(this.getStyleScopeName(is) || `__${guid()}`)
372
+ extraStyleScope = this._$sharedStyleScope
373
+ } else if (
374
+ (styleIsolation === StyleIsolation.Shared || styleIsolation === StyleIsolation.PageShared)
375
+ && this._$isMainSpace
376
+ ) {
377
+ styleScope = this._$sharedStyleScope
378
+ } else {
379
+ styleScope = this.styleScopeManager.register(this.getStyleScopeName(is) || `__${guid()}`)
380
+ }
381
+
382
+ // construct component options
383
+ const ret: glassEasel.ComponentOptions = {
384
+ idPrefixGenerator: guid,
385
+ multipleSlots: options?.multipleSlots,
386
+ pureDataPattern,
387
+ virtualHost: options?.virtualHost,
388
+ styleScope,
389
+ extraStyleScope,
390
+ dataDeepCopy: options?.dataDeepCopy,
391
+ propertyPassingDeepCopy: options?.propertyPassingDeepCopy,
392
+ propertyEarlyInit: options?.propertyEarlyInit,
393
+ }
394
+ return [ret, styleIsolation]
395
+ }
396
+
397
+ /**
398
+ * Create a component definition environment
399
+ *
400
+ * During the sync execution of `cb` , `Component` and `Behavior` global vars will be available.
401
+ * `globalObject` should be the JavaScript global object, a.k.a. `window` in DOM.
402
+ * Some global variables, a.k.a `Component` `Behavior` `Page` ,
403
+ * will be written into `globalObject` .
404
+ * `path` should be the component path (without ".js" extension).
405
+ */
406
+ globalComponentEnv<T>(globalObject: any, path: string, cb: () => T): T {
407
+ return this.componentEnv(path, ({ Page, Component, Behavior }) => {
408
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
409
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
410
+ const oldPage = globalObject.Page
411
+ const oldComponent = globalObject.Component
412
+ const oldBehavior = globalObject.Behavior
413
+ globalObject.Page = Page
414
+ globalObject.Component = Component
415
+ globalObject.Behavior = Behavior
416
+ const ret = cb()
417
+ globalObject.Page = oldPage
418
+ globalObject.Component = oldComponent
419
+ globalObject.Behavior = oldBehavior
420
+ /* eslint-enable @typescript-eslint/no-unsafe-member-access */
421
+ /* eslint-enable @typescript-eslint/no-unsafe-assignment */
422
+ return ret
423
+ })
424
+ }
425
+
426
+ /**
427
+ * Create a component definition environment
428
+ *
429
+ * Like `globalComponentEnv` , but the global variables are given as callback arguments.
430
+ */
431
+ componentEnv<T>(path: string, cb: (env: ComponentEnv) => T): T {
432
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
433
+ const self = this
434
+
435
+ // The page constructor
436
+ function pageConstructor<
437
+ TData extends DataList,
438
+ TNewExtraFields extends { [k: PropertyKey]: any },
439
+ >(
440
+ definition: PageDefinition<
441
+ TData,
442
+ TNewExtraFields
443
+ > & ThisType<Component<TData, Empty, TNewExtraFields, undefined>>,
444
+ ) {
445
+ return self.component(path).pageDefinition(definition).register()
446
+ }
447
+
448
+ // The component constructor
449
+ function componentConstructor(): ComponentBuilder
450
+ function componentConstructor<
451
+ TData extends DataList,
452
+ TProperty extends PropertyList,
453
+ TMethod extends MethodList,
454
+ TComponentExport,
455
+ >(
456
+ definition: ComponentDefinition<
457
+ TData,
458
+ TProperty,
459
+ TMethod,
460
+ TComponentExport
461
+ >,
462
+ ): void
463
+ function componentConstructor<
464
+ TData extends DataList,
465
+ TProperty extends PropertyList,
466
+ TMethod extends MethodList,
467
+ TComponentExport,
468
+ >(
469
+ definition?: ComponentDefinition<
470
+ TData,
471
+ TProperty,
472
+ TMethod,
473
+ TComponentExport
474
+ >,
475
+ ) {
476
+ if (definition !== undefined) {
477
+ return self.component(path).definition(definition).register()
478
+ }
479
+ return self.component(path)
480
+ }
481
+
482
+ // The behavior constructor
483
+ function behaviorConstructor(): BehaviorBuilder
484
+ function behaviorConstructor<
485
+ TData extends DataList,
486
+ TProperty extends PropertyList,
487
+ TMethod extends MethodList,
488
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
489
+ TComponentExport,
490
+ >(
491
+ definition: BehaviorDefinition<
492
+ TData,
493
+ TProperty,
494
+ TMethod
495
+ >,
496
+ ): Behavior<TData, TProperty, TMethod, never>
497
+ function behaviorConstructor<
498
+ TData extends DataList,
499
+ TProperty extends PropertyList,
500
+ TMethod extends MethodList,
501
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
502
+ TComponentExport,
503
+ >(
504
+ definition?: BehaviorDefinition<
505
+ TData,
506
+ TProperty,
507
+ TMethod
508
+ >,
509
+ ) {
510
+ if (definition !== undefined) {
511
+ return self.behavior().definition(definition).register()
512
+ }
513
+ return self.behavior()
514
+ }
515
+ behaviorConstructor.trait = self.traitBehavior.bind(self)
516
+
517
+ // prepare global vars and execute
518
+ return cb({
519
+ Page: pageConstructor,
520
+ Component: componentConstructor,
521
+ Behavior: behaviorConstructor,
522
+ })
523
+ }
524
+
525
+ /**
526
+ * Build a component outside of a definition environment
527
+ *
528
+ * The method do not need a definition environment, so the global object pollution is avoided.
529
+ * `path` should be the component path (without ".js" extension).
530
+ */
531
+ component(path: string): ComponentBuilder {
532
+ return ComponentBuilder.create(this, path, this.waitingAliasMap[path])
533
+ }
534
+
535
+ /**
536
+ * Build a behavior outside of a definition environment
537
+ *
538
+ * The method do not need a definition environment, so the global object pollution is avoided.
539
+ * `path` should be the component path (without ".js" extension).
540
+ */
541
+ behavior(): BehaviorBuilder {
542
+ return BehaviorBuilder.create(this)
543
+ }
544
+
545
+ /**
546
+ * Define a trait behavior
547
+ */
548
+ traitBehavior<TIn extends { [key: string]: any }>(): TraitBehavior<TIn, TIn>
549
+ traitBehavior<
550
+ TIn extends { [key: string]: any },
551
+ TOut extends { [key: string]: any }
552
+ >(trans: (impl: TIn) => TOut): TraitBehavior<TIn, TOut>
553
+ traitBehavior<
554
+ TIn extends { [key: string]: any },
555
+ TOut extends { [key: string]: any }
556
+ >(trans?: (impl: TIn) => TOut): TraitBehavior<TIn, TOut> {
557
+ if (trans === undefined) {
558
+ return new TraitBehavior(this.space.defineTraitBehavior<TIn>())
559
+ }
560
+ return new TraitBehavior(this.space.defineTraitBehavior<TIn, TOut>(trans))
561
+ }
562
+ }
563
+
564
+ type ResolveBehaviorBuilder<B, TChainingFilter extends ChainingFilterType> =
565
+ typeUtils.IsNever<TChainingFilter> extends false
566
+ ? TChainingFilter extends ChainingFilterType
567
+ ? Omit<B, TChainingFilter['remove']> & TChainingFilter['add']
568
+ : B
569
+ : B
570
+
571
+ export class BaseBehaviorBuilder<
572
+ TPrevData extends DataList = Empty,
573
+ TData extends DataList = Empty,
574
+ TProperty extends PropertyList = Empty,
575
+ TMethod extends MethodList = Empty,
576
+ TChainingFilter extends ChainingFilterType = never,
577
+ TPendingChainingFilter extends ChainingFilterType = never,
578
+ TComponentExport = undefined,
579
+ > {
580
+ protected _$codeSpace!: CodeSpace
581
+ protected _$!: glassEasel.BehaviorBuilder<
582
+ TPrevData,
583
+ TData,
584
+ TProperty,
585
+ TMethod,
586
+ TChainingFilter,
587
+ TPendingChainingFilter
588
+ >
589
+ protected _$parents: GeneralBehavior[] = []
590
+
591
+ /** Implement a trait behavior */
592
+ implement<TIn extends { [key: string]: any }>(
593
+ traitBehavior: TraitBehavior<TIn, any>,
594
+ impl: TIn,
595
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
596
+ this._$.implement(traitBehavior._$, impl)
597
+ return this as any
598
+ }
599
+
600
+ /** Add external classes */
601
+ externalClasses(list: string[]): this {
602
+ this._$.externalClasses(list)
603
+ return this
604
+ }
605
+
606
+ data<T extends DataList>(
607
+ gen: (() => typeUtils.NewFieldList<AllData<TData, TProperty>, T>),
608
+ ): ResolveBehaviorBuilder<
609
+ BaseBehaviorBuilder<
610
+ T,
611
+ TData & T,
612
+ TProperty,
613
+ TMethod,
614
+ TChainingFilter,
615
+ TPendingChainingFilter,
616
+ TComponentExport
617
+ >,
618
+ TChainingFilter
619
+ > {
620
+ this._$.data(gen)
621
+ return this as any
622
+ }
623
+
624
+ property<
625
+ N extends string,
626
+ T extends PropertyType,
627
+ V extends PropertyTypeToValueType<T>,
628
+ >(
629
+ name: N,
630
+ def: N extends keyof (TData & TProperty) ? never : typeUtils.PropertyListItem<T, V>,
631
+ ): ResolveBehaviorBuilder<
632
+ BaseBehaviorBuilder<
633
+ TPrevData,
634
+ TData,
635
+ TProperty & Record<N, unknown extends V ? T : typeUtils.PropertyOption<T, V>>,
636
+ TMethod,
637
+ TChainingFilter,
638
+ TPendingChainingFilter,
639
+ TComponentExport
640
+ >,
641
+ TChainingFilter
642
+ > {
643
+ this._$.property(name, def)
644
+ return this as any
645
+ }
646
+
647
+ /**
648
+ * Add some public methods
649
+ *
650
+ * The public method can be used as an event handler, and can be visited in component instance.
651
+ */
652
+ methods<
653
+ T extends MethodList,
654
+ >(
655
+ funcs: T & ThisType<Component<TData, TProperty, TMethod & T, any>>,
656
+ ): ResolveBehaviorBuilder<
657
+ BaseBehaviorBuilder<
658
+ TPrevData,
659
+ TData,
660
+ TProperty,
661
+ TMethod & T,
662
+ TChainingFilter,
663
+ TPendingChainingFilter,
664
+ TComponentExport
665
+ >,
666
+ TChainingFilter
667
+ > {
668
+ this._$.methods(funcs)
669
+ return this as any
670
+ }
671
+
672
+ /**
673
+ * Add a data observer
674
+ */
675
+ observer<
676
+ P extends typeUtils.ObserverDataPathStrings<
677
+ typeUtils.DataWithPropertyValues<TPrevData, TProperty>
678
+ >,
679
+ V = typeUtils.DeepReadonly<
680
+ typeUtils.GetFromObserverPathString<typeUtils.DataWithPropertyValues<TPrevData, TProperty>, P>
681
+ >
682
+ >(
683
+ paths: P,
684
+ func: (this: Component<TData, TProperty, TMethod, any>, newValue: V) => void,
685
+ once?: boolean,
686
+ ): ResolveBehaviorBuilder<this, TChainingFilter>;
687
+ observer<
688
+ P extends typeUtils.ObserverDataPathStrings<
689
+ typeUtils.DataWithPropertyValues<TPrevData, TProperty>
690
+ >[],
691
+ V = {
692
+ [K in keyof P]: typeUtils.DeepReadonly<
693
+ typeUtils.GetFromObserverPathString<
694
+ typeUtils.DataWithPropertyValues<TPrevData, TProperty>,
695
+ P[K]
696
+ >
697
+ >
698
+ }
699
+ >(
700
+ paths: readonly [...P],
701
+ func: (
702
+ this: Component<TData, TProperty, TMethod, any>,
703
+ ...newValues: V extends any[] ? V : never
704
+ ) => void,
705
+ once?: boolean,
706
+ ): ResolveBehaviorBuilder<this, TChainingFilter>;
707
+ observer(
708
+ paths: string | readonly string[],
709
+ func: (this: Component<TData, TProperty, TMethod, any>, ...args: any[]) => any,
710
+ once = false,
711
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
712
+ this._$.observer(paths as any, func as any, once)
713
+ return this as any
714
+ }
715
+
716
+ /**
717
+ * Add a lifetime callback
718
+ */
719
+ lifetime<L extends keyof Lifetimes>(
720
+ name: L,
721
+ func: (
722
+ this: Component<TData, TProperty, TMethod, any>,
723
+ ...args: Parameters<Lifetimes[L]>
724
+ ) => ReturnType<Lifetimes[L]>,
725
+ once = false,
726
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
727
+ this._$.lifetime(name, func as any, once)
728
+ return this as any
729
+ }
730
+
731
+ /**
732
+ * Add a page-lifetime callback
733
+ */
734
+ pageLifetime(
735
+ name: string,
736
+ func: (this: Component<TData, TProperty, TMethod, any>, ...args: any[]) => any,
737
+ once = false,
738
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
739
+ this._$.pageLifetime(name, func as any, once)
740
+ return this as any
741
+ }
742
+
743
+ /**
744
+ * Add a relation
745
+ */
746
+ relation(
747
+ name: string,
748
+ rel: RelationParams & ThisType<Component<TData, TProperty, TMethod, TComponentExport>>,
749
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
750
+ const target = (rel.target instanceof Behavior || rel.target instanceof ComponentType)
751
+ ? rel.target._$
752
+ : rel.target
753
+ this._$.relation(name, {
754
+ target,
755
+ type: rel.type,
756
+ linked: rel.linked,
757
+ linkChanged: rel.linkChanged,
758
+ unlinked: rel.unlinked,
759
+ linkFailed: rel.linkFailed,
760
+ } as any)
761
+ return this as any
762
+ }
763
+
764
+ init<TExport extends Record<string, TaggedMethod<(...args: any[]) => any>> | void>(
765
+ func: (
766
+ this: Component<TData, TProperty, TMethod, TComponentExport>,
767
+ builderContext: BuilderContext<
768
+ TPrevData,
769
+ TProperty,
770
+ Component<TData, TProperty, TMethod, TComponentExport>
771
+ >,
772
+ ) => TExport,
773
+ // eslint-disable-next-line function-paren-newline
774
+ ): ResolveBehaviorBuilder<
775
+ BaseBehaviorBuilder<
776
+ TPrevData,
777
+ TData,
778
+ TProperty,
779
+ TMethod,
780
+ TChainingFilter,
781
+ TPendingChainingFilter,
782
+ TComponentExport
783
+ >,
784
+ TChainingFilter
785
+ > {
786
+ this._$.init(function ({
787
+ data,
788
+ setData,
789
+ implement,
790
+ relation,
791
+ observer,
792
+ lifetime,
793
+ pageLifetime,
794
+ method,
795
+ }) {
796
+ const relationInit = ((rel: RelationParams | TraitRelationParams<any>) => {
797
+ if (rel.target instanceof TraitBehavior) {
798
+ return relation({
799
+ target: rel.target._$,
800
+ type: rel.type,
801
+ linked: rel.linked,
802
+ linkChanged: rel.linkChanged,
803
+ unlinked: rel.unlinked,
804
+ linkFailed: rel.linkFailed,
805
+ } as any)
806
+ }
807
+ const target = (rel.target instanceof Behavior || rel.target instanceof ComponentType)
808
+ ? rel.target._$
809
+ : rel.target
810
+ return relation({
811
+ target,
812
+ type: rel.type,
813
+ linked: rel.linked,
814
+ linkChanged: rel.linkChanged,
815
+ unlinked: rel.unlinked,
816
+ linkFailed: rel.linkFailed,
817
+ } as any)
818
+ }) as BuilderContext<TPrevData, TProperty, TMethod>['relation']
819
+
820
+ const implementInit = ((traitBehavior: TraitBehavior<any>, impl: Record<string, any>) => {
821
+ implement(traitBehavior._$, impl)
822
+ }) as BuilderContext<TPrevData, TProperty, TMethod>['implement']
823
+
824
+ const methodCaller = this as unknown as Component<TData, TProperty, TMethod, TComponentExport>
825
+
826
+ func.call(methodCaller, {
827
+ self: methodCaller,
828
+ data,
829
+ setData: (newData, callback) => {
830
+ setData(newData)
831
+ if (callback) {
832
+ glassEasel.triggerRender(methodCaller._$, () => {
833
+ callback()
834
+ })
835
+ }
836
+ },
837
+ implement: implementInit,
838
+ relation: relationInit,
839
+ observer,
840
+ lifetime,
841
+ pageLifetime,
842
+ method,
843
+ })
844
+ })
845
+ return this as any
846
+ }
847
+
848
+ definition<
849
+ TNewData extends DataList = Empty,
850
+ TNewProperty extends PropertyList = Empty,
851
+ TNewMethod extends MethodList = Empty,
852
+ >(
853
+ def: BehaviorDefinition<
854
+ TNewData,
855
+ TNewProperty,
856
+ TNewMethod
857
+ > & ThisType<
858
+ Component<
859
+ TData & TNewData,
860
+ TProperty & TNewProperty,
861
+ TMethod & TNewMethod,
862
+ TComponentExport
863
+ >
864
+ >,
865
+ ): ResolveBehaviorBuilder<
866
+ BaseBehaviorBuilder<
867
+ TPrevData,
868
+ TData & TNewData,
869
+ TProperty & TNewProperty,
870
+ TMethod & TNewMethod,
871
+ TChainingFilter,
872
+ TPendingChainingFilter,
873
+ TComponentExport
874
+ >,
875
+ TChainingFilter
876
+ > {
877
+ def.behaviors?.forEach((beh) => beh._$bindedDefinitionFilter?.(def))
878
+ const inner = this._$
879
+ const {
880
+ behaviors,
881
+ properties: rawProperties,
882
+ data: rawData,
883
+ observers: rawObservers,
884
+ methods,
885
+ created,
886
+ attached,
887
+ ready,
888
+ moved,
889
+ detached,
890
+ lifetimes: rawLifetimes,
891
+ pageLifetimes: rawPageLifetimes,
892
+ relations: rawRelations,
893
+ externalClasses,
894
+ } = def
895
+ behaviors?.forEach((beh) => {
896
+ this._$parents.push(beh as GeneralBehavior)
897
+ inner.behavior(beh._$)
898
+ })
899
+ if (rawData !== undefined) {
900
+ if (typeof rawData === 'function') {
901
+ inner.data(rawData as any)
902
+ } else {
903
+ inner.staticData(rawData as any)
904
+ }
905
+ }
906
+ if (rawProperties !== undefined) {
907
+ const keys = Object.keys(rawProperties)
908
+ for (let i = 0; i < keys.length; i += 1) {
909
+ const name = keys[i]!
910
+ inner.property(name, rawProperties[name]!)
911
+ }
912
+ }
913
+ if (rawObservers !== undefined) {
914
+ if (Array.isArray(rawObservers)) {
915
+ for (let i = 0; i < rawObservers.length; i += 1) {
916
+ const { fields, observer } = rawObservers[i]!
917
+ inner.observer(fields || '**' as any, observer)
918
+ }
919
+ } else {
920
+ const keys = Object.keys(rawObservers)
921
+ for (let i = 0; i < keys.length; i += 1) {
922
+ const fields = keys[i]!
923
+ const observer = rawObservers[fields]!
924
+ inner.observer(fields as any, observer)
925
+ }
926
+ }
927
+ }
928
+ if (methods) inner.methods(methods)
929
+ if (created && rawLifetimes?.created === undefined) inner.lifetime('created', created, true)
930
+ if (attached && rawLifetimes?.attached === undefined) inner.lifetime('attached', attached, true)
931
+ if (ready && rawLifetimes?.ready === undefined) inner.lifetime('ready', ready, true)
932
+ if (moved && rawLifetimes?.moved === undefined) inner.lifetime('moved', moved, true)
933
+ if (detached && rawLifetimes?.detached === undefined) inner.lifetime('detached', detached, true)
934
+ if (rawLifetimes) {
935
+ const keys = Object.keys(rawLifetimes)
936
+ for (let i = 0; i < keys.length; i += 1) {
937
+ const name = keys[i]!
938
+ const func = rawLifetimes[name]!
939
+ inner.lifetime(name as any, func, true)
940
+ }
941
+ }
942
+ if (rawPageLifetimes) {
943
+ const keys = Object.keys(rawPageLifetimes)
944
+ for (let i = 0; i < keys.length; i += 1) {
945
+ const name = keys[i]!
946
+ const func = rawPageLifetimes[name]!
947
+ inner.pageLifetime(name, func, true)
948
+ }
949
+ }
950
+ if (rawRelations) {
951
+ const keys = Object.keys(rawRelations)
952
+ for (let i = 0; i < keys.length; i += 1) {
953
+ const name = keys[i]!
954
+ const rel = rawRelations[name]!
955
+ inner.relation(name, rel)
956
+ }
957
+ }
958
+ if (externalClasses) inner.externalClasses(externalClasses)
959
+ return this as any
960
+ }
961
+ }
962
+
963
+ export class BehaviorBuilder<
964
+ TPrevData extends DataList = Empty,
965
+ TData extends DataList = Empty,
966
+ TProperty extends PropertyList = Empty,
967
+ TMethod extends MethodList = Empty,
968
+ TChainingFilter extends ChainingFilterType = never,
969
+ TPendingChainingFilter extends ChainingFilterType = never,
970
+ TComponentExport = undefined,
971
+ > extends BaseBehaviorBuilder<
972
+ TPrevData,
973
+ TData,
974
+ TProperty,
975
+ TMethod,
976
+ TChainingFilter,
977
+ TPendingChainingFilter,
978
+ TComponentExport
979
+ > {
980
+ private _$definitionFilter: DefinitionFilter | undefined
981
+
982
+ /** @internal */
983
+ static create(codeSpace: CodeSpace): BehaviorBuilder {
984
+ const ret = new BehaviorBuilder()
985
+ ret._$codeSpace = codeSpace
986
+ ret._$ = codeSpace.getComponentSpace()
987
+ .defineWithMethodCaller()
988
+ return ret
989
+ }
990
+
991
+ /** Define a chaining filter */
992
+ chainingFilter<
993
+ TAddedFields extends { [key: string]: any },
994
+ TRemovedFields extends string = never,
995
+ >(
996
+ func: ChainingFilterFunc<TAddedFields, TRemovedFields>,
997
+ ): ResolveBehaviorBuilder<
998
+ BehaviorBuilder<TPrevData, TData, TProperty, TMethod, TChainingFilter, {
999
+ add: TAddedFields,
1000
+ remove: TRemovedFields,
1001
+ }, TComponentExport>,
1002
+ TChainingFilter
1003
+ > {
1004
+ this._$.chainingFilter(func as any)
1005
+ return this as any
1006
+ }
1007
+
1008
+ /** Use another behavior */
1009
+ behavior<
1010
+ UData extends DataList,
1011
+ UProperty extends PropertyList,
1012
+ UMethod extends MethodList,
1013
+ UChainingFilter extends ChainingFilterType,
1014
+ >(
1015
+ behavior: Behavior<UData, UProperty, UMethod, UChainingFilter>,
1016
+ ): ResolveBehaviorBuilder<
1017
+ BehaviorBuilder<
1018
+ TPrevData,
1019
+ TData & UData,
1020
+ TProperty & UProperty,
1021
+ TMethod & UMethod,
1022
+ UChainingFilter,
1023
+ TPendingChainingFilter,
1024
+ TComponentExport
1025
+ >,
1026
+ UChainingFilter
1027
+ > {
1028
+ this._$parents.push(behavior as GeneralBehavior)
1029
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1030
+ this._$ = this._$.behavior(behavior._$)
1031
+ return this as any
1032
+ }
1033
+
1034
+ /**
1035
+ * Add some template data fields
1036
+ *
1037
+ * It does not support raw data, but a `gen` function which returns the new data fields.
1038
+ * The `gen` function executes once during component creation.
1039
+ */
1040
+ override data<T extends DataList>(
1041
+ gen: (() => typeUtils.NewFieldList<AllData<TData, TProperty>, T>),
1042
+ ): ResolveBehaviorBuilder<
1043
+ BehaviorBuilder<
1044
+ T,
1045
+ TData & T,
1046
+ TProperty,
1047
+ TMethod,
1048
+ TChainingFilter,
1049
+ TPendingChainingFilter,
1050
+ TComponentExport
1051
+ >,
1052
+ TChainingFilter
1053
+ > {
1054
+ return super.data(gen) as any
1055
+ }
1056
+
1057
+ /**
1058
+ * Add a single property
1059
+ *
1060
+ * The property name should be different from other properties.
1061
+ */
1062
+ override property<
1063
+ N extends string,
1064
+ T extends PropertyType,
1065
+ V extends PropertyTypeToValueType<T>,
1066
+ >(
1067
+ name: N,
1068
+ def: N extends keyof (TData & TProperty) ? never : typeUtils.PropertyListItem<T, V>,
1069
+ ): ResolveBehaviorBuilder<
1070
+ BehaviorBuilder<
1071
+ TPrevData,
1072
+ TData,
1073
+ TProperty & Record<N, unknown extends V ? T : typeUtils.PropertyOption<T, V>>,
1074
+ TMethod,
1075
+ TChainingFilter,
1076
+ TPendingChainingFilter,
1077
+ TComponentExport
1078
+ >,
1079
+ TChainingFilter
1080
+ > {
1081
+ return super.property(name, def) as any
1082
+ }
1083
+
1084
+ /**
1085
+ * Add some public methods
1086
+ *
1087
+ * The public method can be used as an event handler, and can be visited in component instance.
1088
+ */
1089
+ override methods<
1090
+ T extends MethodList,
1091
+ >(
1092
+ funcs: T & ThisType<Component<TData, TProperty, TMethod & T, any>>,
1093
+ ): ResolveBehaviorBuilder<
1094
+ BehaviorBuilder<
1095
+ TPrevData,
1096
+ TData,
1097
+ TProperty,
1098
+ TMethod & T,
1099
+ TChainingFilter,
1100
+ TPendingChainingFilter,
1101
+ TComponentExport
1102
+ >,
1103
+ TChainingFilter
1104
+ > {
1105
+ return super.methods(funcs) as any
1106
+ }
1107
+
1108
+ /**
1109
+ * Execute a function while component instance creation
1110
+ *
1111
+ * A `BuilderContext` is provided to tweak the component creation progress.
1112
+ * The return value is used as the "export" value of the behavior,
1113
+ * which can be imported by other behaviors.
1114
+ */
1115
+ override init<TExport extends Record<string, TaggedMethod<(...args: any[]) => any>> | void>(
1116
+ func: (
1117
+ this: Component<TData, TProperty, TMethod, TComponentExport>,
1118
+ builderContext: BuilderContext<
1119
+ TPrevData,
1120
+ TProperty,
1121
+ Component<TData, TProperty, TMethod, TComponentExport>
1122
+ >,
1123
+ ) => TExport,
1124
+ // eslint-disable-next-line function-paren-newline
1125
+ ): ResolveBehaviorBuilder<
1126
+ BehaviorBuilder<
1127
+ TPrevData,
1128
+ TData,
1129
+ TProperty,
1130
+ TMethod,
1131
+ TChainingFilter,
1132
+ TPendingChainingFilter,
1133
+ TComponentExport
1134
+ >,
1135
+ TChainingFilter
1136
+ > {
1137
+ return super.init(func) as any
1138
+ }
1139
+
1140
+ /** Apply a classic definition object */
1141
+ override definition<
1142
+ TNewData extends DataList = Empty,
1143
+ TNewProperty extends PropertyList = Empty,
1144
+ TNewMethod extends MethodList = Empty,
1145
+ >(
1146
+ def: BehaviorDefinition<
1147
+ TNewData,
1148
+ TNewProperty,
1149
+ TNewMethod
1150
+ > & ThisType<
1151
+ Component<
1152
+ TData & TNewData,
1153
+ TProperty & TNewProperty,
1154
+ TMethod & TNewMethod,
1155
+ TComponentExport
1156
+ >
1157
+ >,
1158
+ ): ResolveBehaviorBuilder<
1159
+ BehaviorBuilder<
1160
+ TPrevData,
1161
+ TData & TNewData,
1162
+ TProperty & TNewProperty,
1163
+ TMethod & TNewMethod,
1164
+ TChainingFilter,
1165
+ TPendingChainingFilter,
1166
+ TComponentExport
1167
+ >,
1168
+ TChainingFilter
1169
+ > {
1170
+ super.definition(def)
1171
+ if (def.definitionFilter) this._$definitionFilter = def.definitionFilter
1172
+ return this as any
1173
+ }
1174
+
1175
+ /**
1176
+ * Finish the behavior definition process
1177
+ */
1178
+ register(): Behavior<TData, TProperty, TMethod, TPendingChainingFilter> {
1179
+ return new Behavior(
1180
+ this._$.registerBehavior(),
1181
+ this._$parents,
1182
+ this._$definitionFilter,
1183
+ )
1184
+ }
1185
+ }
1186
+
1187
+ /**
1188
+ * A direct way to create a component
1189
+ */
1190
+ export class ComponentBuilder<
1191
+ TPrevData extends DataList = Empty,
1192
+ TData extends DataList = Empty,
1193
+ TProperty extends PropertyList = Empty,
1194
+ TMethod extends MethodList = Empty,
1195
+ TChainingFilter extends ChainingFilterType = never,
1196
+ TPendingChainingFilter extends ChainingFilterType = never,
1197
+ TComponentExport = undefined,
1198
+ > extends BaseBehaviorBuilder<
1199
+ TPrevData,
1200
+ TData,
1201
+ TProperty,
1202
+ TMethod,
1203
+ TChainingFilter,
1204
+ TPendingChainingFilter,
1205
+ TComponentExport
1206
+ > {
1207
+ private _$is!: string
1208
+ private _$alias?: string[]
1209
+ private _$options?: ComponentDefinitionOptions
1210
+ private _$export?: (source: GeneralComponent | null) => TComponentExport
1211
+ private _$proto?: ComponentProto<TData, TProperty, TMethod, TComponentExport>
1212
+
1213
+ /** @internal */
1214
+ static create(codeSpace: CodeSpace, is: string, alias?: string[]) {
1215
+ const ret = new ComponentBuilder()
1216
+ const overallBehavior = codeSpace._$overallBehavior
1217
+ ret._$codeSpace = codeSpace
1218
+ ret._$ = codeSpace.getComponentSpace()
1219
+ .defineWithMethodCaller(is || '')
1220
+ ret._$is = is || ''
1221
+ ret._$alias = alias
1222
+ ret._$.methodCallerInit(function () {
1223
+ const originalCaller = this as unknown as glassEasel.GeneralComponent
1224
+ let proto = ret._$proto
1225
+ if (proto === undefined) {
1226
+ const methods = originalCaller.getComponentDefinition().behavior.getMethods()
1227
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1228
+ proto = ret._$proto = new ComponentProto(methods) as any
1229
+ }
1230
+ const caller = proto!.derive()
1231
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1232
+ caller._$ = originalCaller as any
1233
+ caller._$export = ret._$export
1234
+ return caller
1235
+ })
1236
+ if (overallBehavior) ret._$.behavior(overallBehavior)
1237
+ return ret
1238
+ }
1239
+
1240
+ /**
1241
+ * Set the component options
1242
+ *
1243
+ * If called multiple times, only the latest call is valid.
1244
+ */
1245
+ options(
1246
+ options: ComponentDefinitionOptions,
1247
+ ): ResolveBehaviorBuilder<this, TChainingFilter> {
1248
+ this._$options = options
1249
+ return this as any
1250
+ }
1251
+
1252
+ /** Use another behavior */
1253
+ behavior<
1254
+ UData extends DataList,
1255
+ UProperty extends PropertyList,
1256
+ UMethod extends MethodList,
1257
+ UChainingFilter extends ChainingFilterType,
1258
+ >(
1259
+ behavior: Behavior<UData, UProperty, UMethod, UChainingFilter>,
1260
+ ): ResolveBehaviorBuilder<
1261
+ ComponentBuilder<
1262
+ TPrevData,
1263
+ TData & UData,
1264
+ TProperty & UProperty,
1265
+ TMethod & UMethod,
1266
+ UChainingFilter,
1267
+ TPendingChainingFilter,
1268
+ TComponentExport
1269
+ >,
1270
+ UChainingFilter
1271
+ > {
1272
+ this._$parents.push(behavior as GeneralBehavior)
1273
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1274
+ this._$ = this._$.behavior(behavior._$)
1275
+ return this as any
1276
+ }
1277
+
1278
+ /** Set the export value when the component is being selected */
1279
+ export<TNewComponentExport>(
1280
+ f: (
1281
+ this: Component<TData, TProperty, TMethod, TComponentExport>,
1282
+ source: GeneralComponent | null,
1283
+ ) => TNewComponentExport,
1284
+ ): ResolveBehaviorBuilder<
1285
+ ComponentBuilder<
1286
+ TPrevData,
1287
+ TData,
1288
+ TProperty,
1289
+ TMethod,
1290
+ TChainingFilter,
1291
+ TPendingChainingFilter,
1292
+ TNewComponentExport
1293
+ >,
1294
+ TChainingFilter
1295
+ > {
1296
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1297
+ this._$export = f as any
1298
+ return this as any
1299
+ }
1300
+
1301
+ /**
1302
+ * Add some template data fields
1303
+ *
1304
+ * It does not support raw data, but a `gen` function which returns the new data fields.
1305
+ * The `gen` function executes once during component creation.
1306
+ */
1307
+ override data<T extends DataList>(
1308
+ gen: (() => typeUtils.NewFieldList<AllData<TData, TProperty>, T>),
1309
+ ): ResolveBehaviorBuilder<
1310
+ ComponentBuilder<
1311
+ T,
1312
+ TData & T,
1313
+ TProperty,
1314
+ TMethod,
1315
+ TChainingFilter,
1316
+ TPendingChainingFilter,
1317
+ TComponentExport
1318
+ >,
1319
+ TChainingFilter
1320
+ > {
1321
+ return super.data(gen) as any
1322
+ }
1323
+
1324
+ /**
1325
+ * Add a single property
1326
+ *
1327
+ * The property name should be different from other properties.
1328
+ */
1329
+ override property<
1330
+ N extends string,
1331
+ T extends PropertyType,
1332
+ V extends PropertyTypeToValueType<T>,
1333
+ >(
1334
+ name: N,
1335
+ def: N extends keyof (TData & TProperty) ? never : typeUtils.PropertyListItem<T, V>,
1336
+ ): ResolveBehaviorBuilder<
1337
+ ComponentBuilder<
1338
+ TPrevData,
1339
+ TData,
1340
+ TProperty & Record<N, unknown extends V ? T : typeUtils.PropertyOption<T, V>>,
1341
+ TMethod,
1342
+ TChainingFilter,
1343
+ TPendingChainingFilter,
1344
+ TComponentExport
1345
+ >,
1346
+ TChainingFilter
1347
+ > {
1348
+ return super.property(name, def) as any
1349
+ }
1350
+
1351
+ /**
1352
+ * Add some public methods
1353
+ *
1354
+ * The public method can be used as an event handler, and can be visited in component instance.
1355
+ */
1356
+ override methods<
1357
+ T extends MethodList,
1358
+ >(
1359
+ funcs: T & ThisType<Component<TData, TProperty, TMethod & T, any>>,
1360
+ ): ResolveBehaviorBuilder<
1361
+ ComponentBuilder<
1362
+ TPrevData,
1363
+ TData,
1364
+ TProperty,
1365
+ TMethod & T,
1366
+ TChainingFilter,
1367
+ TPendingChainingFilter,
1368
+ TComponentExport
1369
+ >,
1370
+ TChainingFilter
1371
+ > {
1372
+ return super.methods(funcs) as any
1373
+ }
1374
+
1375
+ /**
1376
+ * Execute a function while component instance creation
1377
+ *
1378
+ * A `BuilderContext` is provided to tweak the component creation progress.
1379
+ * The return value is used as the "export" value of the behavior,
1380
+ * which can be imported by other behaviors.
1381
+ */
1382
+ override init<TExport extends Record<string, TaggedMethod<(...args: any[]) => any>> | void>(
1383
+ func: (
1384
+ this: Component<TData, TProperty, TMethod, TComponentExport>,
1385
+ builderContext: BuilderContext<
1386
+ TPrevData,
1387
+ TProperty,
1388
+ Component<TData, TProperty, TMethod, TComponentExport>
1389
+ >,
1390
+ ) => TExport,
1391
+ // eslint-disable-next-line function-paren-newline
1392
+ ): ResolveBehaviorBuilder<
1393
+ ComponentBuilder<
1394
+ TPrevData,
1395
+ TData,
1396
+ TProperty,
1397
+ TMethod,
1398
+ TChainingFilter,
1399
+ TPendingChainingFilter,
1400
+ TComponentExport
1401
+ >,
1402
+ TChainingFilter
1403
+ > {
1404
+ return super.init(func) as any
1405
+ }
1406
+
1407
+ /** Apply a classic definition object */
1408
+ override definition<
1409
+ TNewData extends DataList = Empty,
1410
+ TNewProperty extends PropertyList = Empty,
1411
+ TNewMethod extends MethodList = Empty,
1412
+ TNewComponentExport = undefined,
1413
+ >(
1414
+ def: ComponentDefinition<
1415
+ TNewData,
1416
+ TNewProperty,
1417
+ TNewMethod,
1418
+ TNewComponentExport
1419
+ > & ThisType<
1420
+ Component<
1421
+ TData & TNewData,
1422
+ TProperty & TNewProperty,
1423
+ TMethod & TNewMethod,
1424
+ TNewComponentExport
1425
+ >
1426
+ >,
1427
+ ): ResolveBehaviorBuilder<
1428
+ ComponentBuilder<
1429
+ TPrevData,
1430
+ TData & TNewData,
1431
+ TProperty & TNewProperty,
1432
+ TMethod & TNewMethod,
1433
+ TChainingFilter,
1434
+ TPendingChainingFilter,
1435
+ TNewComponentExport
1436
+ >,
1437
+ TChainingFilter
1438
+ > {
1439
+ super.definition(def)
1440
+ if (def.options) this.options(def.options)
1441
+ return this as any
1442
+ }
1443
+
1444
+ pageDefinition<
1445
+ TNewData extends DataList,
1446
+ TNewExtraFields extends { [k: PropertyKey]: any },
1447
+ >(
1448
+ def: PageDefinition<
1449
+ TNewData,
1450
+ TNewExtraFields
1451
+ > & ThisType<Component<TData & TNewData, TProperty, TMethod & TNewExtraFields, undefined>>,
1452
+ ): ResolveBehaviorBuilder<
1453
+ ComponentBuilder<
1454
+ TPrevData,
1455
+ TData & TNewData,
1456
+ TProperty,
1457
+ TMethod & TNewExtraFields,
1458
+ TChainingFilter,
1459
+ TPendingChainingFilter,
1460
+ TComponentExport
1461
+ >,
1462
+ TChainingFilter
1463
+ > {
1464
+ const freeData = Object.create(null) as { [k: string]: any }
1465
+ const compDef = {
1466
+ methods: {},
1467
+ } as { [k: string]: any }
1468
+ const keys = Object.keys(def)
1469
+ for (let i = 0; i < keys.length; i += 1) {
1470
+ const k = keys[i]!
1471
+ if (k === 'data' || k === 'options' || k === 'behaviors') {
1472
+ compDef[k] = def[k]
1473
+ } else if (typeof def[k] === 'function') {
1474
+ (compDef.methods as { [k: string]: unknown })[k] = def[k] as unknown
1475
+ } else {
1476
+ (freeData as { [k: string]: unknown })[k] = def[k]
1477
+ }
1478
+ }
1479
+ this.definition(compDef)
1480
+ this._$.init(function () {
1481
+ Object.assign(this, glassEasel.dataUtils.simpleDeepCopy(freeData))
1482
+ })
1483
+ return this as any
1484
+ }
1485
+
1486
+ /**
1487
+ * Finish the component definition process
1488
+ */
1489
+ register(): ComponentType<TData, TProperty, TMethod, TComponentExport> {
1490
+ const is = this._$is
1491
+ const codeSpace = this._$codeSpace
1492
+
1493
+ // processing common fields
1494
+ const [options, styleIsolation] = codeSpace.prepareComponentOptions(is, this._$options)
1495
+ this._$.options(options)
1496
+ const staticConfig = codeSpace.getComponentStaticConfig(is)
1497
+ const using = staticConfig?.usingComponents
1498
+ const generics = staticConfig?.generics
1499
+ const placeholder = staticConfig?.placeholder
1500
+ if (using) this._$.usingComponents(using)
1501
+ if (generics) this._$.generics(generics)
1502
+ if (placeholder) this._$.placeholders(placeholder)
1503
+ const template = codeSpace.getCompiledTemplate(is)
1504
+ if (template) this._$.template(template)
1505
+
1506
+ // do registration
1507
+ codeSpace._$styleIsolationMap[is] = styleIsolation
1508
+ const compDef = this._$.registerComponent()
1509
+ this._$alias?.forEach((alias) => {
1510
+ this._$codeSpace.getComponentSpace().exportComponent(alias, this._$is)
1511
+ })
1512
+ return new ComponentType(compDef)
1513
+ }
1514
+ }