olova 2.0.4 → 2.0.6

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 CHANGED
@@ -1,258 +1,54 @@
1
- # 🚀 Olova Framework Documentation
1
+ # Olova JavaScript Framework
2
2
 
3
- > A lightweight JavaScript framework for building web applications with JSX
3
+ Olova is a lightweight and reactive JavaScript framework that simplifies UI
4
+ development with a clean, intuitive syntax. It provides a reactivity system and
5
+ hooks to manage state and side effects, allowing developers to build modern web
6
+ applications with ease.
4
7
 
5
- ## What is Olova?
8
+ ## Features
6
9
 
7
- Olova is a simple yet powerful JavaScript framework that lets you build web
8
- applications using JSX. It directly manipulates the DOM without a Virtual DOM,
9
- making it lightweight and straightforward to use.
10
+ - **State Management**: Use the `State` hook to manage reactive state in your
11
+ components.
12
+ - **Side Effects**: Use the `Effect` hook to run side effects in response to
13
+ state changes.
14
+ - **JSX-style Syntax**: Write UI components using a simple, declarative
15
+ JSX-style syntax.
16
+ - **Reactivity**: Automatically re-render components when state changes.
10
17
 
11
- ## ⚡ Quick Start
18
+ ## Installation
12
19
 
13
- ### Installation
14
-
15
- Get started with a single command:
16
-
17
- ```bash
18
- npm create vilo@latest
19
- ```
20
-
21
- This command will set up a new Olova project, similar to how Vite works. You can
22
- use it to create an Olova template or install Olova in an existing project.
23
-
24
- Alternatively, you can install Olova directly:
20
+ To get started with Olova, first install the core library via npm or yarn.
25
21
 
26
22
  ```bash
27
23
  npm install olova
28
24
  ```
29
25
 
30
- ### Your First Olova App
31
-
32
- ```jsx
33
- import Olova from "olova";
34
-
35
- const App = () => {
36
- return (
37
- <>
38
- <h1>Welcome to Olova! 🎉</h1>
39
- <p>Building web apps made simple.</p>
40
- </>
41
- );
42
- };
43
- ```
44
-
45
- ## 🎯 Core Concepts
26
+ or
46
27
 
