eleva 1.0.0-rc.13 → 1.0.0-rc.14

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 (44) hide show
  1. package/README.md +20 -75
  2. package/dist/eleva-plugins.cjs.js +4 -653
  3. package/dist/eleva-plugins.cjs.js.map +1 -1
  4. package/dist/eleva-plugins.esm.js +5 -653
  5. package/dist/eleva-plugins.esm.js.map +1 -1
  6. package/dist/eleva-plugins.umd.js +4 -653
  7. package/dist/eleva-plugins.umd.js.map +1 -1
  8. package/dist/eleva-plugins.umd.min.js +1 -1
  9. package/dist/eleva-plugins.umd.min.js.map +1 -1
  10. package/dist/eleva.cjs.js +52 -110
  11. package/dist/eleva.cjs.js.map +1 -1
  12. package/dist/eleva.d.ts +47 -109
  13. package/dist/eleva.esm.js +52 -110
  14. package/dist/eleva.esm.js.map +1 -1
  15. package/dist/eleva.umd.js +52 -110
  16. package/dist/eleva.umd.js.map +1 -1
  17. package/dist/eleva.umd.min.js +1 -1
  18. package/dist/eleva.umd.min.js.map +1 -1
  19. package/dist/plugins/attr.umd.js +2 -2
  20. package/dist/plugins/attr.umd.js.map +1 -1
  21. package/dist/plugins/attr.umd.min.js +1 -1
  22. package/dist/plugins/attr.umd.min.js.map +1 -1
  23. package/dist/plugins/router.umd.js +1 -1
  24. package/dist/plugins/router.umd.js.map +1 -1
  25. package/dist/plugins/router.umd.min.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/core/Eleva.js +21 -15
  28. package/src/modules/TemplateEngine.js +36 -104
  29. package/src/plugins/Attr.js +2 -2
  30. package/src/plugins/Router.js +1 -1
  31. package/src/plugins/index.js +1 -1
  32. package/types/core/Eleva.d.ts +9 -5
  33. package/types/core/Eleva.d.ts.map +1 -1
  34. package/types/modules/TemplateEngine.d.ts +38 -104
  35. package/types/modules/TemplateEngine.d.ts.map +1 -1
  36. package/types/plugins/Router.d.ts +1 -1
  37. package/types/plugins/index.d.ts +0 -1
  38. package/dist/plugins/props.umd.js +0 -660
  39. package/dist/plugins/props.umd.js.map +0 -1
  40. package/dist/plugins/props.umd.min.js +0 -2
  41. package/dist/plugins/props.umd.min.js.map +0 -1
  42. package/src/plugins/Props.js +0 -602
  43. package/types/plugins/Props.d.ts +0 -49
  44. package/types/plugins/Props.d.ts.map +0 -1
package/dist/eleva.esm.js CHANGED
@@ -1,103 +1,47 @@
1
- /*! Eleva v1.0.0-rc.13 | MIT License | https://elevajs.com */
1
+ /*! Eleva v1.0.0-rc.14 | MIT License | https://elevajs.com */
2
2
  // ============================================================================
3
3
  // TYPE DEFINITIONS - TypeScript-friendly JSDoc types for IDE support
4
4
  // ============================================================================
5
5
  /**
6
- * @typedef {Record<string, unknown>} TemplateData
7
- * Data context for template interpolation
8
- */ /**
9
- * @typedef {string} TemplateString
10
- * A string containing {{ expression }} interpolation markers
6
+ * @typedef {Record<string, unknown>} ContextData
7
+ * Data context for expression evaluation
11
8
  */ /**
12
9
  * @typedef {string} Expression
13
10
  * A JavaScript expression to be evaluated in the data context
14
11
  */ /**
15
12
  * @typedef {unknown} EvaluationResult
16
- * The result of evaluating an expression (string, number, boolean, object, etc.)
13
+ * The result of evaluating an expression (string, number, boolean, object, function, etc.)
17
14
  */ /**
18
15
  * @class 🔒 TemplateEngine
19
- * @classdesc A secure template engine that handles interpolation and dynamic attribute parsing.
20
- * Provides a way to evaluate expressions in templates.
21
- * All methods are static and can be called directly on the class.
16
+ * @classdesc A minimal expression evaluator for Eleva's directive attributes.
17
+ * Evaluates JavaScript expressions against a component's context data.
18
+ * Used internally for `@event` handlers and `:prop` bindings.
22
19
  *
23
- * Template Syntax:
24
- * - `{{ expression }}` - Interpolate any JavaScript expression
25
- * - `{{ variable }}` - Access data properties directly
26
- * - `{{ object.property }}` - Access nested properties
27
- * - `{{ condition ? a : b }}` - Ternary expressions
28
- * - `{{ func(arg) }}` - Call functions from data context
20
+ * All methods are static and can be called directly on the class.
29
21
  *
30
22
  * @example
31
- * // Basic interpolation
32
- * const template = "Hello, {{name}}!";
33
- * const data = { name: "World" };
34
- * const result = TemplateEngine.parse(template, data);
35
- * // Result: "Hello, World!"
23
+ * // Property access
24
+ * TemplateEngine.evaluate("user.name", { user: { name: "John" } });
25
+ * // Result: "John"
36
26
  *
37
27
  * @example
38
- * // Nested properties
39
- * const template = "Welcome, {{user.name}}!";
40
- * const data = { user: { name: "John" } };
41
- * const result = TemplateEngine.parse(template, data);
42
- * // Result: "Welcome, John!"
28
+ * // Function reference (for @event handlers)
29
+ * TemplateEngine.evaluate("handleClick", { handleClick: () => console.log("clicked") });
30
+ * // Result: [Function]
43
31
  *
44
32
  * @example
45
- * // Expressions
46
- * const template = "Status: {{active ? 'Online' : 'Offline'}}";
47
- * const data = { active: true };
48
- * const result = TemplateEngine.parse(template, data);
49
- * // Result: "Status: Online"
33
+ * // Signal values (for :prop bindings)
34
+ * TemplateEngine.evaluate("count.value", { count: { value: 42 } });
35
+ * // Result: 42
50
36
  *
51
37
  * @example
52
- * // With Signal values
53
- * const template = "Count: {{count.value}}";
54
- * const data = { count: { value: 42 } };
55
- * const result = TemplateEngine.parse(template, data);
56
- * // Result: "Count: 42"
38
+ * // Complex expressions
39
+ * TemplateEngine.evaluate("items.filter(i => i.active)", { items: [{active: true}, {active: false}] });
40
+ * // Result: [{active: true}]
57
41
  */ class TemplateEngine {
58
42
  /**
59
- * Parses a template string, replacing expressions with their evaluated values.
60
- * Expressions are evaluated in the provided data context.
61
- *
62
- * @public
63
- * @static
64
- * @param {TemplateString|unknown} template - The template string to parse.
65
- * @param {TemplateData} data - The data context for evaluating expressions.
66
- * @returns {string} The parsed template with expressions replaced by their values.
67
- *
68
- * @example
69
- * // Simple variables
70
- * TemplateEngine.parse("Hello, {{name}}!", { name: "World" });
71
- * // Result: "Hello, World!"
72
- *
73
- * @example
74
- * // Nested properties
75
- * TemplateEngine.parse("{{user.name}} is {{user.age}} years old", {
76
- * user: { name: "John", age: 30 }
77
- * });
78
- * // Result: "John is 30 years old"
79
- *
80
- * @example
81
- * // Multiple expressions
82
- * TemplateEngine.parse("{{greeting}}, {{name}}! You have {{count}} messages.", {
83
- * greeting: "Hello",
84
- * name: "User",
85
- * count: 5
86
- * });
87
- * // Result: "Hello, User! You have 5 messages."
88
- *
89
- * @example
90
- * // With conditionals
91
- * TemplateEngine.parse("Status: {{online ? 'Active' : 'Inactive'}}", {
92
- * online: true
93
- * });
94
- * // Result: "Status: Active"
95
- */ static parse(template, data) {
96
- if (typeof template !== "string") return template;
97
- return template.replace(this.expressionPattern, (_, expression)=>this.evaluate(expression, data));
98
- }
99
- /**
100
43
  * Evaluates an expression in the context of the provided data object.
44
+ * Used for resolving `@event` handlers and `:prop` bindings.
101
45
  *
102
46
  * Note: This does not provide a true sandbox and evaluated expressions may access global scope.
103
47
  * The use of the `with` statement is necessary for expression evaluation but has security implications.
@@ -106,7 +50,7 @@
106
50
  * @public
107
51
  * @static
108
52
  * @param {Expression|unknown} expression - The expression to evaluate.
109
- * @param {TemplateData} data - The data context for evaluation.
53
+ * @param {ContextData} data - The data context for evaluation.
110
54
  * @returns {EvaluationResult} The result of the evaluation, or empty string if evaluation fails.
111
55
  *
112
56
  * @example
@@ -115,9 +59,19 @@
115
59
  * // Result: "John"
116
60
  *
117
61
  * @example
118
- * // Numeric values
119
- * TemplateEngine.evaluate("user.age", { user: { age: 30 } });
120
- * // Result: 30
62
+ * // Function reference
63
+ * TemplateEngine.evaluate("increment", { increment: () => count++ });
64
+ * // Result: [Function]
65
+ *
66
+ * @example
67
+ * // Nested property with Signal
68
+ * TemplateEngine.evaluate("count.value", { count: { value: 42 } });
69
+ * // Result: 42
70
+ *
71
+ * @example
72
+ * // Object reference (no JSON.stringify needed)
73
+ * TemplateEngine.evaluate("user", { user: { name: "John", age: 30 } });
74
+ * // Result: { name: "John", age: 30 }
121
75
  *
122
76
  * @example
123
77
  * // Expressions
@@ -125,19 +79,12 @@
125
79
  * // Result: true
126
80
  *
127
81
  * @example
128
- * // Function calls
129
- * TemplateEngine.evaluate("formatDate(date)", {
130
- * date: new Date(),
131
- * formatDate: (d) => d.toISOString()
132
- * });
133
- * // Result: "2024-01-01T00:00:00.000Z"
134
- *
135
- * @example
136
82
  * // Failed evaluation returns empty string
137
83
  * TemplateEngine.evaluate("nonexistent.property", {});
138
84
  * // Result: ""
139
85
  */ static evaluate(expression, data) {
140
86
  if (typeof expression !== "string") return expression;
87
+ if (!expression.trim()) return "";
141
88
  let fn = this._functionCache.get(expression);
142
89
  if (!fn) {
143
90
  try {
@@ -154,14 +101,6 @@
154
101
  }
155
102
  }
156
103
  }
157
- /**
158
- * Regular expression for matching template expressions in the format {{ expression }}
159
- * Matches: {{ anything }} with optional whitespace inside braces
160
- *
161
- * @static
162
- * @private
163
- * @type {RegExp}
164
- */ TemplateEngine.expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
165
104
  /**
166
105
  * Cache for compiled expression functions.
167
106
  * Stores compiled Function objects keyed by expression string for O(1) lookup.
@@ -1096,8 +1035,7 @@
1096
1035
  * 3. Updating the DOM
1097
1036
  * 4. Processing events, injecting styles, and mounting child components.
1098
1037
  */ const render = async ()=>{
1099
- const templateResult = typeof template === "function" ? await template(mergedContext) : template;
1100
- const html = this.templateEngine.parse(templateResult, mergedContext);
1038
+ const html = typeof template === "function" ? await template(mergedContext) : template;
1101
1039
  // Execute before hooks
1102
1040
  if (!isMounted) {
1103
1041
  await mergedContext.onBeforeMount?.({
@@ -1113,7 +1051,7 @@
1113
1051
  this.renderer.patchDOM(container, html);
1114
1052
  this._processEvents(container, mergedContext, listeners);
1115
1053
  if (style) this._injectStyles(container, compId, style, mergedContext);
1116
- if (children) await this._mountComponents(container, children, childInstances);
1054
+ if (children) await this._mountComponents(container, children, childInstances, mergedContext);
1117
1055
  // Execute after hooks
1118
1056
  if (!isMounted) {
1119
1057
  await mergedContext.onMount?.({
@@ -1206,7 +1144,7 @@
1206
1144
  * @param {ComponentContext} context - The current component context for style interpolation.
1207
1145
  * @returns {void}
1208
1146
  */ _injectStyles(container, compId, styleDef, context) {
1209
- /** @type {string} */ const newStyle = typeof styleDef === "function" ? this.templateEngine.parse(styleDef(context), context) : styleDef;
1147
+ /** @type {string} */ const newStyle = typeof styleDef === "function" ? styleDef(context) : styleDef;
1210
1148
  /** @type {HTMLStyleElement|null} */ let styleEl = container.querySelector(`style[data-e-style="${compId}"]`);
1211
1149
  if (styleEl && styleEl.textContent === newStyle) return;
1212
1150
  if (!styleEl) {
@@ -1217,17 +1155,20 @@
1217
1155
  styleEl.textContent = newStyle;
1218
1156
  }
1219
1157
  /**
1220
- * Extracts props from an element's attributes that start with the specified prefix.
1221
- * This method is used to collect component properties from DOM elements.
1158
+ * Extracts and evaluates props from an element's attributes that start with `:`.
1159
+ * Prop values are evaluated as expressions against the component context,
1160
+ * allowing direct passing of objects, arrays, and other complex types.
1222
1161
  *
1223
1162
  * @private
1224
1163
  * @param {HTMLElement} element - The DOM element to extract props from
1225
- * @returns {Record<string, string>} An object containing the extracted props
1164
+ * @param {ComponentContext} context - The component context for evaluating prop expressions
1165
+ * @returns {Record<string, string>} An object containing the evaluated props
1226
1166
  * @example
1227
1167
  * // For an element with attributes:
1228
- * // <div :name="John" :age="25">
1229
- * // Returns: { name: "John", age: "25" }
1230
- */ _extractProps(element) {
1168
+ * // <div :name="user.name" :data="items">
1169
+ * // With context: { user: { name: "John" }, items: [1, 2, 3] }
1170
+ * // Returns: { name: "John", data: [1, 2, 3] }
1171
+ */ _extractProps(element, context) {
1231
1172
  if (!element.attributes) return {};
1232
1173
  const props = {};
1233
1174
  const attrs = element.attributes;
@@ -1235,7 +1176,7 @@
1235
1176
  const attr = attrs[i];
1236
1177
  if (attr.name.startsWith(":")) {
1237
1178
  const propName = attr.name.slice(1);
1238
- props[propName] = attr.value;
1179
+ props[propName] = this.templateEngine.evaluate(attr.value, context);
1239
1180
  element.removeAttribute(attr.name);
1240
1181
  }
1241
1182
  }
@@ -1253,6 +1194,7 @@
1253
1194
  * @param {HTMLElement} container - The container element to mount components in
1254
1195
  * @param {Object<string, ComponentDefinition>} children - Map of selectors to component definitions for explicit children
1255
1196
  * @param {Array<MountResult>} childInstances - Array to store all mounted component instances
1197
+ * @param {ComponentContext} context - The parent component context for evaluating prop expressions
1256
1198
  * @returns {Promise<void>}
1257
1199
  *
1258
1200
  * @example
@@ -1261,12 +1203,12 @@
1261
1203
  * 'UserProfile': UserProfileComponent,
1262
1204
  * '#settings-panel': "settings-panel"
1263
1205
  * };
1264
- */ async _mountComponents(container, children, childInstances) {
1206
+ */ async _mountComponents(container, children, childInstances, context) {
1265
1207
  for (const [selector, component] of Object.entries(children)){
1266
1208
  if (!selector) continue;
1267
1209
  for (const el of container.querySelectorAll(selector)){
1268
1210
  if (!(el instanceof HTMLElement)) continue;
1269
- /** @type {Record<string, string>} */ const props = this._extractProps(el);
1211
+ /** @type {Record<string, string>} */ const props = this._extractProps(el, context);
1270
1212
  /** @type {MountResult} */ const instance = await this.mount(el, component, props);
1271
1213
  if (instance && !childInstances.includes(instance)) {
1272
1214
  childInstances.push(instance);