eleva 1.0.0-rc.7 → 1.0.0-rc.9
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.
- package/README.md +6 -6
- package/dist/eleva-plugins.cjs.js +114 -42
- package/dist/eleva-plugins.cjs.js.map +1 -1
- package/dist/eleva-plugins.esm.js +114 -42
- package/dist/eleva-plugins.esm.js.map +1 -1
- package/dist/eleva-plugins.umd.js +114 -42
- package/dist/eleva-plugins.umd.js.map +1 -1
- package/dist/eleva-plugins.umd.min.js +2 -2
- package/dist/eleva-plugins.umd.min.js.map +1 -1
- package/dist/eleva.cjs.js +59 -54
- package/dist/eleva.cjs.js.map +1 -1
- package/dist/eleva.d.ts +63 -2
- package/dist/eleva.esm.js +59 -54
- package/dist/eleva.esm.js.map +1 -1
- package/dist/eleva.umd.js +59 -54
- package/dist/eleva.umd.js.map +1 -1
- package/dist/eleva.umd.min.js +2 -2
- package/dist/eleva.umd.min.js.map +1 -1
- package/dist/plugins/attr.umd.js +2 -2
- package/dist/plugins/attr.umd.js.map +1 -1
- package/dist/plugins/attr.umd.min.js +1 -1
- package/dist/plugins/attr.umd.min.js.map +1 -1
- package/dist/plugins/props.umd.js +15 -18
- package/dist/plugins/props.umd.js.map +1 -1
- package/dist/plugins/props.umd.min.js +1 -1
- package/dist/plugins/props.umd.min.js.map +1 -1
- package/dist/plugins/router.umd.js +25 -22
- package/dist/plugins/router.umd.js.map +1 -1
- package/dist/plugins/router.umd.min.js +1 -1
- package/dist/plugins/router.umd.min.js.map +1 -1
- package/dist/plugins/store.umd.js +71 -19
- package/dist/plugins/store.umd.js.map +1 -1
- package/dist/plugins/store.umd.min.js +1 -1
- package/dist/plugins/store.umd.min.js.map +1 -1
- package/package.json +17 -16
- package/src/core/Eleva.js +42 -27
- package/types/core/Eleva.d.ts +14 -2
- package/types/core/Eleva.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -40,9 +40,9 @@ Pure JavaScript, Pure Performance, Simply Elegant.
|
|
|
40
40
|
**A minimalist, lightweight, pure vanilla JavaScript frontend runtime framework.**
|
|
41
41
|
_Built with love for native JavaScript and designed with a minimal core that can be extended through a powerful plugin system-because sometimes, less really is more!_ 😊
|
|
42
42
|
|
|
43
|
-
> **Stability Notice**: This is `v1.0.0-rc.
|
|
43
|
+
> **Stability Notice**: This is `v1.0.0-rc.9` - The core functionality is stable. Seeking community feedback before the final v1.0.0 release.
|
|
44
44
|
|
|
45
|
-
**Version:** `1.0.0-rc.
|
|
45
|
+
**Version:** `1.0.0-rc.9`
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
|
|
@@ -205,10 +205,10 @@ Preliminary benchmarks illustrate Eleva's efficiency compared to popular framewo
|
|
|
205
205
|
|
|
206
206
|
| **Framework** | **Bundle Size** (KB) | **Initial Load Time** (ms) | **DOM Update Speed** (s) | **Peak Memory Usage** (KB) | **Overall Performance Score** (lower is better) |
|
|
207
207
|
| ----------------------------- | -------------------- | -------------------------- | ------------------------ | -------------------------- | ----------------------------------------------- |
|
|
208
|
-
| **Eleva** (Direct DOM) | **2**
|
|
209
|
-
| **React** (Virtual DOM) | 4.1
|
|
210
|
-
| **Vue** (Reactive State) | 45 | 4.72
|
|
211
|
-
| **Angular** (Two-way Binding) | 62 | 5.26
|
|
208
|
+
| **Eleva** (Direct DOM) | **2** | **0.05** | **0.002** | **0.25** | **0.58 (Best)** |
|
|
209
|
+
| **React** (Virtual DOM) | 4.1 | 5.34 | 0.020 | 0.25 | 9.71 |
|
|
210
|
+
| **Vue** (Reactive State) | 45 | 4.72 | 0.021 | 3.10 | 13.21 |
|
|
211
|
+
| **Angular** (Two-way Binding) | 62 | 5.26 | 0.021 | 0.25 | 16.88 (Slowest) |
|
|
212
212
|
|
|
213
213
|
Detailed [Benchmark Metrics Report](BENCHMARK.md)
|
|
214
214
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Eleva Plugins v1.0.0-rc.
|
|
1
|
+
/*! Eleva Plugins v1.0.0-rc.9 | MIT License | https://elevajs.com */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -129,7 +129,7 @@ const AttrPlugin = {
|
|
|
129
129
|
if (hasProperty) {
|
|
130
130
|
// Boolean attribute handling
|
|
131
131
|
if (enableBoolean) {
|
|
132
|
-
const isBoolean = typeof oldEl[prop] === "boolean" ||
|
|
132
|
+
const isBoolean = typeof oldEl[prop] === "boolean" || descriptor?.get && typeof descriptor.get.call(oldEl) === "boolean";
|
|
133
133
|
if (isBoolean) {
|
|
134
134
|
const boolValue = value !== "false" && (value === "" || value === prop || value === "true");
|
|
135
135
|
oldEl[prop] = boolValue;
|
|
@@ -171,7 +171,7 @@ const AttrPlugin = {
|
|
|
171
171
|
|
|
172
172
|
// Override the _patchNode method to use our attribute handler
|
|
173
173
|
eleva.renderer._patchNode = function (oldNode, newNode) {
|
|
174
|
-
if (oldNode
|
|
174
|
+
if (oldNode?._eleva_instance) return;
|
|
175
175
|
if (!this._isSameNode(oldNode, newNode)) {
|
|
176
176
|
oldNode.replaceWith(newNode.cloneNode(true));
|
|
177
177
|
return;
|
|
@@ -221,16 +221,18 @@ const AttrPlugin = {
|
|
|
221
221
|
}
|
|
222
222
|
};
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
return n;
|
|
231
|
-
}, _extends.apply(null, arguments);
|
|
232
|
-
}
|
|
224
|
+
/**
|
|
225
|
+
* @typedef {import('eleva').Eleva} Eleva
|
|
226
|
+
* @typedef {import('eleva').Signal} Signal
|
|
227
|
+
* @typedef {import('eleva').ComponentDefinition} ComponentDefinition
|
|
228
|
+
*/
|
|
233
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Simple error handler for the core router.
|
|
232
|
+
* Can be overridden by error handling plugins.
|
|
233
|
+
* Provides consistent error formatting and logging for router operations.
|
|
234
|
+
* @private
|
|
235
|
+
*/
|
|
234
236
|
const CoreErrorHandler = {
|
|
235
237
|
/**
|
|
236
238
|
* Handles router errors with basic formatting.
|
|
@@ -339,11 +341,12 @@ class Router {
|
|
|
339
341
|
this.eleva = eleva;
|
|
340
342
|
|
|
341
343
|
/** @type {RouterOptions} The merged router options. */
|
|
342
|
-
this.options =
|
|
344
|
+
this.options = {
|
|
343
345
|
mode: "hash",
|
|
344
346
|
queryParam: "view",
|
|
345
|
-
viewSelector: "root"
|
|
346
|
-
|
|
347
|
+
viewSelector: "root",
|
|
348
|
+
...options
|
|
349
|
+
};
|
|
347
350
|
|
|
348
351
|
/** @private @type {RouteDefinition[]} The processed list of route definitions. */
|
|
349
352
|
this.routes = this._processRoutes(options.routes || []);
|
|
@@ -407,9 +410,10 @@ class Router {
|
|
|
407
410
|
const processedRoutes = [];
|
|
408
411
|
for (const route of routes) {
|
|
409
412
|
try {
|
|
410
|
-
processedRoutes.push(
|
|
413
|
+
processedRoutes.push({
|
|
414
|
+
...route,
|
|
411
415
|
segments: this._parsePathIntoSegments(route.path)
|
|
412
|
-
})
|
|
416
|
+
});
|
|
413
417
|
} catch (error) {
|
|
414
418
|
this.errorHandler.warn(`Invalid path in route definition "${route.path || "undefined"}": ${error.message}`, {
|
|
415
419
|
route,
|
|
@@ -664,12 +668,13 @@ class Router {
|
|
|
664
668
|
return false;
|
|
665
669
|
}
|
|
666
670
|
}
|
|
667
|
-
const to =
|
|
671
|
+
const to = {
|
|
672
|
+
...toLocation,
|
|
668
673
|
params: toMatch.params,
|
|
669
674
|
meta: toMatch.route.meta || {},
|
|
670
675
|
name: toMatch.route.name,
|
|
671
676
|
matched: toMatch.route
|
|
672
|
-
}
|
|
677
|
+
};
|
|
673
678
|
try {
|
|
674
679
|
// 1. Run all *pre-navigation* guards.
|
|
675
680
|
const canNavigate = await this._runGuards(to, from, toMatch.route);
|
|
@@ -900,10 +905,7 @@ class Router {
|
|
|
900
905
|
* @returns {Function} A getter function.
|
|
901
906
|
*/
|
|
902
907
|
_createRouteGetter(property, defaultValue) {
|
|
903
|
-
return () =>
|
|
904
|
-
var _this$currentRoute$va, _this$currentRoute$va2;
|
|
905
|
-
return (_this$currentRoute$va = (_this$currentRoute$va2 = this.currentRoute.value) == null ? void 0 : _this$currentRoute$va2[property]) != null ? _this$currentRoute$va : defaultValue;
|
|
906
|
-
};
|
|
908
|
+
return () => this.currentRoute.value?.[property] ?? defaultValue;
|
|
907
909
|
}
|
|
908
910
|
|
|
909
911
|
/**
|
|
@@ -915,7 +917,8 @@ class Router {
|
|
|
915
917
|
_wrapComponent(component) {
|
|
916
918
|
const originalSetup = component.setup;
|
|
917
919
|
const self = this;
|
|
918
|
-
return
|
|
920
|
+
return {
|
|
921
|
+
...component,
|
|
919
922
|
async setup(ctx) {
|
|
920
923
|
ctx.router = {
|
|
921
924
|
navigate: self.navigate.bind(self),
|
|
@@ -940,7 +943,7 @@ class Router {
|
|
|
940
943
|
};
|
|
941
944
|
return originalSetup ? await originalSetup(ctx) : {};
|
|
942
945
|
}
|
|
943
|
-
}
|
|
946
|
+
};
|
|
944
947
|
}
|
|
945
948
|
|
|
946
949
|
/**
|
|
@@ -1334,6 +1337,12 @@ const RouterPlugin = {
|
|
|
1334
1337
|
* const result = TemplateEngine.parse(template, data); // Returns: "Hello, World!"
|
|
1335
1338
|
*/
|
|
1336
1339
|
class TemplateEngine {
|
|
1340
|
+
/**
|
|
1341
|
+
* @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
|
|
1342
|
+
* @type {RegExp}
|
|
1343
|
+
*/
|
|
1344
|
+
static expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
|
|
1345
|
+
|
|
1337
1346
|
/**
|
|
1338
1347
|
* Parses a template string, replacing expressions with their evaluated values.
|
|
1339
1348
|
* Expressions are evaluated in the provided data context.
|
|
@@ -1372,16 +1381,11 @@ class TemplateEngine {
|
|
|
1372
1381
|
if (typeof expression !== "string") return expression;
|
|
1373
1382
|
try {
|
|
1374
1383
|
return new Function("data", `with(data) { return ${expression}; }`)(data);
|
|
1375
|
-
} catch
|
|
1384
|
+
} catch {
|
|
1376
1385
|
return "";
|
|
1377
1386
|
}
|
|
1378
1387
|
}
|
|
1379
1388
|
}
|
|
1380
|
-
/**
|
|
1381
|
-
* @private {RegExp} Regular expression for matching template expressions in the format {{ expression }}
|
|
1382
|
-
* @type {RegExp}
|
|
1383
|
-
*/
|
|
1384
|
-
TemplateEngine.expressionPattern = /\{\{\s*(.*?)\s*\}\}/g;
|
|
1385
1389
|
|
|
1386
1390
|
/**
|
|
1387
1391
|
* @class 🎯 PropsPlugin
|
|
@@ -1717,7 +1721,10 @@ const PropsPlugin = {
|
|
|
1717
1721
|
});
|
|
1718
1722
|
|
|
1719
1723
|
// Merge signal props with regular props (signal props take precedence)
|
|
1720
|
-
enhancedProps =
|
|
1724
|
+
enhancedProps = {
|
|
1725
|
+
...extractedProps,
|
|
1726
|
+
...signalProps
|
|
1727
|
+
};
|
|
1721
1728
|
}
|
|
1722
1729
|
|
|
1723
1730
|
// Create reactive props for non-signal props only
|
|
@@ -1736,7 +1743,10 @@ const PropsPlugin = {
|
|
|
1736
1743
|
const reactiveNonSignalProps = createReactiveProps(nonSignalProps);
|
|
1737
1744
|
|
|
1738
1745
|
// Merge signal props with reactive non-signal props
|
|
1739
|
-
finalProps =
|
|
1746
|
+
finalProps = {
|
|
1747
|
+
...reactiveNonSignalProps,
|
|
1748
|
+
...enhancedProps // Signal props take precedence
|
|
1749
|
+
};
|
|
1740
1750
|
}
|
|
1741
1751
|
|
|
1742
1752
|
/** @type {MountResult} */
|
|
@@ -1907,6 +1917,61 @@ const PropsPlugin = {
|
|
|
1907
1917
|
}
|
|
1908
1918
|
};
|
|
1909
1919
|
|
|
1920
|
+
/**
|
|
1921
|
+
* @class 🏪 StorePlugin
|
|
1922
|
+
* @classdesc A powerful reactive state management plugin for Eleva.js that enables sharing
|
|
1923
|
+
* reactive data across the entire application. The Store plugin provides a centralized,
|
|
1924
|
+
* reactive data store that can be accessed from any component's setup function.
|
|
1925
|
+
*
|
|
1926
|
+
* Core Features:
|
|
1927
|
+
* - Centralized reactive state management using Eleva's signal system
|
|
1928
|
+
* - Global state accessibility through component setup functions
|
|
1929
|
+
* - Namespace support for organizing store modules
|
|
1930
|
+
* - Built-in persistence with localStorage/sessionStorage support
|
|
1931
|
+
* - Action-based state mutations with validation
|
|
1932
|
+
* - Subscription system for reactive updates
|
|
1933
|
+
* - DevTools integration for debugging
|
|
1934
|
+
* - Plugin architecture for extensibility
|
|
1935
|
+
*
|
|
1936
|
+
* @example
|
|
1937
|
+
* // Install the plugin
|
|
1938
|
+
* const app = new Eleva("myApp");
|
|
1939
|
+
* app.use(StorePlugin, {
|
|
1940
|
+
* state: {
|
|
1941
|
+
* user: { name: "John", email: "john@example.com" },
|
|
1942
|
+
* counter: 0,
|
|
1943
|
+
* todos: []
|
|
1944
|
+
* },
|
|
1945
|
+
* actions: {
|
|
1946
|
+
* increment: (state) => state.counter.value++,
|
|
1947
|
+
* addTodo: (state, todo) => state.todos.value.push(todo),
|
|
1948
|
+
* setUser: (state, user) => state.user.value = user
|
|
1949
|
+
* },
|
|
1950
|
+
* persistence: {
|
|
1951
|
+
* enabled: true,
|
|
1952
|
+
* key: "myApp-store",
|
|
1953
|
+
* storage: "localStorage"
|
|
1954
|
+
* }
|
|
1955
|
+
* });
|
|
1956
|
+
*
|
|
1957
|
+
* // Use store in components
|
|
1958
|
+
* app.component("Counter", {
|
|
1959
|
+
* setup({ store }) {
|
|
1960
|
+
* return {
|
|
1961
|
+
* count: store.state.counter,
|
|
1962
|
+
* increment: () => store.dispatch("increment"),
|
|
1963
|
+
* user: store.state.user
|
|
1964
|
+
* };
|
|
1965
|
+
* },
|
|
1966
|
+
* template: (ctx) => `
|
|
1967
|
+
* <div>
|
|
1968
|
+
* <p>Hello ${ctx.user.value.name}!</p>
|
|
1969
|
+
* <p>Count: ${ctx.count.value}</p>
|
|
1970
|
+
* <button onclick="ctx.increment()">+</button>
|
|
1971
|
+
* </div>
|
|
1972
|
+
* `
|
|
1973
|
+
* });
|
|
1974
|
+
*/
|
|
1910
1975
|
const StorePlugin = {
|
|
1911
1976
|
/**
|
|
1912
1977
|
* Unique identifier for the plugin
|
|
@@ -1994,13 +2059,14 @@ const StorePlugin = {
|
|
|
1994
2059
|
this.actions = {};
|
|
1995
2060
|
this.subscribers = new Set();
|
|
1996
2061
|
this.mutations = [];
|
|
1997
|
-
this.persistence =
|
|
2062
|
+
this.persistence = {
|
|
1998
2063
|
enabled: false,
|
|
1999
2064
|
key: "eleva-store",
|
|
2000
2065
|
storage: "localStorage",
|
|
2001
2066
|
include: null,
|
|
2002
|
-
exclude: null
|
|
2003
|
-
|
|
2067
|
+
exclude: null,
|
|
2068
|
+
...persistence
|
|
2069
|
+
};
|
|
2004
2070
|
this.devTools = devTools;
|
|
2005
2071
|
this.onError = onError;
|
|
2006
2072
|
this._initializeState(state, actions);
|
|
@@ -2020,7 +2086,9 @@ const StorePlugin = {
|
|
|
2020
2086
|
});
|
|
2021
2087
|
|
|
2022
2088
|
// Set up actions
|
|
2023
|
-
this.actions =
|
|
2089
|
+
this.actions = {
|
|
2090
|
+
...initialActions
|
|
2091
|
+
};
|
|
2024
2092
|
}
|
|
2025
2093
|
|
|
2026
2094
|
/**
|
|
@@ -2048,7 +2116,9 @@ const StorePlugin = {
|
|
|
2048
2116
|
});
|
|
2049
2117
|
|
|
2050
2118
|
// Set up namespaced actions
|
|
2051
|
-
this.actions[namespace] =
|
|
2119
|
+
this.actions[namespace] = {
|
|
2120
|
+
...moduleActions
|
|
2121
|
+
};
|
|
2052
2122
|
});
|
|
2053
2123
|
}
|
|
2054
2124
|
|
|
@@ -2376,7 +2446,8 @@ const StorePlugin = {
|
|
|
2376
2446
|
}
|
|
2377
2447
|
|
|
2378
2448
|
// Create a wrapped component that injects store into setup
|
|
2379
|
-
const wrappedComponent =
|
|
2449
|
+
const wrappedComponent = {
|
|
2450
|
+
...componentDef,
|
|
2380
2451
|
async setup(ctx) {
|
|
2381
2452
|
// Inject store into the context with enhanced API
|
|
2382
2453
|
ctx.store = {
|
|
@@ -2400,7 +2471,7 @@ const StorePlugin = {
|
|
|
2400
2471
|
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
2401
2472
|
return result;
|
|
2402
2473
|
}
|
|
2403
|
-
}
|
|
2474
|
+
};
|
|
2404
2475
|
|
|
2405
2476
|
// Call original mount with wrapped component
|
|
2406
2477
|
return await originalMount.call(eleva, container, wrappedComponent, props);
|
|
@@ -2414,7 +2485,8 @@ const StorePlugin = {
|
|
|
2414
2485
|
for (const [selector, childComponent] of Object.entries(children)) {
|
|
2415
2486
|
const componentDef = typeof childComponent === "string" ? eleva._components.get(childComponent) || childComponent : childComponent;
|
|
2416
2487
|
if (componentDef && typeof componentDef === "object") {
|
|
2417
|
-
wrappedChildren[selector] =
|
|
2488
|
+
wrappedChildren[selector] = {
|
|
2489
|
+
...componentDef,
|
|
2418
2490
|
async setup(ctx) {
|
|
2419
2491
|
// Inject store into the context with enhanced API
|
|
2420
2492
|
ctx.store = {
|
|
@@ -2438,7 +2510,7 @@ const StorePlugin = {
|
|
|
2438
2510
|
const result = originalSetup ? await originalSetup(ctx) : {};
|
|
2439
2511
|
return result;
|
|
2440
2512
|
}
|
|
2441
|
-
}
|
|
2513
|
+
};
|
|
2442
2514
|
} else {
|
|
2443
2515
|
wrappedChildren[selector] = childComponent;
|
|
2444
2516
|
}
|