@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 CHANGED
@@ -9,12 +9,13 @@
9
9
  nextUnitOfWork: undefined,
10
10
  currentRoot: undefined,
11
11
  wipRoot: undefined,
12
- deletions: undefined,
13
- wipFiber: undefined,
14
- hookIndex: undefined,
12
+ deletions: [],
13
+ workInProgressFiber: undefined,
14
+ hookIndex: 0,
15
+ errorBoundary: null, // New property to hold error boundary references
15
16
  };
16
17
 
17
- const reg = /[A-Z]/g;
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
- const childArray = (children, out) => {
52
- out = out || [];
53
- if (children == undefined || typeof children == STRINGS.boolean) ; else if (Array.isArray(children)) {
54
- children.some((child) => {
55
- childArray(child, out);
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 the given type, props, and children.
65
- * @param type - The type of the element to be created, such as "div", "span", "h1", etc.
66
- * @param props - The `props` parameter is an object that contains the properties or attributes of the
67
- * element being created. These properties can include things like `className`, `id`, `style`, and any
68
- * other custom attributes that the user wants to add to the element. The `props` object is spread
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 with a given text value.
95
- * @param text - The text content that will be used to create a new text element.
96
- * @returns A JavaScript object with a `type` property set to `"TEXT_ELEMENT"` and a `props` property
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
- * The function renders an element into a container using a work-in-progress root.
112
- * @param element - The element parameter is the component or element that needs to be rendered in the
113
- * container. It could be a Ryunix component or a DOM element.
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 init = (root) => {
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
- * @description The function creates a state.
143
- * @param initial - The initial value of the state for the hook.
144
- * @returns The `useStore` function returns an array with two elements: the current state value and a
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 useStore = (initial) => {
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
- * This is a function that creates a hook for managing side effects in Ryunix components.
190
- * @param effect - The effect function that will be executed after the component has rendered or when
191
- * the dependencies have changed. It can perform side effects such as fetching data, updating the DOM,
192
- * or subscribing to events.
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
- * The `useQuery` function is a custom hook in JavaScript that retrieves query parameters from the URL
226
- * and stores them in a hook for easy access.
227
- * @returns The `useQuery` function returns the `query` property of the `hook` object.
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
- * The function cancels all effect hooks in a given fiber.
338
- * @param fiber - The "fiber" parameter is likely referring to a data structure used in React.js to
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
- * The function runs all effect hooks in a given fiber.
355
- * @param fiber - The "fiber" parameter is likely referring to a data structure used in 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
- * The function creates a new DOM element based on the given fiber object and updates its properties.
372
- * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
373
- * contains information about the element type, props, and children of the node.
374
- * @returns The `createDom` function returns a newly created DOM element based on the `fiber` object
375
- * passed as an argument. If the `fiber` object represents a text element, a text node is created using
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 == RYUNIX_TYPES.TEXT_ELEMENT
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
- * The function updates the DOM by removing old event listeners and properties, and adding new ones
393
- * based on the previous and next props.
394
- * @param dom - The DOM element that needs to be updated with new props.
395
- * @param prevProps - An object representing the previous props (properties) of a DOM element.
396
- * @param nextProps - An object containing the new props that need to be updated in the DOM.
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['ryunix-style']);
420
- } else if (name === OLD_STRINGS.style) {
421
- DomStyle(dom, nextProps.style);
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(reg, function (v) {
455
- return '-' + v.toLowerCase()
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
- * The function commits changes made to the DOM based on the effect tag of the fiber.
483
- * @param fiber - A fiber is a unit of work in Ryunix's reconciliation process. It represents a
484
- * component and its state at a particular point in time. The `commitWork` function takes a fiber as a
485
- * parameter to commit the changes made during the reconciliation process to the actual DOM.
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 commitWork = (fiber) => {
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
- const domParent = domParentFiber.dom;
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
- if (fiber.effectTag === EFFECT_TAGS.PLACEMENT) {
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
- } else if (fiber.effectTag === EFFECT_TAGS.UPDATE) {
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
- } else if (fiber.effectTag === EFFECT_TAGS.DELETION) {
347
+ },
348
+ [EFFECT_TAGS.DELETION]: (fiber, domParent) => {
511
349
  cancelEffects(fiber);
512
350
  commitDeletion(fiber, domParent);
513
- return
514
- }
515
-
516
- commitWork(fiber.child);
517
- commitWork(fiber.sibling);
351
+ },
518
352
  };
519
353
 
520
354
  /**
521
- * The function removes a fiber's corresponding DOM node from its parent node or recursively removes
522
- * its child's DOM node until it finds a node to remove.
523
- * @param fiber - a fiber node in a fiber tree, which represents a component or an element in the Ryunix
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 commitDeletion = (fiber, domParent) => {
528
- if (fiber && fiber.dom) {
529
- domParent.removeChild(fiber.dom);
530
- } else if (fiber && fiber.child) {
531
- commitDeletion(fiber.child, domParent);
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 ? oldFiber.type : undefined,
402
+ type: oldFiber.type,
565
403
  props: element.props,
566
- dom: oldFiber ? oldFiber.dom : undefined,
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 (oldFiber && !sameType) {
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
- * This function updates a function component by setting up a work-in-progress fiber, resetting the
609
- * hook index, creating an empty hooks array, rendering the component, and reconciling its children.
610
- * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
611
- * contains information about the component, its props, state, and children. In this function, it is
612
- * used to update the state of the component and its children.
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
- vars.wipFiber = fiber;
454
+ // Set the current work-in-progress fiber
455
+ vars.workInProgressFiber = fiber;
616
456
  vars.hookIndex = 0;
617
- vars.wipFiber.hooks = [];
457
+ vars.workInProgressFiber.hooks = [];
458
+
618
459
  const children = fiber.type(fiber.props);
619
- let childArr = [];
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
- * This function updates a host component's DOM element and reconciles its children.
632
- * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
633
- * contains information about the component's type, props, and children, as well as pointers to other
634
- * fibers in the tree.
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
- * This function uses requestIdleCallback to perform work on a fiber tree until it is complete or the
651
- * browser needs to yield to other tasks.
652
- * @param deadline - The `deadline` parameter is an object that represents the amount of time the
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
- * The function performs a unit of work by updating either a function component or a host component and
675
- * returns the next fiber to be processed.
676
- * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
677
- * contains information about the component's type, props, and children, as well as pointers to its
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
- if (isFunctionComponent) {
688
- updateFunctionComponent(fiber);
689
- } else {
690
- updateHostComponent(fiber);
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 undefined
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 });