@unsetsoft/ryunixjs 1.0.2 → 1.0.3-nightly.3
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/dist/Ryunix.js +308 -377
- package/package.json +2 -2
- package/src/lib/commits.js +73 -29
- package/src/lib/components.js +16 -21
- package/src/lib/createElement.js +25 -28
- package/src/lib/dom.js +61 -38
- package/src/lib/effects.js +35 -10
- package/src/lib/hooks.js +36 -157
- package/src/lib/index.js +2 -19
- package/src/lib/reconciler.js +3 -3
- package/src/lib/render.js +22 -16
- package/src/lib/workers.js +16 -28
- package/src/main.js +0 -2
- package/src/utils/index.js +17 -8
- package/dist/cj/Ryunix.js +0 -702
package/dist/Ryunix.js
CHANGED
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
nextUnitOfWork: undefined,
|
|
10
10
|
currentRoot: undefined,
|
|
11
11
|
wipRoot: undefined,
|
|
12
|
-
deletions:
|
|
13
|
-
|
|
14
|
-
hookIndex:
|
|
12
|
+
deletions: [],
|
|
13
|
+
workInProgressFiber: undefined,
|
|
14
|
+
hookIndex: 0,
|
|
15
|
+
errorBoundary: null, // New property to hold error boundary references
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
const capitalLetterRegex = /[A-Z]/g;
|
|
18
19
|
|
|
19
20
|
const RYUNIX_TYPES = Object.freeze({
|
|
20
21
|
TEXT_ELEMENT: Symbol('text.element'),
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
RYUNIX_MEMO: Symbol('ryunix.memo'),
|
|
23
24
|
RYUNIX_URL_QUERY: Symbol('ryunix.urlQuery'),
|
|
24
25
|
RYUNIX_REF: Symbol('ryunix.ref'),
|
|
26
|
+
RYUNIX_ERROR_BOUNDARY: Symbol('ryunix.errorBoundary'), // New Error Boundary Type
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
const STRINGS = Object.freeze({
|
|
@@ -39,21 +41,24 @@
|
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
const EFFECT_TAGS = Object.freeze({
|
|
42
|
-
PLACEMENT: Symbol(),
|
|
43
|
-
UPDATE: Symbol(),
|
|
44
|
-
DELETION: Symbol(),
|
|
44
|
+
PLACEMENT: Symbol('placement'),
|
|
45
|
+
UPDATE: Symbol('update'),
|
|
46
|
+
DELETION: Symbol('deletion'),
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
const Fragment = (props) => {
|
|
48
50
|
return props.children
|
|
49
51
|
};
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The function takes a set of child elements and flattens them into an array to ensure all children are handled properly.
|
|
55
|
+
* @param children - The child elements, which can be of different types (array, object, primitive).
|
|
56
|
+
* @param out - The array where the final children will be stored.
|
|
57
|
+
* @returns An array with all the child elements.
|
|
58
|
+
*/
|
|
59
|
+
const childArray = (children, out = []) => {
|
|
60
|
+
if (children == undefined || typeof children === STRINGS.boolean) ; else if (Array.isArray(children)) {
|
|
61
|
+
children.forEach((child) => childArray(child, out)); // Use forEach instead of some
|
|
57
62
|
} else {
|
|
58
63
|
out.push(children);
|
|
59
64
|
}
|
|
@@ -61,22 +66,12 @@
|
|
|
61
66
|
};
|
|
62
67
|
|
|
63
68
|
/**
|
|
64
|
-
* The function creates a new element with
|
|
65
|
-
* @param type - The type of
|
|
66
|
-
* @param props - The
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* using the spread
|
|
70
|
-
* @param children - The `children` parameter is a rest parameter that allows the function to accept
|
|
71
|
-
* any number of arguments after the `props` parameter. These arguments will be treated as children
|
|
72
|
-
* elements of the created element. The `map` function is used to iterate over each child and create a
|
|
73
|
-
* new element if it is not
|
|
74
|
-
* @returns A JavaScript object with a `type` property and a `props` property. The `type` property is
|
|
75
|
-
* set to the `type` argument passed into the function, and the `props` property is an object that
|
|
76
|
-
* includes any additional properties passed in the `props` argument, as well as a `children` property
|
|
77
|
-
* that is an array of any child elements passed in the `...children` argument
|
|
69
|
+
* The function creates a new element with a given type, properties, and children.
|
|
70
|
+
* @param type - The type of element to create (div, span, etc.).
|
|
71
|
+
* @param props - The properties of the element.
|
|
72
|
+
* @param children - The child elements of the new element.
|
|
73
|
+
* @returns An object representing the created element.
|
|
78
74
|
*/
|
|
79
|
-
|
|
80
75
|
const createElement = (type, props, ...children) => {
|
|
81
76
|
children = childArray(children, []);
|
|
82
77
|
return {
|
|
@@ -91,254 +86,52 @@
|
|
|
91
86
|
};
|
|
92
87
|
|
|
93
88
|
/**
|
|
94
|
-
* The function creates a text element
|
|
95
|
-
* @param text - The text content
|
|
96
|
-
* @returns
|
|
97
|
-
* that contains a `nodeValue` property set to the `text` parameter and an empty `children` array.
|
|
89
|
+
* The function creates a text element from a given text value.
|
|
90
|
+
* @param text - The text content to create the element.
|
|
91
|
+
* @returns An object representing a text element.
|
|
98
92
|
*/
|
|
99
|
-
|
|
100
93
|
const createTextElement = (text) => {
|
|
101
94
|
return {
|
|
102
95
|
type: RYUNIX_TYPES.TEXT_ELEMENT,
|
|
103
96
|
props: {
|
|
104
97
|
nodeValue: text,
|
|
105
|
-
children: [],
|
|
98
|
+
children: [], // Text elements don't have children.
|
|
106
99
|
},
|
|
107
100
|
}
|
|
108
101
|
};
|
|
109
102
|
|
|
110
103
|
/**
|
|
111
|
-
*
|
|
112
|
-
* @param
|
|
113
|
-
*
|
|
114
|
-
* @param container - The container parameter is the DOM element where the rendered element will be
|
|
115
|
-
* appended to. this parameter is optional if you use createRoot().
|
|
116
|
-
*/
|
|
117
|
-
const render = (element, container) => {
|
|
118
|
-
vars.wipRoot = {
|
|
119
|
-
dom: vars.containerRoot || container,
|
|
120
|
-
props: {
|
|
121
|
-
children: [element],
|
|
122
|
-
},
|
|
123
|
-
alternate: vars.currentRoot,
|
|
124
|
-
};
|
|
125
|
-
vars.deletions = [];
|
|
126
|
-
vars.nextUnitOfWork = vars.wipRoot;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* @description The function creates a reference to a DOM element with the specified ID. This will be used to initialize the app.
|
|
131
|
-
* @example Ryunix.init("root") -> <div id="root" />
|
|
132
|
-
* @param root - The parameter "root" is the id of the HTML element that will serve as the container
|
|
133
|
-
* for the root element.
|
|
104
|
+
* Checks if a key is an event handler (i.e., starts with 'on').
|
|
105
|
+
* @param key - The key to check.
|
|
106
|
+
* @returns A boolean indicating if the key is an event.
|
|
134
107
|
*/
|
|
135
|
-
const
|
|
136
|
-
const rootElement = root || '__ryunix';
|
|
137
|
-
vars.containerRoot = document.getElementById(rootElement);
|
|
138
|
-
return undefined
|
|
139
|
-
};
|
|
108
|
+
const isEvent = (key) => key.startsWith('on');
|
|
140
109
|
|
|
141
110
|
/**
|
|
142
|
-
*
|
|
143
|
-
* @param
|
|
144
|
-
* @returns
|
|
145
|
-
* `setState` function that can be used to update the state.
|
|
111
|
+
* Checks if a key is a property (not 'children' and not an event).
|
|
112
|
+
* @param key - The key to check.
|
|
113
|
+
* @returns A boolean indicating if the key is a property.
|
|
146
114
|
*/
|
|
147
|
-
const
|
|
148
|
-
const oldHook =
|
|
149
|
-
vars.wipFiber.alternate &&
|
|
150
|
-
vars.wipFiber.alternate.hooks &&
|
|
151
|
-
vars.wipFiber.alternate.hooks[vars.hookIndex];
|
|
152
|
-
const hook = {
|
|
153
|
-
state: oldHook ? oldHook.state : initial,
|
|
154
|
-
queue: [],
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const actions = oldHook ? oldHook.queue : [];
|
|
158
|
-
actions.forEach((action) => {
|
|
159
|
-
hook.state =
|
|
160
|
-
typeof action === STRINGS.function ? action(hook.state) : action;
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* The function `setState` updates the state of a component in Ryunix by adding an action to a queue
|
|
165
|
-
* and setting up a new work-in-progress root.
|
|
166
|
-
* @param action - The `action` parameter is an object that represents a state update to be performed
|
|
167
|
-
* on a component. It contains information about the type of update to be performed and any new data
|
|
168
|
-
* that needs to be applied to the component's state.
|
|
169
|
-
*/
|
|
170
|
-
const setState = (action) => {
|
|
171
|
-
hook.queue.push(action);
|
|
172
|
-
vars.wipRoot = {
|
|
173
|
-
dom: vars.currentRoot.dom,
|
|
174
|
-
props: vars.currentRoot.props,
|
|
175
|
-
alternate: vars.currentRoot,
|
|
176
|
-
};
|
|
177
|
-
vars.nextUnitOfWork = vars.wipRoot;
|
|
178
|
-
vars.deletions = [];
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
if (vars.wipFiber && vars.wipFiber.hooks) {
|
|
182
|
-
vars.wipFiber.hooks.push(hook);
|
|
183
|
-
vars.hookIndex++;
|
|
184
|
-
}
|
|
185
|
-
return [hook.state, setState]
|
|
186
|
-
};
|
|
115
|
+
const isProperty = (key) => key !== STRINGS.children && !isEvent(key);
|
|
187
116
|
|
|
188
117
|
/**
|
|
189
|
-
*
|
|
190
|
-
* @param
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* @param deps - An array of dependencies that the effect depends on. If any of the dependencies change
|
|
194
|
-
* between renders, the effect will be re-run. If the array is empty, the effect will only run once on
|
|
195
|
-
* mount and never again.
|
|
118
|
+
* Checks if a property has changed between the previous and next props.
|
|
119
|
+
* @param prev - The previous props object.
|
|
120
|
+
* @param next - The next props object.
|
|
121
|
+
* @returns A function that takes a key and returns true if the property has changed.
|
|
196
122
|
*/
|
|
197
|
-
|
|
198
|
-
const useEffect = (callback, deps) => {
|
|
199
|
-
const oldHook =
|
|
200
|
-
vars.wipFiber.alternate &&
|
|
201
|
-
vars.wipFiber.alternate.hooks &&
|
|
202
|
-
vars.wipFiber.alternate.hooks[vars.hookIndex];
|
|
203
|
-
|
|
204
|
-
const hook = {
|
|
205
|
-
type: RYUNIX_TYPES.RYUNIX_EFFECT,
|
|
206
|
-
deps,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
if (!oldHook) {
|
|
210
|
-
// invoke callback if this is the first time
|
|
211
|
-
callback();
|
|
212
|
-
} else {
|
|
213
|
-
if (!lodash.isEqual(oldHook.deps, hook.deps)) {
|
|
214
|
-
callback();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (vars.wipFiber.hooks) {
|
|
219
|
-
vars.wipFiber.hooks.push(hook);
|
|
220
|
-
vars.hookIndex++;
|
|
221
|
-
}
|
|
222
|
-
};
|
|
123
|
+
const isNew = (prev, next) => (key) => prev[key] !== next[key];
|
|
223
124
|
|
|
224
125
|
/**
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* @returns
|
|
126
|
+
* Checks if a property is no longer present in the next props.
|
|
127
|
+
* @param next - The next props object.
|
|
128
|
+
* @returns A function that takes a key and returns true if the property is not present in the next props.
|
|
228
129
|
*/
|
|
229
|
-
const useQuery = () => {
|
|
230
|
-
const oldHook =
|
|
231
|
-
vars.wipFiber.alternate &&
|
|
232
|
-
vars.wipFiber.alternate.hooks &&
|
|
233
|
-
vars.wipFiber.alternate.hooks[vars.hookIndex];
|
|
234
|
-
|
|
235
|
-
const hasOld = oldHook ? oldHook : undefined;
|
|
236
|
-
|
|
237
|
-
const urlSearchParams = new URLSearchParams(window.location.search);
|
|
238
|
-
const params = Object.fromEntries(urlSearchParams.entries());
|
|
239
|
-
const Query = hasOld ? hasOld : params;
|
|
240
|
-
|
|
241
|
-
const hook = {
|
|
242
|
-
type: RYUNIX_TYPES.RYUNIX_URL_QUERY,
|
|
243
|
-
query: Query,
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
if (vars.wipFiber.hooks) {
|
|
247
|
-
vars.wipFiber.hooks.push(hook);
|
|
248
|
-
vars.hookIndex++;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return hook.query
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const useRef = (initial) => {
|
|
255
|
-
const oldHook =
|
|
256
|
-
vars.wipFiber.alternate &&
|
|
257
|
-
vars.wipFiber.alternate.hooks &&
|
|
258
|
-
vars.wipFiber.alternate.hooks[vars.hookIndex];
|
|
259
|
-
|
|
260
|
-
const hook = {
|
|
261
|
-
type: RYUNIX_TYPES.RYUNIX_REF,
|
|
262
|
-
value: oldHook ? oldHook.value : { current: initial },
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
if (vars.wipFiber.hooks) {
|
|
266
|
-
vars.wipFiber.hooks.push(hook);
|
|
267
|
-
vars.hookIndex++;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return hook.value
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const useMemo = (comp, deps) => {
|
|
274
|
-
const oldHook =
|
|
275
|
-
vars.wipFiber.alternate &&
|
|
276
|
-
vars.wipFiber.alternate.hooks &&
|
|
277
|
-
vars.wipFiber.alternate.hooks[vars.hookIndex];
|
|
278
|
-
|
|
279
|
-
const hook = {
|
|
280
|
-
type: RYUNIX_TYPES.RYUNIX_MEMO,
|
|
281
|
-
value: null,
|
|
282
|
-
deps,
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
if (oldHook) {
|
|
286
|
-
if (lodash.isEqual(oldHook.deps, hook.deps)) {
|
|
287
|
-
hook.value = oldHook.value;
|
|
288
|
-
} else {
|
|
289
|
-
hook.value = comp();
|
|
290
|
-
}
|
|
291
|
-
} else {
|
|
292
|
-
hook.value = comp();
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (vars.wipFiber.hooks) {
|
|
296
|
-
vars.wipFiber.hooks.push(hook);
|
|
297
|
-
vars.hookIndex++;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return hook.value
|
|
301
|
-
};
|
|
302
|
-
const useCallback = (callback, deps) => {
|
|
303
|
-
return useMemo(() => callback, deps)
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
const useRouter = (routes) => {
|
|
307
|
-
const [location, setLocation] = useStore(window.location.pathname);
|
|
308
|
-
|
|
309
|
-
const navigate = (path) => {
|
|
310
|
-
window.history.pushState({}, '', path);
|
|
311
|
-
setLocation(path);
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
useEffect(() => {
|
|
315
|
-
const onPopState = () => {
|
|
316
|
-
setLocation(window.location.pathname);
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
window.addEventListener('popstate', onPopState);
|
|
320
|
-
return () => {
|
|
321
|
-
window.removeEventListener('popstate', onPopState);
|
|
322
|
-
}
|
|
323
|
-
}, []);
|
|
324
|
-
|
|
325
|
-
const currentRoute = routes.find((route) => route.path === location);
|
|
326
|
-
const Children = () => (currentRoute ? currentRoute.component : null);
|
|
327
|
-
|
|
328
|
-
return { Children, navigate }
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const isEvent = (key) => key.startsWith('on');
|
|
332
|
-
const isProperty = (key) => key !== STRINGS.children && !isEvent(key);
|
|
333
|
-
const isNew = (prev, next) => (key) => prev[key] !== next[key];
|
|
334
130
|
const isGone = (next) => (key) => !(key in next);
|
|
335
131
|
|
|
336
132
|
/**
|
|
337
|
-
*
|
|
338
|
-
* @param fiber - The
|
|
339
|
-
* represent a component and its state. It contains information about the component's props, state, and
|
|
340
|
-
* children, as well as metadata used by React to manage updates and rendering. The function
|
|
341
|
-
* "cancelEffects" is likely intended
|
|
133
|
+
* Cancels all effect hooks in a given fiber.
|
|
134
|
+
* @param fiber - The fiber object containing hooks.
|
|
342
135
|
*/
|
|
343
136
|
const cancelEffects = (fiber) => {
|
|
344
137
|
if (fiber.hooks) {
|
|
@@ -351,11 +144,8 @@
|
|
|
351
144
|
};
|
|
352
145
|
|
|
353
146
|
/**
|
|
354
|
-
*
|
|
355
|
-
* @param fiber - The
|
|
356
|
-
* implementation of a fiber-based reconciliation algorithm, such as the one used in React. A fiber
|
|
357
|
-
* represents a unit of work that needs to be performed by the reconciliation algorithm, and it
|
|
358
|
-
* contains information about a component and its children, as
|
|
147
|
+
* Runs all effect hooks in a given fiber.
|
|
148
|
+
* @param fiber - The fiber object containing hooks.
|
|
359
149
|
*/
|
|
360
150
|
const runEffects = (fiber) => {
|
|
361
151
|
if (fiber.hooks) {
|
|
@@ -368,34 +158,34 @@
|
|
|
368
158
|
};
|
|
369
159
|
|
|
370
160
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
* `document.createTextNode("")`. Otherwise, a new element is created using
|
|
377
|
-
* `document.createElement(fiber.type)`. The function then calls the `updateDom` function to update the
|
|
378
|
-
* properties of the newly created
|
|
161
|
+
* Creates a new DOM element based on the given fiber object and updates its properties.
|
|
162
|
+
*
|
|
163
|
+
* @param fiber - The fiber object represents a node in the fiber tree and contains
|
|
164
|
+
* information about the element type, props, and children.
|
|
165
|
+
* @returns A newly created DOM element based on the fiber object.
|
|
379
166
|
*/
|
|
380
167
|
const createDom = (fiber) => {
|
|
381
168
|
const dom =
|
|
382
|
-
fiber.type
|
|
169
|
+
fiber.type === RYUNIX_TYPES.TEXT_ELEMENT
|
|
383
170
|
? document.createTextNode('')
|
|
384
171
|
: document.createElement(fiber.type);
|
|
385
172
|
|
|
173
|
+
// Update the newly created DOM element with initial props
|
|
386
174
|
updateDom(dom, {}, fiber.props);
|
|
387
175
|
|
|
388
176
|
return dom
|
|
389
177
|
};
|
|
390
178
|
|
|
391
179
|
/**
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* @param
|
|
396
|
-
* @param
|
|
180
|
+
* Updates a DOM element by removing old properties and event listeners,
|
|
181
|
+
* and applying new properties and event listeners from the nextProps.
|
|
182
|
+
*
|
|
183
|
+
* @param dom - The DOM element that needs to be updated.
|
|
184
|
+
* @param prevProps - The previous properties of the DOM element.
|
|
185
|
+
* @param nextProps - The new properties that should be applied to the DOM element.
|
|
397
186
|
*/
|
|
398
187
|
const updateDom = (dom, prevProps, nextProps) => {
|
|
188
|
+
// Remove old or changed event listeners
|
|
399
189
|
Object.keys(prevProps)
|
|
400
190
|
.filter(isEvent)
|
|
401
191
|
.filter((key) => isGone(nextProps)(key) || isNew(prevProps, nextProps)(key))
|
|
@@ -404,6 +194,7 @@
|
|
|
404
194
|
dom.removeEventListener(eventType, prevProps[name]);
|
|
405
195
|
});
|
|
406
196
|
|
|
197
|
+
// Remove old properties
|
|
407
198
|
Object.keys(prevProps)
|
|
408
199
|
.filter(isProperty)
|
|
409
200
|
.filter(isGone(nextProps))
|
|
@@ -411,35 +202,21 @@
|
|
|
411
202
|
dom[name] = '';
|
|
412
203
|
});
|
|
413
204
|
|
|
205
|
+
// Set new or changed properties
|
|
414
206
|
Object.keys(nextProps)
|
|
415
207
|
.filter(isProperty)
|
|
416
208
|
.filter(isNew(prevProps, nextProps))
|
|
417
209
|
.forEach((name) => {
|
|
418
|
-
if (name === STRINGS.style) {
|
|
419
|
-
DomStyle(dom, nextProps[
|
|
420
|
-
} else if (name === OLD_STRINGS.
|
|
421
|
-
|
|
422
|
-
} else if (name === STRINGS.className) {
|
|
423
|
-
if (nextProps['ryunix-class'] === '') {
|
|
424
|
-
throw new Error('data-class cannot be empty.')
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
prevProps['ryunix-class'] &&
|
|
428
|
-
dom.classList.remove(...prevProps['ryunix-class'].split(/\s+/));
|
|
429
|
-
dom.classList.add(...nextProps['ryunix-class'].split(/\s+/));
|
|
430
|
-
} else if (name === OLD_STRINGS.className) {
|
|
431
|
-
if (nextProps.className === '') {
|
|
432
|
-
throw new Error('className cannot be empty.')
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
prevProps.className &&
|
|
436
|
-
dom.classList.remove(...prevProps.className.split(/\s+/));
|
|
437
|
-
dom.classList.add(...nextProps.className.split(/\s+/));
|
|
210
|
+
if (name === STRINGS.style || name === OLD_STRINGS.style) {
|
|
211
|
+
DomStyle(dom, nextProps[STRINGS.style] || nextProps[OLD_STRINGS.style]);
|
|
212
|
+
} else if (name === STRINGS.className || name === OLD_STRINGS.className) {
|
|
213
|
+
handleClassName(dom, prevProps, nextProps, name);
|
|
438
214
|
} else {
|
|
439
215
|
dom[name] = nextProps[name];
|
|
440
216
|
}
|
|
441
217
|
});
|
|
442
218
|
|
|
219
|
+
// Add new event listeners
|
|
443
220
|
Object.keys(nextProps)
|
|
444
221
|
.filter(isEvent)
|
|
445
222
|
.filter(isNew(prevProps, nextProps))
|
|
@@ -449,16 +226,47 @@
|
|
|
449
226
|
});
|
|
450
227
|
};
|
|
451
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Updates the DOM element's style by applying the provided styles.
|
|
231
|
+
*
|
|
232
|
+
* @param dom - The DOM element that needs to be styled.
|
|
233
|
+
* @param style - An object containing the styles to be applied.
|
|
234
|
+
*/
|
|
452
235
|
const DomStyle = (dom, style) => {
|
|
453
236
|
dom.style = Object.keys(style).reduce((acc, styleName) => {
|
|
454
|
-
const key = styleName.replace(
|
|
455
|
-
|
|
456
|
-
|
|
237
|
+
const key = styleName.replace(
|
|
238
|
+
capitalLetterRegex,
|
|
239
|
+
(v) => `-${v.toLowerCase()}`,
|
|
240
|
+
);
|
|
457
241
|
acc += `${key}: ${style[styleName]};`;
|
|
458
242
|
return acc
|
|
459
243
|
}, '');
|
|
460
244
|
};
|
|
461
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Handles updating the className or ryunix-class properties, ensuring
|
|
248
|
+
* that the old class names are removed and the new ones are applied.
|
|
249
|
+
*
|
|
250
|
+
* @param dom - The DOM element to be updated.
|
|
251
|
+
* @param prevProps - The previous properties, including className.
|
|
252
|
+
* @param nextProps - The new properties, including className.
|
|
253
|
+
* @param name - The name of the class property (className or ryunix-class).
|
|
254
|
+
*/
|
|
255
|
+
const handleClassName = (dom, prevProps, nextProps, name) => {
|
|
256
|
+
const classProp = name === STRINGS.className ? 'ryunix-class' : 'className';
|
|
257
|
+
|
|
258
|
+
if (nextProps[classProp] === '') {
|
|
259
|
+
throw new Error(`${classProp} cannot be empty.`)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Remove old class names if they exist
|
|
263
|
+
prevProps[classProp] &&
|
|
264
|
+
dom.classList.remove(...prevProps[classProp].split(/\s+/));
|
|
265
|
+
|
|
266
|
+
// Add new class names
|
|
267
|
+
dom.classList.add(...nextProps[classProp].split(/\s+/));
|
|
268
|
+
};
|
|
269
|
+
|
|
462
270
|
var Dom = /*#__PURE__*/Object.freeze({
|
|
463
271
|
__proto__: null,
|
|
464
272
|
DomStyle: DomStyle,
|
|
@@ -479,57 +287,87 @@
|
|
|
479
287
|
};
|
|
480
288
|
|
|
481
289
|
/**
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* @returns The function does not return anything, it performs side effects by manipulating the DOM.
|
|
290
|
+
* Helper function to find the DOM parent of a given fiber.
|
|
291
|
+
* It walks up the fiber tree until it finds a parent with a DOM node.
|
|
292
|
+
* @param fiber - The fiber node whose DOM parent needs to be found.
|
|
293
|
+
* @returns The DOM node of the fiber's parent.
|
|
487
294
|
*/
|
|
488
|
-
const
|
|
489
|
-
if (!fiber) {
|
|
490
|
-
return
|
|
491
|
-
}
|
|
492
|
-
|
|
295
|
+
const findDomParent = (fiber) => {
|
|
493
296
|
let domParentFiber = fiber.parent;
|
|
494
297
|
while (!domParentFiber.dom) {
|
|
495
298
|
domParentFiber = domParentFiber.parent;
|
|
496
299
|
}
|
|
497
|
-
|
|
300
|
+
return domParentFiber.dom
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Safely removes a child DOM node from its parent, with error handling.
|
|
305
|
+
* @param domParent - The parent DOM node.
|
|
306
|
+
* @param childDom - The child DOM node to be removed.
|
|
307
|
+
*/
|
|
308
|
+
const safeRemoveChild = (domParent, childDom) => {
|
|
309
|
+
try {
|
|
310
|
+
domParent.removeChild(childDom);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error('Error removing DOM child:', error);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The function handles the deletion of a fiber's corresponding DOM node.
|
|
318
|
+
* It removes the fiber's DOM element or recursively removes its child.
|
|
319
|
+
* @param fiber - The fiber node to be deleted.
|
|
320
|
+
* @param domParent - The parent DOM node from which the fiber's DOM node will be removed.
|
|
321
|
+
*/
|
|
322
|
+
const commitDeletion = (fiber, domParent) => {
|
|
323
|
+
if (fiber && fiber.dom) {
|
|
324
|
+
safeRemoveChild(domParent, fiber.dom);
|
|
325
|
+
} else if (fiber && fiber.child) {
|
|
326
|
+
commitDeletion(fiber.child, domParent);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
498
329
|
|
|
499
|
-
|
|
330
|
+
/**
|
|
331
|
+
* An object that maps effect tags to their corresponding handling functions.
|
|
332
|
+
* Each function takes a fiber node and a DOM parent, and applies the necessary changes to the DOM.
|
|
333
|
+
*/
|
|
334
|
+
const effectHandlers = {
|
|
335
|
+
[EFFECT_TAGS.PLACEMENT]: (fiber, domParent) => {
|
|
500
336
|
if (fiber.dom != undefined) {
|
|
501
337
|
domParent.appendChild(fiber.dom);
|
|
502
338
|
}
|
|
503
339
|
runEffects(fiber);
|
|
504
|
-
}
|
|
340
|
+
},
|
|
341
|
+
[EFFECT_TAGS.UPDATE]: (fiber, domParent) => {
|
|
505
342
|
cancelEffects(fiber);
|
|
506
343
|
if (fiber.dom != undefined) {
|
|
507
344
|
updateDom(fiber.dom, fiber.alternate.props, fiber.props);
|
|
508
345
|
}
|
|
509
346
|
runEffects(fiber);
|
|
510
|
-
}
|
|
347
|
+
},
|
|
348
|
+
[EFFECT_TAGS.DELETION]: (fiber, domParent) => {
|
|
511
349
|
cancelEffects(fiber);
|
|
512
350
|
commitDeletion(fiber, domParent);
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
commitWork(fiber.child);
|
|
517
|
-
commitWork(fiber.sibling);
|
|
351
|
+
},
|
|
518
352
|
};
|
|
519
353
|
|
|
520
354
|
/**
|
|
521
|
-
* The function
|
|
522
|
-
*
|
|
523
|
-
* @param fiber -
|
|
524
|
-
* application.
|
|
525
|
-
* @param domParent - The parent DOM element from which the fiber's DOM element needs to be removed.
|
|
355
|
+
* The function commits changes made to the DOM based on the effect tag of the fiber.
|
|
356
|
+
* It handles PLACEMENT, UPDATE, and DELETION of DOM nodes, and processes child and sibling fibers.
|
|
357
|
+
* @param fiber - The fiber node whose changes need to be committed to the DOM.
|
|
526
358
|
*/
|
|
527
|
-
const
|
|
528
|
-
if (fiber
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
359
|
+
const commitWork = (fiber) => {
|
|
360
|
+
if (!fiber) return
|
|
361
|
+
|
|
362
|
+
const domParent = findDomParent(fiber);
|
|
363
|
+
const effectHandler = effectHandlers[fiber.effectTag];
|
|
364
|
+
|
|
365
|
+
if (effectHandler) {
|
|
366
|
+
effectHandler(fiber, domParent);
|
|
532
367
|
}
|
|
368
|
+
|
|
369
|
+
commitWork(fiber.child);
|
|
370
|
+
commitWork(fiber.sibling);
|
|
533
371
|
};
|
|
534
372
|
|
|
535
373
|
var Commits = /*#__PURE__*/Object.freeze({
|
|
@@ -561,9 +399,9 @@
|
|
|
561
399
|
|
|
562
400
|
if (sameType) {
|
|
563
401
|
newFiber = {
|
|
564
|
-
type: oldFiber
|
|
402
|
+
type: oldFiber.type,
|
|
565
403
|
props: element.props,
|
|
566
|
-
dom: oldFiber
|
|
404
|
+
dom: oldFiber.dom,
|
|
567
405
|
parent: wipFiber,
|
|
568
406
|
alternate: oldFiber,
|
|
569
407
|
effectTag: EFFECT_TAGS.UPDATE,
|
|
@@ -579,7 +417,7 @@
|
|
|
579
417
|
effectTag: EFFECT_TAGS.PLACEMENT,
|
|
580
418
|
};
|
|
581
419
|
}
|
|
582
|
-
if (
|
|
420
|
+
if (!element && oldFiber) {
|
|
583
421
|
oldFiber.effectTag = EFFECT_TAGS.DELETION;
|
|
584
422
|
vars.deletions.push(oldFiber);
|
|
585
423
|
}
|
|
@@ -605,38 +443,33 @@
|
|
|
605
443
|
});
|
|
606
444
|
|
|
607
445
|
/**
|
|
608
|
-
*
|
|
609
|
-
* hook index,
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
446
|
+
* Updates a function component by setting up a work-in-progress fiber,
|
|
447
|
+
* resetting the hook index, initializing an empty hooks array, rendering the component,
|
|
448
|
+
* and reconciling its children.
|
|
449
|
+
*
|
|
450
|
+
* @param fiber - The fiber node representing the function component being updated. It contains
|
|
451
|
+
* properties such as the component type, props, and hooks.
|
|
613
452
|
*/
|
|
614
453
|
const updateFunctionComponent = (fiber) => {
|
|
615
|
-
|
|
454
|
+
// Set the current work-in-progress fiber
|
|
455
|
+
vars.workInProgressFiber = fiber;
|
|
616
456
|
vars.hookIndex = 0;
|
|
617
|
-
vars.
|
|
457
|
+
vars.workInProgressFiber.hooks = [];
|
|
458
|
+
|
|
618
459
|
const children = fiber.type(fiber.props);
|
|
619
|
-
|
|
620
|
-
if (Array.isArray(children)) {
|
|
621
|
-
// Fragment results returns array
|
|
622
|
-
childArr = [...children];
|
|
623
|
-
} else {
|
|
624
|
-
// Normal function component returns single root node
|
|
625
|
-
childArr = [children];
|
|
626
|
-
}
|
|
627
|
-
reconcileChildren(fiber, childArr);
|
|
460
|
+
reconcileChildren(fiber, children);
|
|
628
461
|
};
|
|
629
|
-
|
|
630
462
|
/**
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
463
|
+
* Updates a host component's DOM element and reconciles its children.
|
|
464
|
+
* This function handles standard DOM elements like div, span, etc.
|
|
465
|
+
*
|
|
466
|
+
* @param fiber - A fiber node representing the host component (e.g., a DOM node like div, span, etc.).
|
|
635
467
|
*/
|
|
636
468
|
const updateHostComponent = (fiber) => {
|
|
637
469
|
if (!fiber.dom) {
|
|
638
470
|
fiber.dom = createDom(fiber);
|
|
639
471
|
}
|
|
472
|
+
|
|
640
473
|
reconcileChildren(fiber, fiber.props.children);
|
|
641
474
|
};
|
|
642
475
|
|
|
@@ -647,15 +480,13 @@
|
|
|
647
480
|
});
|
|
648
481
|
|
|
649
482
|
/**
|
|
650
|
-
*
|
|
651
|
-
* browser needs to yield
|
|
652
|
-
* @param deadline -
|
|
653
|
-
* browser has to perform work before it needs to handle other tasks. It has a `timeRemaining()` method
|
|
654
|
-
* that returns the amount of time remaining before the deadline is reached. The `shouldYield` variable
|
|
655
|
-
* is used to determine
|
|
483
|
+
* Main work loop that processes the fiber tree using requestIdleCallback.
|
|
484
|
+
* It continues processing until all units of work are completed or the browser needs to yield.
|
|
485
|
+
* @param {IdleDeadline} deadline - Represents the time remaining for the browser to execute tasks before yielding.
|
|
656
486
|
*/
|
|
657
487
|
const workLoop = (deadline) => {
|
|
658
488
|
let shouldYield = false;
|
|
489
|
+
|
|
659
490
|
while (vars.nextUnitOfWork && !shouldYield) {
|
|
660
491
|
vars.nextUnitOfWork = performUnitOfWork(vars.nextUnitOfWork);
|
|
661
492
|
shouldYield = deadline.timeRemaining() < 1;
|
|
@@ -668,38 +499,28 @@
|
|
|
668
499
|
requestIdleCallback(workLoop);
|
|
669
500
|
};
|
|
670
501
|
|
|
502
|
+
// Start the work loop for the first time
|
|
671
503
|
requestIdleCallback(workLoop);
|
|
672
504
|
|
|
673
505
|
/**
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* @param fiber -
|
|
677
|
-
*
|
|
678
|
-
* parent, child, and sibling fibers. The `performUnitOfWork` function takes a fiber as a parameter and
|
|
679
|
-
* performs work
|
|
680
|
-
* @returns The function `performUnitOfWork` returns the next fiber to be processed. If the current
|
|
681
|
-
* fiber has a child, it returns the child. Otherwise, it looks for the next sibling of the current
|
|
682
|
-
* fiber. If there are no more siblings, it goes up the tree to the parent and looks for the next
|
|
683
|
-
* sibling of the parent. The function returns `undefined` if there are no more fibers to process.
|
|
506
|
+
* Processes a unit of work in the fiber tree.
|
|
507
|
+
* Decides whether to update a function component or a host component (DOM element).
|
|
508
|
+
* @param {Object} fiber - The current fiber representing a component and its state in the virtual DOM tree.
|
|
509
|
+
* @returns {null} The next fiber to process, or undefined if there are no more.
|
|
684
510
|
*/
|
|
685
511
|
const performUnitOfWork = (fiber) => {
|
|
686
512
|
const isFunctionComponent = fiber.type instanceof Function;
|
|
687
|
-
|
|
688
|
-
updateFunctionComponent(fiber)
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (fiber.child) {
|
|
693
|
-
return fiber.child
|
|
694
|
-
}
|
|
513
|
+
isFunctionComponent
|
|
514
|
+
? updateFunctionComponent(fiber)
|
|
515
|
+
: updateHostComponent(fiber);
|
|
516
|
+
|
|
517
|
+
if (fiber.child) return fiber.child
|
|
695
518
|
let nextFiber = fiber;
|
|
696
519
|
while (nextFiber) {
|
|
697
|
-
if (nextFiber.sibling)
|
|
698
|
-
return nextFiber.sibling
|
|
699
|
-
}
|
|
520
|
+
if (nextFiber.sibling) return nextFiber.sibling
|
|
700
521
|
nextFiber = nextFiber.parent;
|
|
701
522
|
}
|
|
702
|
-
return
|
|
523
|
+
return null
|
|
703
524
|
};
|
|
704
525
|
|
|
705
526
|
var Workers = /*#__PURE__*/Object.freeze({
|
|
@@ -708,6 +529,118 @@
|
|
|
708
529
|
workLoop: workLoop
|
|
709
530
|
});
|
|
710
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Renders an element into the specified container or the default root element.
|
|
534
|
+
*/
|
|
535
|
+
const render = (element, container) => {
|
|
536
|
+
if (!element || !container) {
|
|
537
|
+
console.error('Invalid element or container provided.');
|
|
538
|
+
return
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
vars.wipRoot = {
|
|
542
|
+
dom: container,
|
|
543
|
+
props: { children: [element] },
|
|
544
|
+
alternate: vars.currentRoot,
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
vars.deletions = [];
|
|
548
|
+
vars.nextUnitOfWork = vars.wipRoot;
|
|
549
|
+
requestIdleCallback(workLoop);
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Initializes the app with a root container.
|
|
554
|
+
*/
|
|
555
|
+
const init = (rootId = '__ryunix') => {
|
|
556
|
+
const rootElement = document.getElementById(rootId);
|
|
557
|
+
if (!rootElement) {
|
|
558
|
+
console.error(`Root element with ID '${rootId}' not found.`);
|
|
559
|
+
return null
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
vars.containerRoot = rootElement;
|
|
563
|
+
console.log(`Ryunix initialized with root ID: ${rootId}`);
|
|
564
|
+
return undefined
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* useStore is similar to useState in React, managing local state within a functional component.
|
|
569
|
+
*/
|
|
570
|
+
const useStore = (initial) => {
|
|
571
|
+
const oldHook = vars.workInProgressFiber.alternate?.hooks?.[vars.hookIndex];
|
|
572
|
+
const hook = { state: oldHook ? oldHook.state : initial, queue: [] };
|
|
573
|
+
|
|
574
|
+
const actions = oldHook ? oldHook.queue : [];
|
|
575
|
+
actions.forEach((action) => {
|
|
576
|
+
hook.state = typeof action === 'function' ? action(hook.state) : action;
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const setState = (action) => {
|
|
580
|
+
hook.queue.push(action);
|
|
581
|
+
vars.wipRoot = {
|
|
582
|
+
dom: vars.currentRoot.dom,
|
|
583
|
+
props: vars.currentRoot.props,
|
|
584
|
+
alternate: vars.currentRoot,
|
|
585
|
+
};
|
|
586
|
+
vars.nextUnitOfWork = vars.wipRoot;
|
|
587
|
+
vars.deletions = [];
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
vars.workInProgressFiber.hooks.push(hook);
|
|
591
|
+
vars.hookIndex++;
|
|
592
|
+
return [hook.state, setState]
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* useEffect runs side effects after rendering based on dependencies.
|
|
597
|
+
*/
|
|
598
|
+
const useEffect = (callback, deps) => {
|
|
599
|
+
const oldHook = vars.workInProgressFiber.alternate?.hooks?.[vars.hookIndex];
|
|
600
|
+
const hook = { type: 'effect', deps };
|
|
601
|
+
|
|
602
|
+
const hasChanged = !oldHook || !lodash.isEqual(oldHook.deps, deps);
|
|
603
|
+
if (hasChanged) {
|
|
604
|
+
const cleanup = callback();
|
|
605
|
+
hook.cleanup = cleanup;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
vars.workInProgressFiber.hooks.push(hook);
|
|
609
|
+
vars.hookIndex++;
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* useRef creates a mutable ref object to store values across renders.
|
|
614
|
+
*/
|
|
615
|
+
const useRef = (initialValue) => {
|
|
616
|
+
const oldHook = vars.workInProgressFiber.alternate?.hooks?.[vars.hookIndex];
|
|
617
|
+
const hook = { value: oldHook ? oldHook.value : { current: initialValue } };
|
|
618
|
+
|
|
619
|
+
vars.workInProgressFiber.hooks.push(hook);
|
|
620
|
+
vars.hookIndex++;
|
|
621
|
+
return hook.value
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* useMemo memoizes a value to avoid recomputations.
|
|
626
|
+
*/
|
|
627
|
+
const useMemo = (computeFn, deps) => {
|
|
628
|
+
const oldHook = vars.workInProgressFiber.alternate?.hooks?.[vars.hookIndex];
|
|
629
|
+
const hook = {
|
|
630
|
+
deps,
|
|
631
|
+
value: oldHook && lodash.isEqual(deps, oldHook.deps) ? oldHook.value : computeFn(),
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
vars.workInProgressFiber.hooks.push(hook);
|
|
635
|
+
vars.hookIndex++;
|
|
636
|
+
return hook.value
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* useCallback memoizes a callback function to avoid unnecessary recreations.
|
|
641
|
+
*/
|
|
642
|
+
const useCallback = (callback, deps) => useMemo(() => callback, deps);
|
|
643
|
+
|
|
711
644
|
var Ryunix = {
|
|
712
645
|
createElement,
|
|
713
646
|
render,
|
|
@@ -727,9 +660,7 @@
|
|
|
727
660
|
exports.useCallback = useCallback;
|
|
728
661
|
exports.useEffect = useEffect;
|
|
729
662
|
exports.useMemo = useMemo;
|
|
730
|
-
exports.useQuery = useQuery;
|
|
731
663
|
exports.useRef = useRef;
|
|
732
|
-
exports.useRouter = useRouter;
|
|
733
664
|
exports.useStore = useStore;
|
|
734
665
|
|
|
735
666
|
Object.defineProperty(exports, '__esModule', { value: true });
|