47
- ### Built-in Hooks
48
-
49
- Olova provides several essential hooks for building dynamic applications:
50
-
51
- ```jsx
52
- import {
53
- $signal, // Manage component state
54
- $effect, // Handle side effects
55
- $memo, // Memoize values
56
- $ref, // Reference DOM elements
57
- $context, // Share data between components
58
- $callback, // Memoize functions
59
- } from "olova";
60
- ```
61
-
62
- ### Default Exports
63
-
64
- Olova comes with two built-in utilities:
65
-
66
- ```jsx
67
- import Olova, { h, Fragment } from "olova";
68
-
69
- // h: Element generator (automatically used for JSX transformation)
70
- // Fragment: Wrapper for multiple elements, can be used as <></> or <Fragment></Fragment>
28
+ ```bash
29
+ yarn add olova
71
30
  ```
72
31
 
73
- ## 💫 Using Hooks
32
+ ## Example Usage
74
33
 
75
- ### State Management
34
+ Here is an example of a basic component in Olova:
76
35
 
77
- ```jsx
78
- import Olova, { $signal } from "olova";
36
+ ```js
37
+ import Olova, { State, Effect } from "olova";
79
38
 
80
- const Counter = () => {
39
+ export default function Home() {
81
40
  const [count, setCount] = State(0);
82
41
 
83
- return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
84
- };
85
- ```
86
-
87
- ### Side Effects
88
-
89
- ```jsx
90
- import Olova, { $effect, $signal } from "olova";
91
-
92
- const DataFetcher = () => {
93
- const [data, setData] = State(null);
94
-
95
- Effect(() => {
96
- fetch("/api/data")
97
- .then((res) => res.json())
98
- .then(setData);
99
- }, []); // Empty array means run once on mount
100
-
101
- return <div>{data ? "Data loaded!" : "Loading..."}</div>;
102
- };
103
- ```
104
-
105
- ### Using References
106
-
107
- ```jsx
108
- import Olova, { $ref } from "olova";
109
-
110
- const FocusInput = () => {
111
- const inputRef = $ref(null);
112
-
113
- return <input ref={inputRef} onFocus={() => console.log("Input focused!")} />;
114
- };
115
- ```
116
-
117
- ### Memoization
118
-
119
- ```jsx
120
- import Olova, { $memo, $signal } from "olova";
121
-
122
- const ExpensiveComponent = ({ data }) => {
123
- const processedData = Memo(() => {
124
- // Expensive computation here
125
- return data.map((item) => item * 2);
126
- }, [data]);
127
-
128
- return <div>{processedData.join(", ")}</div>;
129
- };
130
- ```
131
-
132
- ## 🎨 Styling in Olova
133
-
134
- Olova supports all standard CSS approaches:
135
-
136
- ### CSS Imports
137
-
138
- ```jsx
139
- import "./styles.css";
140
-
141
- const StyledComponent = () => (
142
- <div className="my-component">Styled content</div>
143
- );
144
- ```
145
-
146
- ### CSS Modules
147
-
148
- ```jsx
149
- import styles from "./Component.module.css";
150
-
151
- const ModuleStyledComponent = () => (
152
- <div className={styles.container}>Module styled content</div>
153
- );
154
- ```
155
-
156
- ### Inline Styles
157
-
158
- ```jsx
159
- const InlineStyledComponent = () => (
160
- <div style={{ padding: "20px", color: "blue" }}>Inline styled content</div>
161
- );
162
- ```
163
-
164
- ## 🔮 How Olova Works
165
-
166
- 1. **Direct DOM Manipulation**
167
-
168
- 1. Olova directly updates the DOM without a Virtual DOM
169
- 1. Efficient updates when state changes
170
- 1. Lightweight and fast performance
171
-
172
- 1. **JSX Transformation**
173
-
174
- 1. Uses the `h` function to transform JSX into DOM elements
175
- 1. Handles event binding automatically
176
- 1. Manages component lifecycle efficiently
177
-
178
- ## 📚 Best Practices
179
-
180
- ### Component Structure
181
-
182
- ```jsx
183
- // Good: Single responsibility component
184
- const UserProfile = ({ user }) => (
185
- <div className="profile">
186
- <img src={user.avatar || "/placeholder.svg"} alt={user.name} />
187
- <h2>{user.name}</h2>
188
- </div>
189
- );
190
-
191
- // Better: Using Fragment for multiple elements
192
- const UserCard = ({ user }) => (
193
- <>
194
- <UserProfile user={user} />
195
- <UserStats stats={user.stats} />
196
- </>
197
- );
198
- ```
199
-
200
- ### Hook Usage
201
-
202
- ```jsx
203
- // Effective use of multiple hooks
204
- const UserDashboard = () => {
205
- const [user, setUser] = State(null);
206
- const userCache = $ref({});
207
-
208
42
  Effect(() => {
209
- // Side effect cleanup example
210
- return () => {
211
- userCache.current = {};
212
- };
213
- }, []);
214
-
215
- return <div>Dashboard Content</div>;
216
- };
217
- ```
218
-
219
- ## 🚀 Coming Soon
220
-
221
- - More built-in hooks
222
- - Enhanced development tools
223
- - Additional utility functions
224
- - Performance optimizations
225
-
226
- ## 🤝 Contributing
227
-
228
- We welcome contributions! Whether it's:
229
-
230
- - Bug reports
231
- - Feature requests
232
- - Documentation improvements
233
- - Pull requests
234
-
235
- ## 📖 Examples
236
-
237
- Find more examples in our [GitHub repository](https://github.com/olovajs/olova).
238
-
239
- ## 🛠 Using Vilo
43
+ console.log("Home is rendered");
44
+ console.log(count);
45
+ }, [count]);
240
46
 
241
- Vilo is a project creation tool for Olova, similar to Vite. You can use it to
242
- quickly set up new Olova projects or add Olova to existing projects.
243
-
244
- To create a new Olova project:
245
-
246
- ```bash
247
- npm create vilo@latest my-olova-app
248
- cd my-olova-app
249
- npm install
250
- npm run dev
47
+ return (
48
+ <>
49
+ <h1>{count}</h1>
50
+ <button onClick={() => setCount(count + 1)}>Increment</button>
51
+ </>
52
+ );
53
+ }
251
54
  ```
252
-
253
- This will set up a new Olova project with a basic template, ready for you to
254
- start developing.
255
-
256
- ---
257
-
258
- Made with simplicity in mind 🌟
@@ -0,0 +1,4 @@
1
+ import o from "./memo.js";
2
+ export default function r(r, t, e) {
3
+ return o(r, () => t, e);
4
+ }
@@ -0,0 +1,21 @@
1
+ export default function e(e, r) {
2
+ const n = e.getCurrentInstance(),
3
+ o = n.currentHook++;
4
+ if (!n.hooks[o]) {
5
+ n.hooks[o] = r._currentValue;
6
+ const e = { instance: n, hookIndex: o, version: r._version };
7
+ r._subscribers.add(e),
8
+ n.contextSubscriptions.add(() => {
9
+ r._subscribers.delete(e);
10
+ });
11
+ }
12
+ const s = Array.from(r._subscribers).find(
13
+ (e) => e.instance === n && e.hookIndex === o
14
+ );
15
+ return (
16
+ s &&
17
+ s.version !== r._version &&
18
+ ((n.hooks[o] = r._currentValue), (s.version = r._version)),
19
+ r._currentValue
20
+ );
21
+ }
@@ -0,0 +1,11 @@
1
+ export default function e(e, n, t) {
2
+ const o = e.getCurrentInstance(),
3
+ s = o.currentHook++,
4
+ c = o.hooks[s];
5
+ (!c || !t || t.length !== c.length || t.some((e, n) => e !== c[n])) &&
6
+ o.pendingEffects.push(() => {
7
+ o.cleanups.has(s) && o.cleanups.get(s)();
8
+ const e = n();
9
+ (o.hooks[s] = t), "function" == typeof e && o.cleanups.set(s, e);
10
+ });
11
+ }
@@ -0,0 +1,15 @@
1
+ export default function o(o, t, n) {
2
+ const e = o.getCurrentInstance(),
3
+ r = e.currentHook++,
4
+ [s, u] = e.hooks[r] || [void 0, void 0];
5
+ if (
6
+ !u ||
7
+ !n ||
8
+ n.length !== u.length ||
9
+ n.some((t, n) => !o.shallowEqual(t, u[n]))
10
+ ) {
11
+ const o = t();
12
+ return (e.hooks[r] = [o, n]), o;
13
+ }
14
+ return s;
15
+ }
@@ -0,0 +1,11 @@
1
+ export default function o(o, e, t) {
2
+ const n = o.getCurrentInstance(),
3
+ s = n.currentHook++;
4
+ n.hooks[s] = n.hooks[s] || t;
5
+ return [
6
+ n.hooks[s],
7
+ (t) => {
8
+ (n.hooks[s] = e(n.hooks[s], t)), o.scheduleUpdate();
9
+ },
10
+ ];
11
+ }
@@ -0,0 +1,5 @@
1
+ export default function o(o, t) {
2
+ const n = o.getCurrentInstance(),
3
+ r = n.currentHook++;
4
+ return n.hooks[r] || (n.hooks[r] = { current: t }), n.hooks[r];
5
+ }
@@ -0,0 +1,19 @@
1
+ export default function e(e, o) {
2
+ const t = e.getCurrentInstance(),
3
+ s = t.currentHook++;
4
+ return (
5
+ t.hooks[s] ||
6
+ (t.hooks[s] = {
7
+ value: o,
8
+ setValue: (o) => {
9
+ const n = "function" == typeof o ? o(t.hooks[s].value) : o;
10
+ n !== t.hooks[s].value &&
11
+ ((t.hooks[s].value = n),
12
+ (e.dirtyInstances = e.dirtyInstances || new Set()),
13
+ e.dirtyInstances.add(t),
14
+ e.scheduleUpdate());
15
+ },
16
+ }),
17
+ [t.hooks[s].value, t.hooks[s].setValue]
18
+ );
19
+ }
package/dist/olova.js CHANGED
@@ -1,291 +1,248 @@
1
- const memoize = (e) => {
2
- const t = new Map();
3
- return (...n) => {
4
- const r = JSON.stringify(n);
5
- return t.has(r) || t.set(r, e(...n)), t.get(r);
6
- };
7
- };
8
- let currentObserver = null;
9
- function diffProps(e = {}, t = {}, n) {
10
- void 0 !== t.className && ((t.class = t.className), delete t.className),
11
- void 0 !== e.className && ((e.class = e.className), delete e.className);
12
- for (const [r, o] of Object.entries(e))
13
- r in t ||
14
- (r.startsWith("on")
15
- ? n.removeEventListener(r.slice(2).toLowerCase(), o)
16
- : "style" === r
17
- ? (n.style.cssText = "")
18
- : n.removeAttribute(r));
19
- for (const [r, o] of Object.entries(t))
20
- if (null != o && e[r] !== o)
21
- if (r.startsWith("on")) {
22
- const t = r.slice(2).toLowerCase();
23
- e[r] && n.removeEventListener(t, e[r]), n.addEventListener(t, o);
24
- } else if ("ref" === r)
25
- o && "object" == typeof o && "current" in o
26
- ? (o.current = n)
27
- : "function" == typeof o && o(n);
28
- else if ("style" === r) {
29
- const e = n.style;
30
- "object" == typeof o ? Object.assign(e, o) : (e.cssText = o);
31
- } else n.setAttribute(r, o);
32
- }
33
- const diffChildren = memoize((e, t, n) => {
34
- const r = Array.isArray(e) ? e.flat() : [e],
35
- o = Array.isArray(t) ? t.flat() : [t],
36
- s = Math.max(r.length, o.length);
37
- for (let e = 0; e < s; e++) {
38
- const t = r[e],
39
- s = o[e];
40
- if (!t || s)
41
- if (t || !s) {
42
- if (t instanceof Node && s instanceof Node)
43
- t.nodeType !== s.nodeType || t.nodeName !== s.nodeName
44
- ? n.replaceChild(s, t)
45
- : (diffProps(t.attributes, s.attributes, t),
46
- diffChildren(
47
- Array.from(t.childNodes),
48
- Array.from(s.childNodes),
49
- t
50
- ));
51
- else if (t !== s) {
52
- const t = n.childNodes[e];
53
- t && (t.textContent = String(s));
1
+ import e from "./hooks/state.js";
2
+ import t from "./hooks/effect.js";
3
+ import n from "./hooks/memo.js";
4
+ import s from "./hooks/callback.js";
5
+ import o from "./hooks/reducer.js";
6
+ import r from "./hooks/ref.js";
7
+ import i from "./hooks/context.js";
8
+ const c = new (class {
9
+ constructor() {
10
+ (this.rootElement = null),
11
+ (this.components = new Map()),
12
+ (this.componentStack = []),
13
+ (this.componentInstances = new Map()),
14
+ (this.pendingUpdates = []),
15
+ (this.pendingEffects = []),
16
+ (this.contextSubscriptions = new WeakMap()),
17
+ (this.isBatchingUpdates = !1),
18
+ (this.hasScheduledFlush = !1),
19
+ (this.dirtyInstances = null);
20
+ }
21
+ createElement(e, t, ...n) {
22
+ return null == e
23
+ ? (console.error("Element type cannot be null or undefined"), null)
24
+ : "function" == typeof e || "string" == typeof e
25
+ ? { type: e, props: { ...t, children: n.flat() } }
26
+ : (console.error("Invalid element type:", e), null);
27
+ }
28
+ render(e, t) {
29
+ try {
30
+ if (null == e) return;
31
+ if ("string" == typeof e || "number" == typeof e)
32
+ return void t.appendChild(document.createTextNode(e));
33
+ if (Array.isArray(e)) return void e.forEach((e) => this.render(e, t));
34
+ if ("function" == typeof e.type) {
35
+ const n = e.type,
36
+ s = this.getComponentInstance(n);
37
+ if (n.__isMemoized) {
38
+ const n = s.lastProps;
39
+ if (n && this.deepEqual(n, e.props) && s.lastResult)
40
+ return void this.render(s.lastResult, t);
54
41
  }
55
- } else {
56
- const e = s instanceof Node ? s : document.createTextNode(String(s));
57
- n.appendChild(e);
42
+ this.componentStack.push(s),
43
+ (s.currentHook = 0),
44
+ (s.lastProps = e.props);
45
+ const o = n(e.props);
46
+ return (
47
+ (s.lastResult = o), this.componentStack.pop(), void this.render(o, t)
48
+ );
58
49
  }
59
- else t instanceof Node && n.removeChild(t);
50
+ if ("string" != typeof e.type)
51
+ return void console.error("Invalid element type:", e.type);
52
+ const n = document.createElement(e.type);
53
+ this.applyProps(n, e.props),
54
+ (e.props.children || []).forEach((e) => this.render(e, n)),
55
+ t.appendChild(n);
56
+ } catch (e) {
57
+ console.error("Render error:", e);
58
+ }
60
59
  }
61
- });
62
- class Signal {
63
- constructor(e) {
64
- (this._value = e),
65
- (this.observers = new Map()),
66
- (this.pending = new Set()),
67
- (this.isBatching = !1);
60
+ applyProps(e, t) {
61
+ Object.keys(t || {}).forEach((n) => {
62
+ if ("ref" === n && t[n]) t[n].current = e;
63
+ else if (n.startsWith("on")) {
64
+ const s = n.toLowerCase().substring(2);
65
+ e.addEventListener(s, t[n]);
66
+ } else
67
+ "children" !== n &&
68
+ ("className" === n ? e.setAttribute("class", t[n]) : (e[n] = t[n]));
69
+ });
68
70
  }
69
- get value() {
71
+ getComponentInstance(e) {
70
72
  return (
71
- currentObserver &&
72
- (this.observers.has("_root") || this.observers.set("_root", new Set()),
73
- this.observers.get("_root").add(currentObserver)),
74
- this._value
73
+ this.componentInstances.has(e) ||
74
+ this.componentInstances.set(e, {
75
+ hooks: [],
76
+ currentHook: 0,
77
+ effects: [],
78
+ cleanups: new Map(),
79
+ pendingEffects: [],
80
+ contextSubscriptions: new Set(),
81
+ lastProps: null,
82
+ lastResult: null,
83
+ }),
84
+ this.componentInstances.get(e)
75
85
  );
76
86
  }
77
- set value(e) {
78
- if (this._value === e) return;
79
- const t = this._value;
80
- if (((this._value = e), this.observers.has("_root")))
81
- for (const e of this.observers.get("_root")) this.pending.add(e);
82
- if ("object" == typeof t && "object" == typeof e) {
83
- const n = new Set([...Object.keys(t), ...Object.keys(e)]);
84
- for (const r of n)
85
- if (t[r] !== e[r] && this.observers.has(r))
86
- for (const e of this.observers.get(r)) this.pending.add(e);
87
- }
88
- this.batchUpdate();
89
- }
90
- batchUpdate() {
91
- this.isBatching ||
92
- ((this.isBatching = !0),
93
- Promise.resolve().then(() => {
94
- this.pending.forEach((e) => e()),
95
- this.pending.clear(),
96
- (this.isBatching = !1);
97
- }));
87
+ renderComponent(e, t) {
88
+ const n = this.getComponentInstance(e);
89
+ this.componentStack.push(n), (n.currentHook = 0);
90
+ if (!n.lastResult || this.shouldComponentUpdate(n, e)) {
91
+ const s = e();
92
+ (n.lastResult = s),
93
+ this.render(s, t),
94
+ n.pendingEffects.length > 0 &&
95
+ (this.pendingEffects.push(...n.pendingEffects),
96
+ (n.pendingEffects = []));
97
+ } else this.render(n.lastResult, t);
98
+ this.componentStack.pop();
98
99
  }
99
- observe(e, t) {
100
- this.observers.has(e) || this.observers.set(e, new Set()),
101
- this.observers.get(e).add(t);
100
+ shouldComponentUpdate(e, t) {
101
+ return (
102
+ !t.__isMemoized || !e.lastProps || !this.deepEqual(e.lastProps, t.props)
103
+ );
102
104
  }
103
- unobserve(e, t) {
104
- this.observers.has(e) && this.observers.get(e).delete(t);
105
+ getCurrentInstance() {
106
+ return this.componentStack[this.componentStack.length - 1];
105
107
  }
106
- }
107
- function $signal(e) {
108
- const t = new Signal(e),
109
- n = () => t.value;
110
- return (
111
- (n.toString = () => t.value),
112
- (n.observe = (e, n) => t.observe(e, n)),
113
- (n.unobserve = (e, n) => t.unobserve(e, n)),
114
- [
115
- n,
116
- (e) => {
117
- t.value = "function" == typeof e ? e(t.value) : e;
108
+ createContext(e) {
109
+ const t = {
110
+ _currentValue: e,
111
+ _defaultValue: e,
112
+ _subscribers: new Set(),
113
+ _version: 0,
114
+ Provider: ({ value: e, children: n }) => {
115
+ const s = this.getCurrentInstance(),
116
+ o = s.currentHook++,
117
+ r = s.hooks[o];
118
+ return (
119
+ this.shallowEqual(r, e) ||
120
+ ((t._currentValue = e),
121
+ t._version++,
122
+ t._subscribers.forEach((t) => {
123
+ (t.instance.hooks[t.hookIndex] = e), this.scheduleUpdate();
124
+ })),
125
+ (s.hooks[o] = e),
126
+ n
127
+ );
118
128
  },
119
- ]
120
- );
121
- }
122
- function $effect(e, t) {
123
- let n,
124
- r = !0,
125
- o = null;
126
- const s = () => {
127
- "function" == typeof n && (n(), (n = void 0)), (currentObserver = o);
128
- const t = e();
129
- (currentObserver = null), "function" == typeof t && (n = t);
130
- };
131
- return (
132
- (o = () => {
133
- if (r || !t) return s(), void (r = !1);
134
- Array.isArray(t) && 0 === t.length ? r && (s(), (r = !1)) : s();
135
- }),
136
- o(),
137
- () => {
138
- n && n();
129
+ Consumer: ({ children: e }) => {
130
+ if ("function" != typeof e)
131
+ throw new Error("Context.Consumer expects a function as a child");
132
+ return e(t._currentValue);
133
+ },
134
+ };
135
+ return t;
136
+ }
137
+ scheduleUpdate() {
138
+ this.isBatchingUpdates ||
139
+ ((this.isBatchingUpdates = !0),
140
+ this.pendingUpdates.push(() => {
141
+ if (this.rootElement) {
142
+ const e = this.dirtyInstances || new Set();
143
+ for (
144
+ this.components.forEach((t, n) => {
145
+ const s = this.getComponentInstance(t);
146
+ e.has(s) && ((n.innerHTML = ""), this.renderComponent(t, n));
147
+ }),
148
+ this.dirtyInstances = new Set();
149
+ this.pendingEffects.length > 0;
150
+
151
+ ) {
152
+ this.pendingEffects.shift()();
153
+ }
154
+ }
155
+ }),
156
+ this.hasScheduledFlush ||
157
+ ((this.hasScheduledFlush = !0),
158
+ queueMicrotask(() => {
159
+ this.flushUpdates(), (this.isBatchingUpdates = !1);
160
+ })));
161
+ }
162
+ flushUpdates() {
163
+ for (; this.pendingUpdates.length > 0; ) {
164
+ this.pendingUpdates.shift()();
165
+ }
166
+ this.hasScheduledFlush = !1;
167
+ }
168
+ mount(e, t) {
169
+ for (
170
+ this.rootElement = t,
171
+ this.components.set(t, e),
172
+ this.renderComponent(e, t);
173
+ this.pendingEffects.length > 0;
174
+
175
+ ) {
176
+ this.pendingEffects.shift()();
139
177
  }
140
- );
141
- }
142
- function $memo(e) {
143
- const [t, n] = $signal(e());
144
- return $effect(() => n(e())), t;
145
- }
146
- function $ref(e) {
147
- const [t, n] = $signal({
148
- current: e,
149
- toString() {
150
- return this.current;
151
- },
152
- valueOf() {
153
- return this.current;
154
- },
155
- });
156
- return {
157
- get current() {
158
- return t().current;
159
- },
160
- set current(e) {
161
- n((t) => (t.current === e ? t : { ...t, current: e }));
162
- },
163
- toString() {
164
- return this.current.toString();
165
- },
166
- valueOf() {
167
- return this.current;
168
- },
169
- };
170
- }
171
- function h(e, t, ...n) {
172
- if (e === Fragment || Array.isArray(e)) {
173
- const r = document.createDocumentFragment(),
174
- o = e === Fragment ? t?.children || n : e;
178
+ }
179
+ unmount(e) {
180
+ const t = this.components.get(e);
181
+ if (t) {
182
+ const n = this.componentInstances.get(t);
183
+ n &&
184
+ (n.contextSubscriptions.forEach((e) => e()),
185
+ n.contextSubscriptions.clear(),
186
+ n.cleanups.forEach((e) => e()),
187
+ n.cleanups.clear(),
188
+ this.componentInstances.delete(t)),
189
+ this.components.delete(e);
190
+ }
191
+ e.innerHTML = "";
192
+ }
193
+ memo(e) {
194
+ const t = (t) => {
195
+ const n = this.getCurrentInstance();
196
+ if (!n) return e(t);
197
+ const s = n.currentHook++,
198
+ o = n.hooks[s] || { props: null, result: null },
199
+ r = !o.props || !this.deepEqual(t, o.props);
200
+ if (!o.result || r) {
201
+ const o = e(t);
202
+ return (n.hooks[s] = { props: t, result: o }), o;
203
+ }
204
+ return o.result;
205
+ };
206
+ return (t.__isMemoized = !0), (t.__original = e), t;
207
+ }
208
+ shallowEqual(e, t) {
209
+ if (e === t) return !0;
210
+ if (!e || !t) return !1;
211
+ if ("object" != typeof e || "object" != typeof t) return e === t;
212
+ const n = Object.keys(e),
213
+ s = Object.keys(t);
175
214
  return (
176
- Array.isArray(o) &&
177
- o.flat().forEach((e) => {
178
- null != e &&
179
- r.appendChild(
180
- e instanceof Node ? e : document.createTextNode(String(e))
181
- );
182
- }),
183
- r
215
+ n.length === s.length &&
216
+ n.every((n) => t.hasOwnProperty(n) && e[n] === t[n])
184
217
  );
185
218
  }
186
- if (!e) return null;
187
- const r = "function" == typeof e ? e(t) : document.createElement(e);
188
- if (("string" == typeof e && r && t && diffProps({}, t, r), n.length)) {
189
- const e = document.createDocumentFragment(),
190
- t = (n) => {
191
- if (null != n)
192
- if ("function" == typeof n) {
193
- const t = document.createTextNode("");
194
- $effect(() => {
195
- const e = n();
196
- if (Array.isArray(e)) {
197
- const n = document.createDocumentFragment();
198
- e.forEach((e) => {
199
- n.appendChild(
200
- e instanceof Node ? e : document.createTextNode(String(e))
201
- );
202
- }),
203
- t.parentNode && t.parentNode.replaceChild(n, t);
204
- } else t.textContent = String(e);
205
- }),
206
- e.appendChild(t);
207
- } else
208
- n instanceof Node
209
- ? e.appendChild(n)
210
- : Array.isArray(n)
211
- ? n.flat().forEach(t)
212
- : e.appendChild(document.createTextNode(String(n)));
213
- };
214
- n.forEach(t), r.appendChild(e);
215
- }
216
- return r;
217
- }
218
- const Component = (e, t) => {
219
- if ("function" != typeof e)
220
- throw new Error("Invalid Component: must be a function");
221
- return e(t);
222
- },
223
- Fragment = (e) => e.children;
224
- window.Fragment = Fragment;
225
- const Olova = {
226
- render(e, t) {
227
- const n = "function" == typeof e ? e() : e;
219
+ deepEqual(e, t) {
220
+ if (e === t) return !0;
221
+ if (!e || !t) return !1;
222
+ if (typeof e != typeof t) return !1;
223
+ if ("object" != typeof e) return e === t;
224
+ if (Array.isArray(e))
228
225
  return (
229
- t.firstChild ? diffChildren([t.firstChild], [n], t) : t.appendChild(n),
230
- n
226
+ !(!Array.isArray(t) || e.length !== t.length) &&
227
+ e.every((e, n) => this.deepEqual(e, t[n]))
231
228
  );
232
- },
233
- mount(e, t) {
234
- return this.render(e, t);
235
- },
236
- unmount(e) {
237
- e.innerHTML = "";
238
- },
239
- Fragment: Fragment,
240
- },
241
- contextRegistry = new Map();
242
- function $context(e) {
243
- const t = Symbol("context");
244
- return (
245
- contextRegistry.set(t, e),
246
- {
247
- Provider({ value: e, children: n }) {
248
- const r = contextRegistry.get(t);
249
- contextRegistry.set(t, e);
250
- const o = n;
251
- return contextRegistry.set(t, r), o;
252
- },
253
- use() {
254
- const n = contextRegistry.get(t);
255
- if (void 0 === n && void 0 === e)
256
- throw new Error("Context used outside of Provider");
257
- return n ?? e;
258
- },
259
- }
260
- );
261
- }
262
- function $callback(e, t) {
263
- const [n, r] = $signal(() => ({
264
- fn: e,
265
- deps: t,
266
- memoized: (...t) => e(...t),
267
- }));
268
- return (
269
- $effect(() => {
270
- const o = n();
271
- t &&
272
- ((o.deps &&
273
- t.length === o.deps.length &&
274
- !t.some((e, t) => e !== o.deps[t])) ||
275
- r({ fn: e, deps: t, memoized: (...t) => e(...t) }));
276
- }),
277
- () => n().memoized
278
- );
279
- }
280
- export {
281
- $signal,
282
- $effect,
283
- $memo,
284
- $ref,
285
- $context,
286
- $callback,
287
- Component,
288
- h,
289
- Fragment,
290
- };
291
- export default Olova;
229
+ const n = Object.keys(e),
230
+ s = Object.keys(t);
231
+ return (
232
+ n.length === s.length &&
233
+ n.every((n) => t.hasOwnProperty(n) && this.deepEqual(e[n], t[n]))
234
+ );
235
+ }
236
+ })();
237
+ export const h = c.createElement.bind(c);
238
+ export const Fragment = (e) => (e ? e.children : null);
239
+ export const $state = (t) => e(c, t);
240
+ export const $effect = (e, n) => t(c, e, n);
241
+ export const $memo = (e, t) => n(c, e, t);
242
+ export const $callback = (e, t) => s(c, e, t);
243
+ export const $reducer = (e, t) => o(c, e, t);
244
+ export const $ref = (e) => r(c, e);
245
+ export const $context = (e) => i(c, e);
246
+ export const createContext = c.createContext.bind(c);
247
+ export const memo = c.memo.bind(c);
248
+ export default c;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "olova",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "A lightweight JavaScript framework for building reactive applications.",
5
5
  "main": "dist/olova.js",
6
6
  "keywords": [