@estjs/template 0.0.15-beta.9 → 0.0.15

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.
@@ -24,30 +24,16 @@ declare function provide<T>(key: InjectionKey<T> | string | number, value: T): v
24
24
  */
25
25
  declare function inject<T>(key: InjectionKey<T> | string | number, defaultValue?: T): T;
26
26
 
27
- /**
28
- * Scope represents an execution context in the component tree.
29
- * It manages provides, cleanup functions, and lifecycle hooks.
30
- */
31
27
  interface Scope {
32
- /** Unique identifier for debugging */
33
28
  readonly id: number;
34
- /** Parent scope in the hierarchy */
35
29
  parent: Scope | null;
36
- /** Child scopes (lazy initialized) */
37
- children: Set<Scope> | null;
38
- /** Provided values (lazy initialized) */
30
+ children: Scope[] | null;
39
31
  provides: Map<InjectionKey<unknown> | string | number | symbol, unknown> | null;
40
- /** Cleanup functions (lazy initialized) */
41
- cleanup: Set<() => void> | null;
42
- /** Mount lifecycle hooks (lazy initialized) */
43
- onMount: Set<() => void | Promise<void>> | null;
44
- /** Update lifecycle hooks (lazy initialized) */
45
- onUpdate: Set<() => void | Promise<void>> | null;
46
- /** Destroy lifecycle hooks (lazy initialized) */
47
- onDestroy: Set<() => void | Promise<void>> | null;
48
- /** Whether the scope has been mounted */
32
+ cleanup: (() => void)[] | null;
33
+ onMount: (() => void | Promise<void>)[] | null;
34
+ onUpdate: (() => void | Promise<void>)[] | null;
35
+ onDestroy: (() => void | Promise<void>)[] | null;
49
36
  isMounted: boolean;
50
- /** Whether the scope has been destroyed */
51
37
  isDestroyed: boolean;
52
38
  }
53
39
  /**
@@ -60,13 +46,6 @@ declare function getActiveScope(): Scope | null;
60
46
  * @param scope - The scope to set as active
61
47
  */
62
48
  declare function setActiveScope(scope: Scope | null): void;
63
- /**
64
- * Create a new scope with optional parent.
65
- * If no parent is provided, uses the current active scope as parent.
66
- *
67
- * @param parent - Optional parent scope (defaults to active scope)
68
- * @returns A new scope instance
69
- */
70
49
  declare function createScope(parent?: Scope | null): Scope;
71
50
  /**
72
51
  * Run a function within a scope, ensuring proper cleanup.
@@ -78,27 +57,46 @@ declare function createScope(parent?: Scope | null): Scope;
78
57
  */
79
58
  declare function runWithScope<T>(scope: Scope, fn: () => T): T;
80
59
  /**
81
- * Dispose a scope and all its children.
82
- * Children are disposed first (depth-first), then the scope itself.
60
+ * Dispose a scope and recursively dispose all child scopes.
61
+ * Performs the following cleanup in order:
62
+ * 1. Recursively disposes all children (depth-first)
63
+ * 2. Executes destroy lifecycle hooks
64
+ * 3. Executes registered cleanup functions
65
+ * 4. Removes scope from parent's children list
66
+ * 5. Clears all internal collections and resets state
67
+ *
68
+ * Safe to call multiple times (idempotent).
83
69
  *
84
70
  * @param scope - The scope to dispose
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const scope = createScope(parent);
75
+ * // ... use scope ...
76
+ * disposeScope(scope); // Cleanup everything
77
+ * ```
85
78
  */
86
79
  declare function disposeScope(scope: Scope): void;
87
80
  /**
88
- * Register a cleanup function in the current scope.
89
- * The function will be called when the scope is disposed.
81
+ * Register a cleanup function to be executed when the scope is disposed.
82
+ * Useful for cleaning up timers, subscriptions, event listeners, etc.
83
+ *
84
+ * Cleanup functions are executed in LIFO order (last registered, first executed).
85
+ * Cleanup errors don't prevent other cleanups from running.
90
86
  *
91
- * @param fn - The cleanup function
87
+ * @param fn - The cleanup function to register
88
+ *
89
+ * @throws Error in dev mode if called outside a scope
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * const timerId = setInterval(() => {}, 1000);
94
+ * onCleanup(() => clearInterval(timerId));
95
+ * ```
92
96
  */
93
97
  declare function onCleanup(fn: () => void): void;
