eleva 1.0.1 → 1.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 (97) hide show
  1. package/README.md +21 -10
  2. package/dist/{eleva-plugins.cjs.js → eleva-plugins.cjs} +1002 -292
  3. package/dist/eleva-plugins.cjs.map +1 -0
  4. package/dist/eleva-plugins.d.cts +1352 -0
  5. package/dist/eleva-plugins.d.cts.map +1 -0
  6. package/dist/eleva-plugins.d.ts +1352 -0
  7. package/dist/eleva-plugins.d.ts.map +1 -0
  8. package/dist/{eleva-plugins.esm.js → eleva-plugins.js} +1002 -292
  9. package/dist/eleva-plugins.js.map +1 -0
  10. package/dist/eleva-plugins.umd.js +1001 -291
  11. package/dist/eleva-plugins.umd.js.map +1 -1
  12. package/dist/eleva-plugins.umd.min.js +1 -1
  13. package/dist/eleva-plugins.umd.min.js.map +1 -1
  14. package/dist/{eleva.cjs.js → eleva.cjs} +421 -191
  15. package/dist/eleva.cjs.map +1 -0
  16. package/dist/eleva.d.cts +1329 -0
  17. package/dist/eleva.d.cts.map +1 -0
  18. package/dist/eleva.d.ts +473 -226
  19. package/dist/eleva.d.ts.map +1 -0
  20. package/dist/{eleva.esm.js → eleva.js} +422 -192
  21. package/dist/eleva.js.map +1 -0
  22. package/dist/eleva.umd.js +420 -190
  23. package/dist/eleva.umd.js.map +1 -1
  24. package/dist/eleva.umd.min.js +1 -1
  25. package/dist/eleva.umd.min.js.map +1 -1
  26. package/dist/plugins/attr.cjs +279 -0
  27. package/dist/plugins/attr.cjs.map +1 -0
  28. package/dist/plugins/attr.d.cts +101 -0
  29. package/dist/plugins/attr.d.cts.map +1 -0
  30. package/dist/plugins/attr.d.ts +101 -0
  31. package/dist/plugins/attr.d.ts.map +1 -0
  32. package/dist/plugins/attr.js +276 -0
  33. package/dist/plugins/attr.js.map +1 -0
  34. package/dist/plugins/attr.umd.js +111 -22
  35. package/dist/plugins/attr.umd.js.map +1 -1
  36. package/dist/plugins/attr.umd.min.js +1 -1
  37. package/dist/plugins/attr.umd.min.js.map +1 -1
  38. package/dist/plugins/router.cjs +1873 -0
  39. package/dist/plugins/router.cjs.map +1 -0
  40. package/dist/plugins/router.d.cts +1296 -0
  41. package/dist/plugins/router.d.cts.map +1 -0
  42. package/dist/plugins/router.d.ts +1296 -0
  43. package/dist/plugins/router.d.ts.map +1 -0
  44. package/dist/plugins/router.js +1870 -0
  45. package/dist/plugins/router.js.map +1 -0
  46. package/dist/plugins/router.umd.js +482 -186
  47. package/dist/plugins/router.umd.js.map +1 -1
  48. package/dist/plugins/router.umd.min.js +1 -1
  49. package/dist/plugins/router.umd.min.js.map +1 -1
  50. package/dist/plugins/store.cjs +920 -0
  51. package/dist/plugins/store.cjs.map +1 -0
  52. package/dist/plugins/store.d.cts +266 -0
  53. package/dist/plugins/store.d.cts.map +1 -0
  54. package/dist/plugins/store.d.ts +266 -0
  55. package/dist/plugins/store.d.ts.map +1 -0
  56. package/dist/plugins/store.js +917 -0
  57. package/dist/plugins/store.js.map +1 -0
  58. package/dist/plugins/store.umd.js +410 -85
  59. package/dist/plugins/store.umd.js.map +1 -1
  60. package/dist/plugins/store.umd.min.js +1 -1
  61. package/dist/plugins/store.umd.min.js.map +1 -1
  62. package/package.json +112 -68
  63. package/src/core/Eleva.js +195 -115
  64. package/src/index.cjs +10 -0
  65. package/src/index.js +11 -0
  66. package/src/modules/Emitter.js +68 -20
  67. package/src/modules/Renderer.js +82 -20
  68. package/src/modules/Signal.js +43 -15
  69. package/src/modules/TemplateEngine.js +50 -9
  70. package/src/plugins/Attr.js +121 -19
  71. package/src/plugins/Router.js +526 -181
  72. package/src/plugins/Store.js +448 -69
  73. package/src/plugins/index.js +1 -0
  74. package/types/core/Eleva.d.ts +263 -169
  75. package/types/core/Eleva.d.ts.map +1 -1
  76. package/types/index.d.cts +3 -0
  77. package/types/index.d.cts.map +1 -0
  78. package/types/index.d.ts +5 -0
  79. package/types/index.d.ts.map +1 -1
  80. package/types/modules/Emitter.d.ts +73 -30
  81. package/types/modules/Emitter.d.ts.map +1 -1
  82. package/types/modules/Renderer.d.ts +48 -18
  83. package/types/modules/Renderer.d.ts.map +1 -1
  84. package/types/modules/Signal.d.ts +44 -16
  85. package/types/modules/Signal.d.ts.map +1 -1
  86. package/types/modules/TemplateEngine.d.ts +46 -11
  87. package/types/modules/TemplateEngine.d.ts.map +1 -1
  88. package/types/plugins/Attr.d.ts +83 -16
  89. package/types/plugins/Attr.d.ts.map +1 -1
  90. package/types/plugins/Router.d.ts +498 -207
  91. package/types/plugins/Router.d.ts.map +1 -1
  92. package/types/plugins/Store.d.ts +211 -37
  93. package/types/plugins/Store.d.ts.map +1 -1
  94. package/dist/eleva-plugins.cjs.js.map +0 -1
  95. package/dist/eleva-plugins.esm.js.map +0 -1
  96. package/dist/eleva.cjs.js.map +0 -1
  97. package/dist/eleva.esm.js.map +0 -1
package/src/core/Eleva.js CHANGED
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
 
3
+ /**
4
+ * @module eleva
5
+ * @fileoverview Core Eleva framework providing signal-based component lifecycle management,
6
+ * reactive rendering, and plugin architecture.
7
+ */
8
+
3
9
  import { TemplateEngine } from "../modules/TemplateEngine.js";
4
10
  import { Signal } from "../modules/Signal.js";
5
11
  import { Emitter } from "../modules/Emitter.js";
6
12
  import { Renderer } from "../modules/Renderer.js";
7
13
 
8
14
  // ============================================================================
9
- // TYPE DEFINITIONS - TypeScript-friendly JSDoc types for IDE support
15
+ // TYPE DEFINITIONS
10
16
  // ============================================================================
11
17
 
12
18
  // -----------------------------------------------------------------------------
@@ -14,13 +20,8 @@ import { Renderer } from "../modules/Renderer.js";
14
20
  // -----------------------------------------------------------------------------
15
21
 
16
22
  /**
17
- * @typedef {Object} ElevaConfig
18
- * @property {boolean} [debug=false]
19
- * Enable debug mode for verbose logging
20
- * @property {string} [prefix='e']
21
- * Prefix for component style scoping
22
- * @property {boolean} [async=true]
23
- * Enable async component setup
23
+ * Configuration options for the Eleva instance (reserved for future use).
24
+ * @typedef {Record<string, unknown>} ElevaConfig
24
25
  */
25
26
 
26
27
  // -----------------------------------------------------------------------------
@@ -28,43 +29,54 @@ import { Renderer } from "../modules/Renderer.js";
28
29
  // -----------------------------------------------------------------------------
29
30
 
30
31
  /**
32
+ * Component definition object.
31
33
  * @typedef {Object} ComponentDefinition
32
34
  * @property {SetupFunction} [setup]
33
- * Optional setup function that initializes the component's state and returns reactive data
34
- * @property {TemplateFunction|string} template
35
- * Required function or string that defines the component's HTML structure
36
- * @property {StyleFunction|string} [style]
37
- * Optional function or string that provides component-scoped CSS styles
35
+ * Optional setup function that initializes the component's state and returns reactive data.
36
+ * @property {TemplateFunction | string} template
37
+ * Required function or string that defines the component's HTML structure.
38
+ * @property {StyleFunction | string} [style]
39
+ * Optional function or string that provides CSS styles for the component.
40
+ * Styles are preserved across DOM diffs via data-e-style markers.
38
41
  * @property {ChildrenMap} [children]
39
- * Optional object defining nested child components
42
+ * Optional object defining nested child components.
40
43
  */
41
44
 
42
45
  /**
46
+ * Setup function that initializes component state.
43
47
  * @callback SetupFunction
44
- * @param {ComponentContext} ctx - The component context with props, emitter, and signal factory
45
- * @returns {SetupResult|Promise<SetupResult>} Reactive data and lifecycle hooks
48
+ * @param {ComponentContext} ctx
49
+ * The component context with props, emitter, and signal factory.
50
+ * @returns {SetupResult | Promise<SetupResult>}
51
+ * Reactive data and lifecycle hooks.
46
52
  */
47
53
 
48
54
  /**
55
+ * Data returned from setup function, may include lifecycle hooks.
49
56
  * @typedef {Record<string, unknown> & LifecycleHooks} SetupResult
50
- * Data returned from setup function, may include lifecycle hooks
51
57
  */
52
58
 
53
59
  /**
60
+ * Template function that returns HTML markup.
54
61
  * @callback TemplateFunction
55
- * @param {ComponentContext} ctx - The component context
56
- * @returns {string|Promise<string>} HTML template string
62
+ * @param {ComponentContext & SetupResult} ctx
63
+ * The merged component context and setup data.
64
+ * @returns {string | Promise<string>}
65
+ * HTML template string.
57
66
  */
58
67
 
59
68
  /**
69
+ * Style function that returns CSS styles.
60
70
  * @callback StyleFunction
61
- * @param {ComponentContext} ctx - The component context
62
- * @returns {string} CSS styles string
71
+ * @param {ComponentContext & SetupResult} ctx
72
+ * The merged component context and setup data.
73
+ * @returns {string}
74
+ * CSS styles string.
63
75
  */
64
76
 
65
77
  /**
66
- * @typedef {Record<string, ComponentDefinition|string>} ChildrenMap
67
- * Map of CSS selectors to component definitions or registered component names
78
+ * Map of CSS selectors to component definitions or registered component names.
79
+ * @typedef {Record<string, ComponentDefinition | string>} ChildrenMap
68
80
  */
69
81
 
70
82
  // -----------------------------------------------------------------------------
@@ -72,25 +84,28 @@ import { Renderer } from "../modules/Renderer.js";
72
84
  // -----------------------------------------------------------------------------
73
85
 
74
86
  /**
87
+ * Context passed to component setup function.
75
88
  * @typedef {Object} ComponentContext
76
89
  * @property {ComponentProps} props
77
- * Component properties passed during mounting
90
+ * Component properties passed during mounting.
78
91
  * @property {Emitter} emitter
79
- * Event emitter instance for component event handling
92
+ * Event emitter instance for component event handling.
80
93
  * @property {SignalFactory} signal
81
- * Factory function to create reactive Signal instances
94
+ * Factory function to create reactive Signal instances.
95
+ * @description
96
+ * Plugins may extend this context with additional properties (e.g., `ctx.router`, `ctx.store`).
97
+ * @see RouterContext - Router plugin injected context.
98
+ * @see StoreApi - Store plugin injected context.
82
99
  */
83
100
 
84
101
  /**
102
+ * Properties passed to a component during mounting.
85
103
  * @typedef {Record<string, unknown>} ComponentProps
86
- * Properties passed to a component during mounting
87
104
  */
88
105
 
89
106
  /**
90
- * @callback SignalFactory
91
- * @template T
92
- * @param {T} initialValue - The initial value for the signal
93
- * @returns {Signal<T>} A new Signal instance
107
+ * Factory function to create reactive Signal instances.
108
+ * @typedef {<T>(initialValue: T) => Signal<T>} SignalFactory
94
109
  */
95
110
 
96
111
  // -----------------------------------------------------------------------------
@@ -98,57 +113,65 @@ import { Renderer } from "../modules/Renderer.js";
98
113
  // -----------------------------------------------------------------------------
99
114
 
100
115
  /**
116
+ * Lifecycle hooks that can be returned from setup function.
101
117
  * @typedef {Object} LifecycleHooks
102
118
  * @property {LifecycleHook} [onBeforeMount]
103
- * Hook called before component mounting
119
+ * Called before component mounting.
104
120
  * @property {LifecycleHook} [onMount]
105
- * Hook called after component mounting
121
+ * Called after component mounting.
106
122
  * @property {LifecycleHook} [onBeforeUpdate]
107
- * Hook called before component update
123
+ * Called before component update.
108
124
  * @property {LifecycleHook} [onUpdate]
109
- * Hook called after component update
125
+ * Called after component update.
110
126
  * @property {UnmountHook} [onUnmount]
111
- * Hook called during component unmounting
127
+ * Called during component unmounting.
112
128
  */
113
129
 
114
130
  /**
131
+ * Lifecycle hook function.
115
132
  * @callback LifecycleHook
116
- * @param {LifecycleHookContext} ctx - Context with container and component data
117
- * @returns {void|Promise<void>}
133
+ * @param {LifecycleHookContext} ctx
134
+ * Context with container and component data.
135
+ * @returns {void | Promise<void>}
118
136
  */
119
137
 
120
138
  /**
139
+ * Unmount hook function with cleanup resources.
121
140
  * @callback UnmountHook
122
- * @param {UnmountHookContext} ctx - Context with cleanup resources
123
- * @returns {void|Promise<void>}
141
+ * @param {UnmountHookContext} ctx
142
+ * Context with cleanup resources.
143
+ * @returns {void | Promise<void>}
124
144
  */
125
145
 
126
146
  /**
147
+ * Context passed to lifecycle hooks.
127
148
  * @typedef {Object} LifecycleHookContext
128
149
  * @property {HTMLElement} container
129
- * The DOM element where the component is mounted
150
+ * The DOM element where the component is mounted.
130
151
  * @property {ComponentContext & SetupResult} context
131
- * The component's reactive state and context data
152
+ * The component's reactive state and context data.
132
153
  */
133
154
 
134
155
  /**
156
+ * Context passed to unmount hook with cleanup resources.
135
157
  * @typedef {Object} UnmountHookContext
136
158
  * @property {HTMLElement} container
137
- * The DOM element where the component is mounted
159
+ * The DOM element where the component is mounted.
138
160
  * @property {ComponentContext & SetupResult} context
139
- * The component's reactive state and context data
161
+ * The component's reactive state and context data.
140
162
  * @property {CleanupResources} cleanup
141
- * Object containing cleanup functions and instances
163
+ * Object containing cleanup functions and instances.
142
164
  */
143
165
 
144
166
  /**
167
+ * Resources available for cleanup during unmount.
145
168
  * @typedef {Object} CleanupResources
146
- * @property {Array<UnsubscribeFunction>} watchers
147
- * Signal watcher cleanup functions
148
- * @property {Array<UnsubscribeFunction>} listeners
149
- * Event listener cleanup functions
150
- * @property {Array<MountResult>} children
151
- * Child component instances
169
+ * @property {UnsubscribeFunction[]} watchers
170
+ * Signal watcher cleanup functions.
171
+ * @property {UnsubscribeFunction[]} listeners
172
+ * Event listener cleanup functions.
173
+ * @property {MountResult[]} children
174
+ * Child component instances.
152
175
  */
153
176
 
154
177
  // -----------------------------------------------------------------------------
@@ -156,23 +179,26 @@ import { Renderer } from "../modules/Renderer.js";
156
179
  // -----------------------------------------------------------------------------
157
180
 
158
181
  /**
182
+ * Result of mounting a component.
159
183
  * @typedef {Object} MountResult
160
184
  * @property {HTMLElement} container
161
- * The DOM element where the component is mounted
185
+ * The DOM element where the component is mounted.
162
186
  * @property {ComponentContext & SetupResult} data
163
- * The component's reactive state and context data
187
+ * The component's reactive state and context data.
164
188
  * @property {UnmountFunction} unmount
165
- * Function to clean up and unmount the component
189
+ * Function to clean up and unmount the component.
166
190
  */
167
191
 
168
192
  /**
193
+ * Function to unmount a component and clean up resources.
169
194
  * @callback UnmountFunction
170
195
  * @returns {Promise<void>}
171
196
  */
172
197
 
173
198
  /**
199
+ * Function to unsubscribe from events or watchers.
174
200
  * @callback UnsubscribeFunction
175
- * @returns {void|boolean}
201
+ * @returns {void | boolean}
176
202
  */
177
203
 
178
204
  // -----------------------------------------------------------------------------
@@ -180,31 +206,39 @@ import { Renderer } from "../modules/Renderer.js";
180
206
  // -----------------------------------------------------------------------------
181
207
 
182
208
  /**
209
+ * Plugin interface for extending Eleva.
183
210
  * @typedef {Object} ElevaPlugin
184
- * @property {PluginInstallFunction} install
185
- * Function that installs the plugin into the Eleva instance
186
211
  * @property {string} name
187
- * Unique identifier name for the plugin
212
+ * Unique identifier name for the plugin.
213
+ * @property {string} [version]
214
+ * Optional version string for the plugin.
215
+ * @property {PluginInstallFunction} install
216
+ * Function that installs the plugin.
188
217
  * @property {PluginUninstallFunction} [uninstall]
189
- * Optional function to uninstall the plugin
218
+ * Optional function to uninstall the plugin.
190
219
  */
191
220
 
192
221
  /**
222
+ * Plugin install function.
193
223
  * @callback PluginInstallFunction
194
- * @param {Eleva} eleva - The Eleva instance
195
- * @param {PluginOptions} options - Plugin configuration options
196
- * @returns {void|Eleva|unknown} Optionally returns the Eleva instance or plugin result
224
+ * @param {Eleva} eleva
225
+ * The Eleva instance.
226
+ * @param {PluginOptions} [options]
227
+ * Plugin configuration options.
228
+ * @returns {void | Eleva | unknown}
197
229
  */
198
230
 
199
231
  /**
232
+ * Plugin uninstall function.
200
233
  * @callback PluginUninstallFunction
201
- * @param {Eleva} eleva - The Eleva instance
202
- * @returns {void}
234
+ * @param {Eleva} eleva
235
+ * The Eleva instance.
236
+ * @returns {void | Promise<void>}
203
237
  */
204
238
 
205
239
  /**
240
+ * Configuration options passed to a plugin during installation.
206
241
  * @typedef {Record<string, unknown>} PluginOptions
207
- * Configuration options passed to a plugin during installation
208
242
  */
209
243
 
210
244
  // -----------------------------------------------------------------------------
@@ -212,20 +246,19 @@ import { Renderer } from "../modules/Renderer.js";
212
246
  // -----------------------------------------------------------------------------
213
247
 
214
248
  /**
215
- * @callback EventHandler
216
- * @param {Event} event - The DOM event object
217
- * @returns {void}
249
+ * Handler function for DOM events (e.g., click, input, submit).
250
+ * @typedef {(event: Event) => void} DOMEventHandler
218
251
  */
219
252
 
220
253
  /**
254
+ * Common DOM event names (prefixed with @ in templates).
221
255
  * @typedef {'click'|'submit'|'input'|'change'|'focus'|'blur'|'keydown'|'keyup'|'keypress'|'mouseenter'|'mouseleave'|'mouseover'|'mouseout'|'mousedown'|'mouseup'|'touchstart'|'touchend'|'touchmove'|'scroll'|'resize'|'load'|'error'|string} DOMEventName
222
- * Common DOM event names (prefixed with @ in templates)
223
256
  */
224
257
 
225
258
  /**
226
259
  * @class 🧩 Eleva
227
260
  * @classdesc A modern, signal-based component runtime framework that provides lifecycle hooks,
228
- * scoped styles, and plugin support. Eleva manages component registration, plugin integration,
261
+ * component styles, and plugin support. Eleva manages component registration, plugin integration,
229
262
  * event handling, and DOM rendering with a focus on performance and developer experience.
230
263
  *
231
264
  * @example
@@ -255,11 +288,10 @@ export class Eleva {
255
288
  * Creates a new Eleva instance with the specified name and configuration.
256
289
  *
257
290
  * @public
291
+ * @constructor
258
292
  * @param {string} name - The unique identifier name for this Eleva instance.
259
- * @param {Record<string, unknown>} [config={}] - Optional configuration object for the instance.
260
- * May include framework-wide settings and default behaviors.
293
+ * @param {ElevaConfig} [config={}] - Optional configuration object for the instance.
261
294
  * @throws {Error} If the name is not provided or is not a string.
262
- * @returns {Eleva} A new Eleva instance.
263
295
  *
264
296
  * @example
265
297
  * const app = new Eleva("myApp");
@@ -274,17 +306,17 @@ export class Eleva {
274
306
  if (!name || typeof name !== "string") {
275
307
  throw new Error("Eleva: name must be a non-empty string");
276
308
  }
277
- /** @public {string} The unique identifier name for this Eleva instance */
309
+ /** @public @readonly {string} The unique identifier name for this Eleva instance */
278
310
  this.name = name;
279
- /** @public {Object<string, unknown>} Optional configuration object for the Eleva instance */
311
+ /** @public @readonly {Record<string, unknown>} Configuration object for the Eleva instance */
280
312
  this.config = config;
281
- /** @public {Emitter} Instance of the event emitter for handling component events */
313
+ /** @public @readonly {Emitter} Event emitter for handling component events */
282
314
  this.emitter = new Emitter();
283
- /** @public {typeof Signal} Static reference to the Signal class for creating reactive state */
315
+ /** @public @readonly {typeof Signal} Signal class for creating reactive state */
284
316
  this.signal = Signal;
285
- /** @public {typeof TemplateEngine} Static reference to the TemplateEngine class for template parsing */
317
+ /** @public @readonly {typeof TemplateEngine} TemplateEngine class for template parsing */
286
318
  this.templateEngine = TemplateEngine;
287
- /** @public {Renderer} Instance of the renderer for handling DOM updates and patching */
319
+ /** @public @readonly {Renderer} Renderer for handling DOM updates and patching */
288
320
  this.renderer = new Renderer();
289
321
 
290
322
  /** @private {Map<string, ComponentDefinition>} Registry of all component definitions by name */
@@ -300,14 +332,16 @@ export class Eleva {
300
332
  * The plugin's install function will be called with the Eleva instance and provided options.
301
333
  * After installation, the plugin will be available for use by components.
302
334
  *
303
- * Note: Plugins that wrap core methods (e.g., mount) must be uninstalled in reverse order
335
+ * @note Plugins that wrap core methods (e.g., mount) must be uninstalled in reverse order
304
336
  * of installation (LIFO - Last In, First Out) to avoid conflicts.
305
337
  *
306
338
  * @public
307
339
  * @param {ElevaPlugin} plugin - The plugin object which must have an `install` function.
308
- * @param {Object<string, unknown>} [options={}] - Optional configuration options for the plugin.
309
- * @returns {Eleva} The Eleva instance (for method chaining).
340
+ * @param {PluginOptions} [options={}] - Optional configuration options for the plugin.
341
+ * @returns {Eleva | unknown} The Eleva instance (for method chaining) or the result returned by the plugin.
310
342
  * @throws {Error} If plugin does not have an install function.
343
+ * @see component - Register components after installing plugins.
344
+ * @see mount - Mount components to the DOM.
311
345
  * @example
312
346
  * app.use(myPlugin, { option1: "value1" });
313
347
  *
@@ -338,6 +372,7 @@ export class Eleva {
338
372
  * @param {ComponentDefinition} definition - The component definition including setup, template, style, and children.
339
373
  * @returns {Eleva} The Eleva instance (for method chaining).
340
374
  * @throws {Error} If name is not a non-empty string or definition has no template.
375
+ * @see mount - Mount this component to the DOM.
341
376
  * @example
342
377
  * app.component("myButton", {
343
378
  * template: (ctx) => `<button>${ctx.props.text}</button>`,
@@ -359,21 +394,25 @@ export class Eleva {
359
394
  /**
360
395
  * Mounts a registered component to a DOM element.
361
396
  * This will initialize the component, set up its reactive state, and render it to the DOM.
397
+ * If the container already has a mounted Eleva instance, it is returned as-is.
398
+ * Unmount clears the container contents and removes the internal instance marker.
362
399
  *
363
400
  * @public
401
+ * @async
364
402
  * @param {HTMLElement} container - The DOM element where the component will be mounted.
365
- * @param {string|ComponentDefinition} compName - The name of the registered component or a direct component definition.
366
- * @param {Object<string, unknown>} [props={}] - Optional properties to pass to the component.
403
+ * @param {string | ComponentDefinition} compName - The name of the registered component or a direct component definition.
404
+ * @param {ComponentProps} [props={}] - Optional properties to pass to the component.
367
405
  * @returns {Promise<MountResult>}
368
406
  * A Promise that resolves to an object containing:
369
407
  * - container: The mounted component's container element
370
408
  * - data: The component's reactive state and context
371
409
  * - unmount: Function to clean up and unmount the component
372
410
  * @throws {Error} If container is not a DOM element or component is not registered.
411
+ * @throws {Error} If setup function, template function, or style function throws.
373
412
  * @example
374
413
  * const instance = await app.mount(document.getElementById("app"), "myComponent", { text: "Click me" });
375
414
  * // Later...
376
- * instance.unmount();
415
+ * await instance.unmount();
377
416
  */
378
417
  async mount(container, compName, props = {}) {
379
418
  if (!container?.nodeType) {
@@ -394,7 +433,7 @@ export class Eleva {
394
433
  * Destructure the component definition to access core functionality.
395
434
  * - setup: Optional function for component initialization and state management
396
435
  * - template: Required function or string that returns the component's HTML structure
397
- * - style: Optional function or string for component-scoped CSS styles
436
+ * - style: Optional function or string for component CSS styles (not auto-scoped)
398
437
  * - children: Optional object defining nested child components
399
438
  */
400
439
  const { setup, template, style, children } = definition;
@@ -403,7 +442,7 @@ export class Eleva {
403
442
  const context = {
404
443
  props,
405
444
  emitter: this.emitter,
406
- /** @type {(v: unknown) => Signal<unknown>} */
445
+ /** @type {SignalFactory} */
407
446
  signal: (v) => new this.signal(v),
408
447
  };
409
448
 
@@ -415,20 +454,21 @@ export class Eleva {
415
454
  * 3. Rendering the component
416
455
  * 4. Managing component lifecycle
417
456
  *
418
- * @param {Object<string, unknown>} data - Data returned from the component's setup function
457
+ * @inner
458
+ * @param {Record<string, unknown>} data - Data returned from the component's setup function.
419
459
  * @returns {Promise<MountResult>} An object containing:
420
460
  * - container: The mounted component's container element
421
461
  * - data: The component's reactive state and context
422
462
  * - unmount: Function to clean up and unmount the component
423
463
  */
424
464
  const processMount = async (data) => {
425
- /** @type {ComponentContext} */
465
+ /** @type {ComponentContext & SetupResult} */
426
466
  const mergedContext = { ...context, ...data };
427
- /** @type {Array<() => void>} */
467
+ /** @type {UnsubscribeFunction[]} */
428
468
  const watchers = [];
429
- /** @type {Array<MountResult>} */
469
+ /** @type {MountResult[]} */
430
470
  const childInstances = [];
431
- /** @type {Array<() => void>} */
471
+ /** @type {UnsubscribeFunction[]} */
432
472
  const listeners = [];
433
473
  /** @private {boolean} Local mounted state for this component instance */
434
474
  let isMounted = false;
@@ -446,7 +486,10 @@ export class Eleva {
446
486
  * changes in the same synchronous block will each call this function,
447
487
  * but only one render will be scheduled via queueMicrotask.
448
488
  * This separates concerns: signals handle state, components handle scheduling.
489
+ *
490
+ * @inner
449
491
  * @private
492
+ * @returns {void}
450
493
  */
451
494
  const scheduleRender = () => {
452
495
  if (renderScheduled) return;
@@ -463,6 +506,10 @@ export class Eleva {
463
506
  * 2. Processing the template
464
507
  * 3. Updating the DOM
465
508
  * 4. Processing events, injecting styles, and mounting child components.
509
+ *
510
+ * @inner
511
+ * @private
512
+ * @returns {Promise<void>}
466
513
  */
467
514
  const render = async () => {
468
515
  const html =
@@ -484,6 +531,22 @@ export class Eleva {
484
531
  }
485
532
 
486
533
  this.renderer.patchDOM(container, html);
534
+
535
+ // Unmount child components whose host elements were removed by patching.
536
+ const childrenToUnmount = [];
537
+ for (let i = childInstances.length - 1; i >= 0; i--) {
538
+ const child = childInstances[i];
539
+ if (!container.contains(child.container)) {
540
+ childInstances.splice(i, 1);
541
+ childrenToUnmount.push(child);
542
+ }
543
+ }
544
+ if (childrenToUnmount.length) {
545
+ await Promise.allSettled(
546
+ childrenToUnmount.map((child) => child.unmount())
547
+ );
548
+ }
549
+
487
550
  this._processEvents(container, mergedContext, listeners);
488
551
  if (style) this._injectStyles(container, compId, style, mergedContext);
489
552
  if (children)
@@ -514,6 +577,10 @@ export class Eleva {
514
577
  * When a Signal's value changes, a batched render is scheduled.
515
578
  * Multiple changes within the same frame are collapsed into one render.
516
579
  * Stores unsubscribe functions to clean up watchers when component unmounts.
580
+ *
581
+ * @note Signal watchers are invoked synchronously when values change.
582
+ * Render batching is handled at the component level via queueMicrotask,
583
+ * not at the signal level. This preserves stack traces for debugging.
517
584
  */
518
585
  for (const val of Object.values(data)) {
519
586
  if (val instanceof Signal) watchers.push(val.watch(scheduleRender));
@@ -526,17 +593,17 @@ export class Eleva {
526
593
  data: mergedContext,
527
594
  /**
528
595
  * Unmounts the component, cleaning up watchers and listeners, child components, and clearing the container.
596
+ * Removes the internal instance marker from the container when complete.
529
597
  *
530
- * @returns {void}
598
+ * @returns {Promise<void>}
531
599
  */
532
600
  unmount: async () => {
533
- /** @type {UnmountHookContext} */
534
601
  await mergedContext.onUnmount?.({
535
602
  container,
536
603
  context: mergedContext,
537
604
  cleanup: {
538
- watchers: watchers,
539
- listeners: listeners,
605
+ watchers,
606
+ listeners,
540
607
  children: childInstances,
541
608
  },
542
609
  });
@@ -559,13 +626,19 @@ export class Eleva {
559
626
 
560
627
  /**
561
628
  * Processes DOM elements for event binding based on attributes starting with "@".
562
- * This method handles the event delegation system and ensures proper cleanup of event listeners.
629
+ * This method attaches event listeners directly to elements and ensures proper cleanup.
630
+ * Bound `@event` attributes are removed after listeners are attached.
631
+ *
632
+ * Handler resolution order:
633
+ * 1. Direct context property lookup (e.g., context["handleClick"])
634
+ * 2. Template expression evaluation via TemplateEngine (e.g., "increment()")
563
635
  *
564
636
  * @private
565
637
  * @param {HTMLElement} container - The container element in which to search for event attributes.
566
- * @param {ComponentContext} context - The current component context containing event handler definitions.
567
- * @param {Array<() => void>} listeners - Array to collect cleanup functions for each event listener.
638
+ * @param {ComponentContext & SetupResult} context - The merged component context and setup data.
639
+ * @param {UnsubscribeFunction[]} listeners - Array to collect cleanup functions for each event listener.
568
640
  * @returns {void}
641
+ * @see TemplateEngine.evaluate - Expression evaluation. fallback.
569
642
  */
570
643
  _processEvents(container, context, listeners) {
571
644
  /** @type {NodeListOf<Element>} */
@@ -583,7 +656,7 @@ export class Eleva {
583
656
  const event = attr.name.slice(1);
584
657
  /** @type {string} */
585
658
  const handlerName = attr.value;
586
- /** @type {(event: Event) => void} */
659
+ /** @type {DOMEventHandler} */
587
660
  const handler =
588
661
  context[handlerName] ||
589
662
  this.templateEngine.evaluate(handlerName, context);
@@ -597,14 +670,18 @@ export class Eleva {
597
670
  }
598
671
 
599
672
  /**
600
- * Injects scoped styles into the component's container.
601
- * The styles are automatically prefixed to prevent style leakage to other components.
673
+ * Injects styles into the component's container.
674
+ * Styles are placed in a `<style>` element with a `data-e-style` attribute for identification.
675
+ *
676
+ * @note Styles are not automatically scoped - use unique class names or CSS nesting for isolation.
677
+ *
678
+ * Optimization: Skips DOM update if style content hasn't changed.
602
679
  *
603
680
  * @private
604
681
  * @param {HTMLElement} container - The container element where styles should be injected.
605
682
  * @param {string} compId - The component ID used to identify the style element.
606
- * @param {(function(ComponentContext): string)|string} styleDef - The component's style definition (function or string).
607
- * @param {ComponentContext} context - The current component context for style interpolation.
683
+ * @param {StyleFunction | string} styleDef - The component's style definition (function or string).
684
+ * @param {ComponentContext & SetupResult} context - The merged component context and setup data.
608
685
  * @returns {void}
609
686
  */
610
687
  _injectStyles(container, compId, styleDef, context) {
@@ -612,7 +689,7 @@ export class Eleva {
612
689
  const newStyle =
613
690
  typeof styleDef === "function" ? styleDef(context) : styleDef;
614
691
 
615
- /** @type {HTMLStyleElement|null} */
692
+ /** @type {HTMLStyleElement | null} */
616
693
  let styleEl = container.querySelector(`style[data-e-style="${compId}"]`);
617
694
 
618
695
  if (styleEl && styleEl.textContent === newStyle) return;
@@ -629,11 +706,13 @@ export class Eleva {
629
706
  * Extracts and evaluates props from an element's attributes that start with `:`.
630
707
  * Prop values are evaluated as expressions against the component context,
631
708
  * allowing direct passing of objects, arrays, and other complex types.
709
+ * Processed attributes are removed from the element after extraction.
632
710
  *
633
711
  * @private
634
- * @param {HTMLElement} element - The DOM element to extract props from
635
- * @param {ComponentContext} context - The component context for evaluating prop expressions
636
- * @returns {Record<string, string>} An object containing the evaluated props
712
+ * @param {HTMLElement} element - The DOM element to extract props from.
713
+ * @param {ComponentContext & SetupResult} context - The merged component context and setup data.
714
+ * @returns {ComponentProps} An object containing the evaluated props.
715
+ * @see TemplateEngine.evaluate - Expression evaluation.
637
716
  * @example
638
717
  * // For an element with attributes:
639
718
  * // <div :name="user.name" :data="items">
@@ -662,20 +741,21 @@ export class Eleva {
662
741
  * This method handles mounting of explicitly defined children components.
663
742
  *
664
743
  * The mounting process follows these steps:
665
- * 1. Cleans up any existing component instances
744
+ * 1. Finds matching DOM nodes within the container
666
745
  * 2. Mounts explicitly defined children components
667
746
  *
668
747
  * @private
669
- * @param {HTMLElement} container - The container element to mount components in
670
- * @param {Object<string, ComponentDefinition>} children - Map of selectors to component definitions for explicit children
671
- * @param {Array<MountResult>} childInstances - Array to store all mounted component instances
672
- * @param {ComponentContext} context - The parent component context for evaluating prop expressions
748
+ * @async
749
+ * @param {HTMLElement} container - The container element to mount components in.
750
+ * @param {ChildrenMap} children - Map of selectors to component definitions for explicit children.
751
+ * @param {MountResult[]} childInstances - Array to store all mounted component instances.
752
+ * @param {ComponentContext & SetupResult} context - The merged component context and setup data.
673
753
  * @returns {Promise<void>}
674
754
  *
675
755
  * @example
676
756
  * // Explicit children mounting:
677
757
  * const children = {
678
- * 'UserProfile': UserProfileComponent,
758
+ * 'user-profile': UserProfileComponent,
679
759
  * '#settings-panel': "settings-panel"
680
760
  * };
681
761
  */
@@ -684,7 +764,7 @@ export class Eleva {
684
764
  if (!selector) continue;
685
765
  for (const el of container.querySelectorAll(selector)) {
686
766
  if (!(el instanceof HTMLElement)) continue;
687
- /** @type {Record<string, string>} */
767
+ /** @type {ComponentProps} */
688
768
  const props = this._extractProps(el, context);
689
769
  /** @type {MountResult} */
690
770
  const instance = await this.mount(el, component, props);