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.umd.js CHANGED
@@ -1,4 +1,4 @@
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
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -9,101 +9,45 @@
9
9
  // TYPE DEFINITIONS - TypeScript-friendly JSDoc types for IDE support
10
10
  // ============================================================================
11
11
  /**
12
- * @typedef {Record<string, unknown>} TemplateData
13
- * Data context for template interpolation
14
- */ /**
15
- * @typedef {string} TemplateString
16
- * A string containing {{ expression }} interpolation markers
12
+ * @typedef {Record<string, unknown>} ContextData
13
+ * Data context for expression evaluation
17
14
  */ /**
18
15
  * @typedef {string} Expression
19
16
  * A JavaScript expression to be evaluated in the data context
20
17
  */ /**
21
18
  * @typedef {unknown} EvaluationResult
22
- * The result of evaluating an expression (string, number, boolean, object, etc.)
19
+ * The result of evaluating an expression (string, number, boolean, object, function, etc.)
23
20
  */ /**
24
21
  * @class 🔒 TemplateEngine
25
- * @classdesc A secure template engine that handles interpolation and dynamic attribute parsing.
26
- * Provides a way to evaluate expressions in templates.
27
- * All methods are static and can be called directly on the class.
22
+ * @classdesc A minimal expression evaluator for Eleva's directive attributes.
23
+ * Evaluates JavaScript expressions against a component's context data.
24
+ * Used internally for `@event` handlers and `:prop` bindings.
28
25
  *
29
- * Template Syntax:
30
- * - `{{ expression }}` - Interpolate any JavaScript expression
31
- * - `{{ variable }}` - Access data properties directly
32
- * - `{{ object.property }}` - Access nested properties
33
- * - `{{ condition ? a : b }}` - Ternary expressions
34
- * - `{{ func(arg) }}` - Call functions from data context
26
+ * All methods are static and can be called directly on the class.
35
27
  *
36
28
  * @example
37
- * // Basic interpolation
38
- * const template = "Hello, {{name}}!";
39
- * const data = { name: "World" };
40
- * const result = TemplateEngine.parse(template, data);
41
- * // Result: "Hello, World!"
29
+ * // Property access
30
+ * TemplateEngine.evaluate("user.name", { user: { name: "John" } });
31
+ * // Result: "John"
42
32
  *
43
33
  * @example
44
- * // Nested properties
45
- * const template = "Welcome, {{user.name}}!";
46
- * const data = { user: { name: "John" } };
47
- * const result = TemplateEngine.parse(template, data);
48
- * // Result: "Welcome, John!"
34
+ * // Function reference (for @event handlers)
35
+ * TemplateEngine.evaluate("handleClick", { handleClick: () => console.log("clicked") });
36
+ * // Result: [Function]
49
37
  *
50
38
  * @example
51
- * // Expressions
52
- * const template = "Status: {{active ? 'Online' : 'Offline'}}";
53
- * const data = { active: true };
54
- * const result = TemplateEngine.parse(template, data);
55
- * // Result: "Status: Online"
39
+ * // Signal values (for :prop bindings)
40
+ * TemplateEngine.evaluate("count.value", { count: { value: 42 } });
41
+ * // Result: 42
56
42
  *
57
43
  * @example
58
- * // With Signal values
59
- * const template = "Count: {{count.value}}";
60
- * const data = { count: { value: 42 } };
61
- * const result = TemplateEngine.parse(template, data);
62
- * // Result: "Count: 42"
44
+ * // Complex expressions
45
+ * TemplateEngine.evaluate("items.filter(i => i.active)", { items: [{active: true}, {active: false}] });
46
+ * // Result: [{active: true}]
63
47
  */ class TemplateEngine {
64
48
  /**
65
- * Parses a template string, replacing expressions with their evaluated values.
66
- * Expressions are evaluated in the provided data context.
67
- *
68
- * @public
69
- * @static
70
- * @param {TemplateString|unknown} template - The template string to parse.
71
- * @param {TemplateData} data - The data context for evaluating expressions.
72
- * @returns {string} The parsed template with expressions replaced by their values.
73
- *
74
- * @example
75
- * // Simple variables
76
- * TemplateEngine.parse("Hello, {{name}}!", { name: "World" });
77
- * // Result: "Hello, World!"
78
- *
79
- * @example
80
- * // Nested properties
81
- * TemplateEngine.parse("{{user.name}} is {{user.age}} years old", {
82
- * user: { name: "John", age: 30 }
83
- * });
84
- * // Result: "John is 30 years old"
85
- *
86
- * @example
87
- * // Multiple expressions
88
- * TemplateEngine.parse("{{greeting}}, {{name}}! You have {{count}} messages.", {
89
- * greeting: "Hello",
90
- * name: "User",
91
- * count: 5
92
- * });
93
- * // Result: "Hello, User! You have 5 messages."
94
- *
95
- * @example
96
- * // With conditionals
97
- * TemplateEngine.parse("Status: {{online ? 'Active' : 'Inactive'}}", {
98
- * online: true
99
- * });
100
- * // Result: "Status: Active"
101
- */ static parse(template, data) {
102
- if (typeof template !== "string") return template;
103
- return template.replace(this.expressionPattern, (_, expression)=>this.evaluate(expression, data));
104
- }
105
- /**
106
49
  * Evaluates an expression in the context of the provided data object.
50
+ * Used for resolving `@event` handlers and `:prop` bindings.
107
51
  *
108
52
  * Note: This does not provide a true sandbox and evaluated expressions may access global scope.
109
53
  * The use of the `with` statement is necessary for expression evaluation but has security implications.
@@ -112,7 +56,7 @@
112
56
  * @public
113
57
  * @static
114
58
  * @param {Expression|unknown} expression - The expression to evaluate.
115
- * @param {TemplateData} data - The data context for evaluation.
59
+ * @param {ContextData} data - The data context for evaluation.
116
60
  * @returns {EvaluationResult} The result of the evaluation, or empty string if evaluation fails.
117
61
  *
118
62
  * @example
@@ -121,9 +65,19 @@
121
65
  * // Result: "John"
122
66
  *
123
67
  * @example
124
- * // Numeric values
125
- * TemplateEngine.evaluate("user.age", { user: { age: 30 } });
126
- * // Result: 30
68
+ * // Function reference
69
+ * TemplateEngine.evaluate("increment", { increment: () => count++ });
70
+ * // Result: [Function]
71
+ *
72
+ * @example
73
+ * // Nested property with Signal
74
+ * TemplateEngine.evaluate("count.value", { count: { value: 42 } });
75
+ * // Result: 42
76
+ *
77
+ * @example
78
+ * // Object reference (no JSON.stringify needed)
79
+ * TemplateEngine.evaluate("user", { user: { name: "John", age: 30 } });
80
+ * // Result: { name: "John", age: 30 }
127
81
  *
128
82
  * @example
129
83
  * // Expressions
@@ -131,19 +85,12 @@
131
85
  * // Result: true
132
86
  *
133
87
  * @example
134
- * // Function calls
135
- * TemplateEngine.evaluate("formatDate(date)", {
136
- * date: new Date(),
137
- * formatDate: (d) => d.toISOString()
138
- * });
139
- * // Result: "2024-01-01T00:00:00.000Z"
140
- *
141
- * @example
142
88
  * // Failed evaluation returns empty string
143
89
  * TemplateEngine.evaluate("nonexistent.property", {});
144
90
  * // Result: ""
145
91
  */ static evaluate(expression, data) {
146
92
  if (typeof expression !== "string") return expression;
93
+ if (!expression.trim()) return "";
147
94
  let fn = this._functionCache.get(expression);
148
95
  if (!fn) {
149
96
  try {
@@ -160,14 +107,6 @@
160
107
  }
161
108
  }
162
109
  }
163
- /**
164
- * Regular expression for matching template expressions in the format {{ expression }}
165
- * Matches: {{ anything }} with optional whitespace inside braces
166
- *
167
- * @static
168
- * @private
169
- * @type {RegExp}
170
- */ TemplateEngine.expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
171
110
  /**
172
111
  * Cache for compiled expression functions.
173
112
  * Stores compiled Function objects keyed by expression string for O(1) lookup.
@@ -1102,8 +1041,7 @@
1102
1041
  * 3. Updating the DOM
1103
1042
  * 4. Processing events, injecting styles, and mounting child components.
1104
1043
  */ const render = async ()=>{
1105
- const templateResult = typeof template === "function" ? await template(mergedContext) : template;
1106
- const html = this.templateEngine.parse(templateResult, mergedContext);
1044
+ const html = typeof template === "function" ? await template(mergedContext) : template;
1107
1045
  // Execute before hooks
1108
1046
  if (!isMounted) {
1109
1047
  await mergedContext.onBeforeMount?.({
@@ -1119,7 +1057,7 @@
1119
1057
  this.renderer.patchDOM(container, html);
1120
1058
  this._processEvents(container, mergedContext, listeners);
1121
1059
  if (style) this._injectStyles(container, compId, style, mergedContext);
1122
- if (children) await this._mountComponents(container, children, childInstances);
1060
+ if (children) await this._mountComponents(container, children, childInstances, mergedContext);
1123
1061
  // Execute after hooks
1124
1062
  if (!isMounted) {
1125
1063
  await mergedContext.onMount?.({
@@ -1212,7 +1150,7 @@
1212
1150
  * @param {ComponentContext} context - The current component context for style interpolation.
1213
1151
  * @returns {void}
1214
1152
  */ _injectStyles(container, compId, styleDef, context) {
1215
- /** @type {string} */ const newStyle = typeof styleDef === "function" ? this.templateEngine.parse(styleDef(context), context) : styleDef;
1153
+ /** @type {string} */ const newStyle = typeof styleDef === "function" ? styleDef(context) : styleDef;
1216
1154
  /** @type {HTMLStyleElement|null} */ let styleEl = container.querySelector(`style[data-e-style="${compId}"]`);
1217
1155
  if (styleEl && styleEl.textContent === newStyle) return;
1218
1156
  if (!styleEl) {
@@ -1223,17 +1161,20 @@
1223
1161
  styleEl.textContent = newStyle;
1224
1162
  }
1225
1163
  /**
1226
- * Extracts props from an element's attributes that start with the specified prefix.
1227
- * This method is used to collect component properties from DOM elements.
1164
+ * Extracts and evaluates props from an element's attributes that start with `:`.
1165
+ * Prop values are evaluated as expressions against the component context,
1166
+ * allowing direct passing of objects, arrays, and other complex types.
1228
1167
  *
1229
1168
  * @private
1230
1169
  * @param {HTMLElement} element - The DOM element to extract props from
1231
- * @returns {Record<string, string>} An object containing the extracted props
1170
+ * @param {ComponentContext} context - The component context for evaluating prop expressions
1171
+ * @returns {Record<string, string>} An object containing the evaluated props
1232
1172
  * @example
1233
1173
  * // For an element with attributes:
1234
- * // <div :name="John" :age="25">
1235
- * // Returns: { name: "John", age: "25" }
1236
- */ _extractProps(element) {
1174
+ * // <div :name="user.name" :data="items">
1175
+ * // With context: { user: { name: "John" }, items: [1, 2, 3] }
1176
+ * // Returns: { name: "John", data: [1, 2, 3] }
1177
+ */ _extractProps(element, context) {
1237
1178
  if (!element.attributes) return {};
1238
1179
  const props = {};
1239
1180
  const attrs = element.attributes;
@@ -1241,7 +1182,7 @@
1241
1182
  const attr = attrs[i];
1242
1183
  if (attr.name.startsWith(":")) {
1243
1184
  const propName = attr.name.slice(1);
1244
- props[propName] = attr.value;
1185
+ props[propName] = this.templateEngine.evaluate(attr.value, context);
1245
1186
  element.removeAttribute(attr.name);
1246
1187
  }
1247
1188
  }
@@ -1259,6 +1200,7 @@
1259
1200
  * @param {HTMLElement} container - The container element to mount components in
1260
1201
  * @param {Object<string, ComponentDefinition>} children - Map of selectors to component definitions for explicit children
1261
1202
  * @param {Array<MountResult>} childInstances - Array to store all mounted component instances
1203
+ * @param {ComponentContext} context - The parent component context for evaluating prop expressions
1262
1204
  * @returns {Promise<void>}
1263
1205
  *
1264
1206
  * @example
@@ -1267,12 +1209,12 @@
1267
1209
  * 'UserProfile': UserProfileComponent,
1268
1210
  * '#settings-panel': "settings-panel"
1269
1211
  * };
1270
- */ async _mountComponents(container, children, childInstances) {
1212
+ */ async _mountComponents(container, children, childInstances, context) {
1271
1213
  for (const [selector, component] of Object.entries(children)){
1272
1214
  if (!selector) continue;
1273
1215
  for (const el of container.querySelectorAll(selector)){
1274
1216
  if (!(el instanceof HTMLElement)) continue;
1275
- /** @type {Record<string, string>} */ const props = this._extractProps(el);
1217
+ /** @type {Record<string, string>} */ const props = this._extractProps(el, context);
1276
1218
  /** @type {MountResult} */ const instance = await this.mount(el, component, props);
1277
1219
  if (instance && !childInstances.includes(instance)) {
1278
1220
  childInstances.push(instance);