94
98
 
95
- declare enum COMPONENT_TYPE {
96
- NORMAL = "normal",
97
- FRAGMENT = "fragment",
98
- PORTAL = "portal",
99
- SUSPENSE = "suspense",
100
- FOR = "for"
101
- }
99
+ declare const NORMAL_COMPONENT: unique symbol;
102
100
 
103
101
  type AnyNode = Node | Component<any> | Element | string | number | boolean | null | undefined | AnyNode[] | (() => AnyNode) | Signal<AnyNode> | Computed<AnyNode>;
104
102
  type ComponentProps = Record<string, unknown>;
@@ -112,16 +110,26 @@ declare class Component<P extends ComponentProps = ComponentProps> {
112
110
  protected parentNode: Node | undefined;
113
111
  beforeNode: Node | undefined;
114
112
  private reactiveProps;
113
+ private _propSnapshots;
115
114
  readonly key: string | undefined;
116
115
  protected state: number;
117
116
  protected parentScope: Scope | null;
118
- private [COMPONENT_TYPE.NORMAL];
117
+ readonly [NORMAL_COMPONENT] = true;
119
118
  get isConnected(): boolean;
120
119
  get firstChild(): Node | undefined;
121
120
  constructor(component: ComponentFn<P>, props?: P);
122
121
  mount(parentNode: Node, beforeNode?: Node): AnyNode[];
123
- update(prevNode: Component<any>): Component<P>;
124
- forceUpdate(): Promise<void>;
122
+ update<T extends ComponentProps>(prevNode: Component<T>): Component<T>;
123
+ /**
124
+ * Update reactive props by comparing with current values
125
+ */
126
+ private _updateReactiveProps;
127
+ private unwrapRenderResult;
128
+ forceUpdate(): void;
129
+ /**
130
+ * Get anchor node for insertion
131
+ */
132
+ private _getAnchorNode;
125
133
  /**
126
134
  * Destroy component
127
135
  */
@@ -151,13 +159,7 @@ declare function createComponent<P extends ComponentProps>(componentFn: Componen
151
159
  * @param html - The HTML string to create template from
152
160
  * @returns Factory function that returns a cloned node of the template
153
161
  * @throws {Error} When template content is empty or invalid
154
- *
155
- * @example
156
- * ```typescript
157
- * const buttonTemplate = template('<button>Click me</button>');
158
- * const button1 = buttonTemplate(); // Creates first button instance
159
- * const button2 = buttonTemplate(); // Creates second button instance
160
- * ```
162
+
161
163
  */
162
164
  declare function template(html: string): () => Node;
163
165
  /**
@@ -169,41 +171,57 @@ declare function template(html: string): () => Node;
169
171
  * @param component - The root component function to mount
170
172
  * @param target - CSS selector string or DOM element to mount to
171
173
  * @returns The mount root component instance, or undefined if target not found
172
- *
173
- * @example
174
- * ```typescript
175
- * const App = () => template('<div>Hello World</div>')
176
- * const app = createApp(App, '#root');
177
- *
178
- * // Or with DOM element
179
- * const container = document.getElementById('app');
180
- * const app = createApp(App, container);
181
- * ```
182
174
  */
183
- declare function createApp<P extends ComponentProps = {}>(component: ComponentFn<P>, target: string | Element): Component<P> | undefined;
175
+ declare function createApp<P extends ComponentProps = {}>(component: ComponentFn<P> | Component<P>, target: string | Element): Component<P> | undefined;
184
176
 
177
+ /**
178
+ * Lifecycle hook type: returns void or a Promise that resolves when complete.
179
+ * Hooks can perform cleanup by returning a cleanup function.
180
+ */
185
181
  type LifecycleHook = () => void | Promise<void>;
186
182
  /**
187
183
  * Register a mount lifecycle hook.
188
- * Called after the component is mounted to the DOM.
184
+ * Runs after component is mounted and virtual tree is committed.
185
+ * If the scope is already mounted, the hook executes immediately.
189
186
  *
190
- * @param hook - The hook function to execute on mount
187
+ * @throws Error in dev mode if called outside a scope
188
+ * @example
189
+ * ```tsx
190
+ * onMount(() => {
191
+ * console.log('Component mounted');
192
+ * return () => console.log('Cleanup');
193
+ * });
194
+ * ```
191
195
  */
192
196
  declare function onMount(hook: LifecycleHook): void;
193
197
  /**
194
- * Register a destroy lifecycle hook.
195
- * Called before the component is removed from the DOM.
198
+ * Register an update lifecycle hook.
199
+ * Runs whenever the component re-renders due to prop or state changes.
196
200
  *
197
- * @param hook - The hook function to execute on destroy
201
+ * @throws Error in dev mode if called outside a scope
202
+ * @example
203
+ * ```tsx
204
+ * onUpdate(() => {
205
+ * console.log('Component updated');
206
+ * });
207
+ * ```
198
208
  */
199
- declare function onDestroy(hook: LifecycleHook): void;
209
+ declare function onUpdate(hook: LifecycleHook): void;
200
210
  /**
201
- * Register an update lifecycle hook.
202
- * Called after the component updates.
211
+ * Register a destroy lifecycle hook.
212
+ * Runs before scope is disposed and resources are cleaned up.
213
+ * Perfect for resetting external state, unsubscribing from events, etc.
203
214
  *
204
- * @param hook - The hook function to execute on update
215
+ * @throws Error in dev mode if called outside a scope
216
+ * @example
217
+ * ```tsx
218
+ * onDestroy(() => {
219
+ * unsubscribe();
220
+ * clearTimeout(timerId);
221
+ * });
222
+ * ```
205
223
  */
206
- declare function onUpdate(hook: LifecycleHook): void;
224
+ declare function onDestroy(hook: LifecycleHook): void;
207
225
 
208
226
  /**
209
227
  * Add event listener with automatic cleanup on scope destruction
@@ -226,16 +244,15 @@ declare function bindElement(node: Element, key: string, defaultValue: unknown,
226
244
  /**
227
245
  * Reactive node insertion with binding support
228
246
  *
229
- * @param parent - Parent node
230
- * @param nodeFactory - Node factory function or static node
231
- * @param before - Reference node for insertion position
232
- * @returns Array of rendered nodes
247
+ * @param parent Parent node
248
+ * @param nodeFactory Node factory function or static node
249
+ * @param before Reference node for insertion position
233
250
  *
234
251
  * @example
235
252
  * ```typescript
236
253
  * insert(container, () => message.value, null);
237
254
  * insert(container, staticElement, referenceNode);
238
- * insert(container, "Hello World", null);
255
+ * insert(container, "Hello World", null); // Direct string support
239
256
  * ```
240
257
  */
241
258
  declare function insert(parent: Node, nodeFactory: AnyNode, before?: Node): AnyNode[] | undefined;
@@ -322,13 +339,35 @@ declare function normalizeNode(node: unknown): Node;
322
339
  */
323
340
  declare function isSameNode(a: AnyNode, b: AnyNode): boolean;
324
341
  /**
325
- * Shallow compare two objects
342
+ * Shallow compare two objects or arrays
343
+ * Performs strict equality check on top-level properties
326
344
  *
327
- * @param a - First object
328
- * @param b - Second object
329
- * @returns true if objects are shallowly equal
345
+ * @param a - First value to compare
346
+ * @param b - Second value to compare
347
+ * @returns true if values are shallowly equal
330
348
  */
331
- declare function shallowCompare(a: any, b: any): boolean;
349
+ declare function shallowCompare(a: unknown, b: unknown): boolean;
350
+
351
+ /**
352
+ * Get rendered element by hydration key or create from template
353
+ * @param {string} temp - the template string
354
+ * @returns {Function} a function that returns the element
355
+ */
356
+ declare function getRenderedElement(temp: string): () => Node | null;
357
+ /**
358
+ * Maps server-side rendered nodes during hydration
359
+ * @param {HTMLElement} templateEl - The root template element
360
+ * @param {number[]} idx - Array of indices to map
361
+ * @returns {Node[]} Array of mapped nodes
362
+ */
363
+ declare function mapSSRNodes(templateEl: HTMLElement, idx: number[]): Node[];
364
+ /**
365
+ * Hydrate a server-rendered component
366
+ * @param {ComponentFn} component - Component function to hydrate
367
+ * @param {HTMLElement | string} container - Container element or selector
368
+ * @returns {any} Component instance or undefined if hydration fails
369
+ */
370
+ declare function hydrate(component: ComponentFn, container: HTMLElement | string): any;
332
371
 
333
372
  /**
334
373
  * Start hydration mode
@@ -345,6 +384,15 @@ declare function endHydration(): void;
345
384
  * @returns true if hydration is in progress
346
385
  */
347
386
  declare function isHydrating(): boolean;
387
+ /**
388
+ * Get the hydration key
389
+ * @returns the hydration key string
390
+ */
391
+ declare function getHydrationKey(): string;
392
+ /**
393
+ * Reset the hydration key counter
394
+ */
395
+ declare function resetHydrationKey(): void;
348
396
 
349
397
  /**
350
398
  * Patches the class attribute of an element
@@ -420,23 +468,39 @@ interface FragmentProps extends ComponentProps {
420
468
  children?: AnyNode | AnyNode[];
421
469
  }
422
470
  /**
423
- * Fragment component - renders multiple children without wrapper elements
471
+ * Fragment component - renders multiple children without wrapper elements (Client-side only)
472
+ *
473
+ * **Client-side behavior:**
474
+ * - Returns children directly for rendering
475
+ * - Hydration system matches children using hydration keys
476
+ * - The template system handles array children automatically
424
477
  *
425
478
  * @param props - Component props with children
426
- * @returns DocumentFragment containing all children
479
+ * @returns Children directly without wrapper
427
480
  *
428
481
  * @example
429
482
  * ```tsx
483
+ * // Basic usage
430
484
  * <Fragment>
431
485
  * <div>First</div>
432
486
  * <span>Second</span>
433
487
  * </Fragment>
488
+ *
489
+ * // Nested fragments
490
+ * <Fragment>
491
+ * <Fragment>
492
+ * <div>Nested 1</div>
493
+ * <div>Nested 2</div>
494
+ * </Fragment>
495
+ * <div>Third</div>
496
+ * </Fragment>
497
+ *
498
+ * // Empty fragment (renders nothing)
499
+ * <Fragment />
434
500
  * ```
435
501
  */
436
502
  declare function Fragment(props?: FragmentProps): AnyNode;
437
- declare namespace Fragment {
438
- var fragment: boolean;
439
- }
503
+ declare namespace Fragment { }
440
504
  /**
441
505
  * Check if a node is a Fragment component
442
506
  * @param node - Node to check
@@ -460,12 +524,9 @@ interface PortalProps {
460
524
  * <Portal target="#modal-root">
461
525
  * <div>Modal content</div>
462
526
  * </Portal>
463
- * ```
464
527
  */
465
528
  declare function Portal(props: PortalProps): Comment | string;
466
- declare namespace Portal {
467
- var portal: boolean;
468
- }
529
+ declare namespace Portal { }
469
530
  /**
470
531
  * Check if a node is a Portal component
471
532
  * @param node - Node to check
@@ -495,9 +556,7 @@ interface SuspenseProps {
495
556
  * ```
496
557
  */
497
558
  declare function Suspense(props: SuspenseProps): AnyNode;
498
- declare namespace Suspense {
499
- var suspense: boolean;
500
- }
559
+ declare namespace Suspense { }
501
560
  /**
502
561
  * Check if a node is a Suspense component
503
562
  * @param node - Node to check
@@ -548,4 +607,19 @@ interface ResourceOptions<T> {
548
607
  */
549
608
  declare function createResource<T>(fetcher: () => Promise<T>, options?: ResourceOptions<T>): [Resource<T>, ResourceActions<T>];
550
609
 
551
- export { Component, type ComponentFn, type ComponentProps, Fragment, type FragmentProps, type InjectionKey, Portal, type PortalProps, type Scope, Suspense, type SuspenseProps, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };
610
+ interface ForProps<T> {
611
+ each: T[] | Signal<T[]> | (() => T[]);
612
+ children: (item: T, index: number) => AnyNode;
613
+ keyFn?: (item: T) => unknown;
614
+ fallback?: () => AnyNode;
615
+ }
616
+ /**
617
+ * Optimized For Component
618
+ * - Uses createDetachedScope to avoid parent.children Set overhead (SolidJS style)
619
+ * - Uses DocumentFragment batching for mass creation
620
+ * - Inlines scope switching for performance
621
+ */
622
+ declare function For<T>(props: ForProps<T>): Node;
623
+ declare namespace For { }
624
+
625
+ export { Component, type ComponentFn, type ComponentProps, For, type ForProps, Fragment, type FragmentProps, type InjectionKey, Portal, type PortalProps, type Scope, Suspense, type SuspenseProps, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, getHydrationKey, getRenderedElement, hydrate, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, mapSSRNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, resetHydrationKey, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };