@unsetsoft/ryunixjs 1.1.6-canary.113 → 1.1.6-canary.115

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
@@ -121,792 +121,765 @@
121
121
  }
122
122
  };
123
123
 
124
+ const isEvent = (key) => key.startsWith('on');
125
+ const isProperty = (key) => key !== STRINGS.children && !isEvent(key);
126
+ const isNew = (prev, next) => (key) => prev[key] !== next[key];
127
+ const isGone = (next) => (key) => !(key in next);
128
+
124
129
  /**
125
- * Renders an element into a container using a work-in-progress (WIP) root.
126
- * @function render
127
- * @param {Object|HTMLElement} element - The element to be rendered in the container. It can be a Ryunix component (custom element) or a standard DOM element.
128
- * @param {HTMLElement} container - The container where the element will be rendered. This parameter is optional if `createRoot()` is used beforehand to set up the container.
129
- * @description The function assigns the `container` to a work-in-progress root and sets up properties for reconciliation, including children and the reference to the current root.
130
- * It also clears any scheduled deletions and establishes the next unit of work for incremental rendering.
130
+ * The function cancels all effect hooks in a given fiber.
131
+ * @param fiber - The "fiber" parameter is likely referring to a data structure used in React.js to
132
+ * represent a component and its state. It contains information about the component's props, state, and
133
+ * children, as well as metadata used by React to manage updates and rendering. The function
134
+ * "cancelEffects" is likely intended
131
135
  */
132
- const render = (element, container) => {
133
- vars.wipRoot = {
134
- dom: container,
135
- props: {
136
- children: [element],
137
- },
138
- alternate: vars.currentRoot,
139
- };
140
-
141
- vars.deletions = [];
142
- vars.nextUnitOfWork = vars.wipRoot;
136
+ const cancelEffects = (fiber) => {
137
+ if (fiber.hooks) {
138
+ fiber.hooks
139
+ .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.cancel)
140
+ .forEach((effectHook) => {
141
+ effectHook.cancel();
142
+ });
143
+ }
144
+ };
143
145
 
144
- return vars.wipRoot
146
+ /**
147
+ * The function runs all effect hooks in a given fiber.
148
+ * @param fiber - The "fiber" parameter is likely referring to a data structure used in the
149
+ * implementation of a fiber-based reconciliation algorithm, such as the one used in React. A fiber
150
+ * represents a unit of work that needs to be performed by the reconciliation algorithm, and it
151
+ * contains information about a component and its children, as
152
+ */
153
+ const runEffects = (fiber) => {
154
+ if (fiber.hooks) {
155
+ fiber.hooks
156
+ .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.effect)
157
+ .forEach((effectHook) => {
158
+ effectHook.cancel = effectHook.effect();
159
+ });
160
+ }
145
161
  };
146
162
 
147
163
  /**
148
- * Initializes the application by creating a reference to a DOM element with the specified ID and rendering the main component.
149
- * @function init
150
- * @param {Object} MainElement - The main component to render, typically the root component of the application.
151
- * @param {string} [root='__ryunix'] - The ID of the HTML element that serves as the container for the root element. Defaults to `'__ryunix'` if not provided.
152
- * @example
153
- * Ryunix.init(App, "__ryunix"); // Initializes and renders the App component into the <div id="__ryunix"></div> element.
154
- * @description This function retrieves the container element by its ID and invokes the `render` function to render the main component into it.
164
+ * The function creates a new DOM element based on the given fiber object and updates its properties.
165
+ * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
166
+ * contains information about the element type, props, and children of the node.
167
+ * @returns The `createDom` function returns a newly created DOM element based on the `fiber` object
168
+ * passed as an argument. If the `fiber` object represents a text element, a text node is created using
169
+ * `document.createTextNode("")`. Otherwise, a new element is created using
170
+ * `document.createElement(fiber.type)`. The function then calls the `updateDom` function to update the
171
+ * properties of the newly created
155
172
  */
156
- const init = (MainElement, root = '__ryunix') => {
157
- vars.containerRoot = document.getElementById(root);
173
+ const createDom = (fiber) => {
174
+ const dom =
175
+ fiber.type == RYUNIX_TYPES.TEXT_ELEMENT
176
+ ? document.createTextNode('')
177
+ : document.createElement(fiber.type);
158
178
 
159
- const renderProcess = render(MainElement, vars.containerRoot);
179
+ updateDom(dom, {}, fiber.props);
160
180
 
161
- return renderProcess
181
+ return dom
162
182
  };
163
183
 
164
184
  /**
165
- * @description The function creates a state.
166
- * @param initial - The initial value of the state for the hook.
167
- * @returns The `useStore` function returns an array with two elements: the current state value and a
168
- * `setState` function that can be used to update the state.
185
+ * The function updates the DOM by removing old event listeners and properties, and adding new ones
186
+ * based on the previous and next props.
187
+ * @param dom - The DOM element that needs to be updated with new props.
188
+ * @param prevProps - An object representing the previous props (properties) of a DOM element.
189
+ * @param nextProps - An object containing the new props that need to be updated in the DOM.
169
190
  */
170
- const useStore = (initial) => {
171
- const oldHook =
172
- vars.wipFiber.alternate &&
173
- vars.wipFiber.alternate.hooks &&
174
- vars.wipFiber.alternate.hooks[vars.hookIndex];
175
- const hook = {
176
- state: oldHook ? oldHook.state : initial,
177
- queue: oldHook ? [...oldHook.queue] : [],
178
- };
179
-
180
- hook.queue.forEach((action) => {
181
- hook.state =
182
- typeof action === STRINGS.function ? action(hook.state) : action;
183
- });
191
+ const updateDom = (dom, prevProps, nextProps) => {
192
+ Object.keys(prevProps)
193
+ .filter(isEvent)
194
+ .filter((key) => isGone(nextProps)(key) || isNew(prevProps, nextProps)(key))
195
+ .forEach((name) => {
196
+ const eventType = name.toLowerCase().substring(2);
197
+ dom.removeEventListener(eventType, prevProps[name]);
198
+ });
184
199
 
185
- hook.queue = [];
200
+ Object.keys(prevProps)
201
+ .filter(isProperty)
202
+ .filter(isGone(nextProps))
203
+ .forEach((name) => {
204
+ dom[name] = '';
205
+ });
186
206
 
187
- const setState = (action) => {
188
- hook.queue.push(action);
207
+ Object.keys(nextProps)
208
+ .filter(isProperty)
209
+ .filter(isNew(prevProps, nextProps))
210
+ .forEach((name) => {
211
+ if (name === STRINGS.style) {
212
+ DomStyle(dom, nextProps['ryunix-style']);
213
+ } else if (name === OLD_STRINGS.style) {
214
+ DomStyle(dom, nextProps.style);
215
+ } else if (name === STRINGS.className) {
216
+ if (nextProps['ryunix-class'] === '') {
217
+ throw new Error('data-class cannot be empty.')
218
+ }
189
219
 
190
- vars.wipRoot = {
191
- dom: vars.currentRoot.dom,
192
- props: {
193
- ...vars.currentRoot.props,
194
- },
195
- alternate: vars.currentRoot,
196
- };
197
- vars.nextUnitOfWork = vars.wipRoot;
198
- vars.deletions = [];
199
- };
220
+ prevProps['ryunix-class'] &&
221
+ dom.classList.remove(...prevProps['ryunix-class'].split(/\s+/));
222
+ dom.classList.add(...nextProps['ryunix-class'].split(/\s+/));
223
+ } else if (name === OLD_STRINGS.className) {
224
+ if (nextProps.className === '') {
225
+ throw new Error('className cannot be empty.')
226
+ }
200
227
 
201
- if (vars.wipFiber && vars.wipFiber.hooks) {
202
- vars.wipFiber.hooks.push(hook);
203
- vars.hookIndex++;
204
- }
228
+ prevProps.className &&
229
+ dom.classList.remove(...prevProps.className.split(/\s+/));
230
+ dom.classList.add(...nextProps.className.split(/\s+/));
231
+ } else {
232
+ dom[name] = nextProps[name];
233
+ }
234
+ });
205
235
 
206
- console.log(hook.state);
236
+ Object.keys(nextProps)
237
+ .filter(isEvent)
238
+ .filter(isNew(prevProps, nextProps))
239
+ .forEach((name) => {
240
+ const eventType = name.toLowerCase().substring(2);
241
+ dom.addEventListener(eventType, nextProps[name]);
242
+ });
243
+ };
207
244
 
208
- return [hook.state, setState]
245
+ const DomStyle = (dom, style) => {
246
+ dom.style = Object.keys(style).reduce((acc, styleName) => {
247
+ const key = styleName.replace(reg, function (v) {
248
+ return '-' + v.toLowerCase()
249
+ });
250
+ acc += `${key}: ${style[styleName]};`;
251
+ return acc
252
+ }, '');
209
253
  };
210
254
 
255
+ var Dom = /*#__PURE__*/Object.freeze({
256
+ __proto__: null,
257
+ DomStyle: DomStyle,
258
+ createDom: createDom,
259
+ updateDom: updateDom
260
+ });
261
+
211
262
  /**
212
- * This is a function that creates a hook for managing side effects in Ryunix components.
213
- * @param effect - The effect function that will be executed after the component has rendered or when
214
- * the dependencies have changed. It can perform side effects such as fetching data, updating the DOM,
215
- * or subscribing to events.
216
- * @param deps - An array of dependencies that the effect depends on. If any of the dependencies change
217
- * between renders, the effect will be re-run. If the array is empty, the effect will only run once on
218
- * mount and never again.
263
+ * The function commits changes made to the virtual DOM to the actual DOM.
219
264
  */
265
+ const commitRoot = () => {
266
+ vars.deletions.forEach(commitWork);
267
+ if (vars.wipRoot && vars.wipRoot.child) {
268
+ commitWork(vars.wipRoot.child);
269
+ vars.currentRoot = vars.wipRoot;
270
+ }
271
+ vars.wipRoot = undefined;
272
+ };
220
273
 
221
- const useEffect = (callback, deps) => {
222
- const oldHook =
223
- vars.wipFiber.alternate &&
224
- vars.wipFiber.alternate.hooks &&
225
- vars.wipFiber.alternate.hooks[vars.hookIndex];
226
-
227
- const hook = {
228
- type: RYUNIX_TYPES.RYUNIX_EFFECT,
229
- deps,
230
- };
274
+ /**
275
+ * The function commits changes made to the DOM based on the effect tag of the fiber.
276
+ * @param fiber - A fiber is a unit of work in Ryunix's reconciliation process. It represents a
277
+ * component and its state at a particular point in time. The `commitWork` function takes a fiber as a
278
+ * parameter to commit the changes made during the reconciliation process to the actual DOM.
279
+ * @returns The function does not return anything, it performs side effects by manipulating the DOM.
280
+ */
281
+ const commitWork = (fiber) => {
282
+ if (!fiber) return
231
283
 
232
- if (!oldHook) {
233
- // invoke callback if this is the first time
234
- callback();
235
- } else {
236
- if (!lodash.isEqual(oldHook.deps, hook.deps)) {
237
- callback();
238
- }
284
+ let domParentFiber = fiber.parent;
285
+ while (!domParentFiber.dom) {
286
+ domParentFiber = domParentFiber.parent;
239
287
  }
288
+ const domParent = domParentFiber.dom;
240
289
 
241
- if (vars.wipFiber.hooks) {
242
- vars.wipFiber.hooks.push(hook);
243
- vars.hookIndex++;
290
+ if (fiber.effectTag === EFFECT_TAGS.PLACEMENT && fiber.dom != null) {
291
+ domParent.appendChild(fiber.dom);
292
+ runEffects(fiber);
293
+ } else if (fiber.effectTag === EFFECT_TAGS.UPDATE && fiber.dom != null) {
294
+ cancelEffects(fiber);
295
+ updateDom(fiber.dom, fiber.alternate.props, fiber.props);
296
+ runEffects(fiber);
297
+ } else if (fiber.effectTag === EFFECT_TAGS.DELETION) {
298
+ commitDeletion(fiber, domParent);
299
+ cancelEffects(fiber);
300
+ return
244
301
  }
245
- };
246
302
 
247
- const useRef = (initial) => {
248
- const oldHook =
249
- vars.wipFiber.alternate &&
250
- vars.wipFiber.alternate.hooks &&
251
- vars.wipFiber.alternate.hooks[vars.hookIndex];
303
+ // Recorre los "fibers" hijos y hermanos
304
+ commitWork(fiber.child);
305
+ commitWork(fiber.sibling);
306
+ };
252
307
 
253
- const hook = {
254
- type: RYUNIX_TYPES.RYUNIX_REF,
255
- value: oldHook ? oldHook.value : { current: initial },
256
- };
257
-
258
- if (vars.wipFiber.hooks) {
259
- vars.wipFiber.hooks.push(hook);
260
- vars.hookIndex++;
308
+ /**
309
+ * The function removes a fiber's corresponding DOM node from its parent node or recursively removes
310
+ * its child's DOM node until it finds a node to remove.
311
+ * @param fiber - a fiber node in a fiber tree, which represents a component or an element in the Ryunix
312
+ * application.
313
+ * @param domParent - The parent DOM element from which the fiber's DOM element needs to be removed.
314
+ */
315
+ const commitDeletion = (fiber, domParent) => {
316
+ if (fiber.dom) {
317
+ domParent.removeChild(fiber.dom);
318
+ } else {
319
+ commitDeletion(fiber.child, domParent);
261
320
  }
321
+ };
262
322
 
263
- return hook.value
323
+ var Commits = /*#__PURE__*/Object.freeze({
324
+ __proto__: null,
325
+ commitDeletion: commitDeletion,
326
+ commitRoot: commitRoot,
327
+ commitWork: commitWork
328
+ });
329
+
330
+ /**
331
+ * This function reconciles the children of a fiber node with a new set of elements, creating new
332
+ * fibers for new elements, updating existing fibers for elements with the same type, and marking old
333
+ * fibers for deletion if they are not present in the new set of elements.
334
+ * @param wipFiber - A work-in-progress fiber object representing a component or element in the virtual
335
+ * DOM tree.
336
+ * @param elements - an array of elements representing the new children to be rendered in the current
337
+ * fiber's subtree
338
+ */
339
+ const shouldComponentUpdate = (oldProps, newProps) => {
340
+ // Comparar las propiedades antiguas y nuevas
341
+ return (
342
+ !oldProps ||
343
+ !newProps ||
344
+ Object.keys(oldProps).length !== Object.keys(newProps).length ||
345
+ Object.keys(newProps).some((key) => oldProps[key] !== newProps[key])
346
+ )
264
347
  };
265
348
 
266
- const useMemo = (comp, deps) => {
267
- const oldHook =
268
- vars.wipFiber.alternate &&
269
- vars.wipFiber.alternate.hooks &&
270
- vars.wipFiber.alternate.hooks[vars.hookIndex];
349
+ const recycleFiber = (oldFiber, newProps) => {
350
+ return {
351
+ ...oldFiber,
352
+ props: newProps,
353
+ alternate: oldFiber,
354
+ effectTag: EFFECT_TAGS.UPDATE,
355
+ }
356
+ };
271
357
 
272
- const hook = {
273
- type: RYUNIX_TYPES.RYUNIX_MEMO,
274
- value: null,
275
- deps,
276
- };
358
+ const reconcileChildren = (wipFiber, elements) => {
359
+ let index = 0;
360
+ let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
361
+ let prevSibling = null;
277
362
 
278
- if (oldHook) {
279
- if (lodash.isEqual(oldHook.deps, hook.deps)) {
280
- hook.value = oldHook.value;
281
- } else {
282
- hook.value = comp();
283
- }
284
- } else {
285
- hook.value = comp();
363
+ const oldFibersMap = new Map();
364
+ while (oldFiber) {
365
+ const oldKey = oldFiber.props.key || oldFiber.type;
366
+ oldFibersMap.set(oldKey, oldFiber);
367
+ oldFiber = oldFiber.sibling;
286
368
  }
287
369
 
288
- if (vars.wipFiber.hooks) {
289
- vars.wipFiber.hooks.push(hook);
290
- vars.hookIndex++;
370
+ while (index < elements.length) {
371
+ const element = elements[index];
372
+ const key = element.props.key || element.type;
373
+ const oldFiber = oldFibersMap.get(key);
374
+
375
+ let newFiber;
376
+ const sameType = oldFiber && element && element.type === oldFiber.type;
377
+
378
+ if (sameType && !shouldComponentUpdate(oldFiber.props, element.props)) {
379
+ // Reutilizar fibra existente si no hay cambios
380
+ newFiber = recycleFiber(oldFiber, element.props);
381
+ oldFibersMap.delete(key);
382
+ } else if (sameType) {
383
+ // Actualizar fibra existente
384
+ newFiber = {
385
+ type: oldFiber.type,
386
+ props: element.props,
387
+ dom: oldFiber.dom,
388
+ parent: wipFiber,
389
+ alternate: oldFiber,
390
+ effectTag: EFFECT_TAGS.UPDATE,
391
+ };
392
+ oldFibersMap.delete(key);
393
+ } else if (element) {
394
+ // Crear nueva fibra
395
+ newFiber = {
396
+ type: element.type,
397
+ props: element.props,
398
+ dom: undefined,
399
+ parent: wipFiber,
400
+ alternate: undefined,
401
+ effectTag: EFFECT_TAGS.PLACEMENT,
402
+ };
403
+ }
404
+
405
+ if (index === 0) {
406
+ wipFiber.child = newFiber;
407
+ } else if (prevSibling) {
408
+ prevSibling.sibling = newFiber;
409
+ }
410
+
411
+ prevSibling = newFiber;
412
+ index++;
291
413
  }
292
414
 
293
- return hook.value
415
+ oldFibersMap.forEach((oldFiber) => {
416
+ oldFiber.effectTag = EFFECT_TAGS.DELETION;
417
+ vars.deletions.push(oldFiber);
418
+ });
294
419
  };
295
420
 
296
- const useCallback = (callback, deps) => {
297
- return useMemo(() => callback, deps)
421
+ var Reconciler = /*#__PURE__*/Object.freeze({
422
+ __proto__: null,
423
+ reconcileChildren: reconcileChildren
424
+ });
425
+
426
+ /**
427
+ * This function updates a function component by setting up a work-in-progress fiber, resetting the
428
+ * hook index, creating an empty hooks array, rendering the component, and reconciling its children.
429
+ * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
430
+ * contains information about the component, its props, state, and children. In this function, it is
431
+ * used to update the state of the component and its children.
432
+ */
433
+ const updateFunctionComponent = (fiber) => {
434
+ vars.wipFiber = fiber;
435
+ vars.hookIndex = 0;
436
+ vars.wipFiber.hooks = [];
437
+
438
+ const children = fiber.type(fiber.props);
439
+ let childArr = Array.isArray(children) ? children : [children];
440
+
441
+ reconcileChildren(fiber, childArr);
298
442
  };
299
443
 
300
444
  /**
301
- * The `useQuery` function parses the query parameters from the URL and returns them as an object.
302
- * @returns An object containing key-value pairs of the query parameters from the URLSearchParams in
303
- * the current window's URL is being returned.
445
+ * This function updates a host component's DOM element and reconciles its children.
446
+ * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
447
+ * contains information about the component's type, props, and children, as well as pointers to other
448
+ * fibers in the tree.
304
449
  */
305
- const useQuery = () => {
306
- const searchParams = new URLSearchParams(window.location.search);
307
- const query = {};
308
- for (let [key, value] of searchParams.entries()) {
309
- query[key] = value;
450
+ const updateHostComponent = (fiber) => {
451
+ if (!fiber.dom) {
452
+ fiber.dom = createDom(fiber);
310
453
  }
311
- return query
454
+ reconcileChildren(fiber, fiber.props.children);
312
455
  };
313
456
 
457
+ var Components = /*#__PURE__*/Object.freeze({
458
+ __proto__: null,
459
+ updateFunctionComponent: updateFunctionComponent,
460
+ updateHostComponent: updateHostComponent
461
+ });
462
+
314
463
  /**
315
- * `useRouter` is a routing function to manage navigation, nested routes, and route pre-loading.
316
- *
317
- * This function handles client-side routing, URL updates, and component rendering based on defined routes. It supports:
318
- * - Dynamic routes (e.g., "/user/:id").
319
- * - Optional nested routes with an `subRoutes` property in route objects.
320
- * - Default pre-loading of all routes except the current active route.
321
- *
322
- * @param {Array} routes - An array of route objects, each containing:
323
- * - `path` (string): The URL path to match (supports dynamic segments like "/user/:id").
324
- * - `component` (function): The component to render when the route matches.
325
- * - `subRoutes` (optional array): An optional array of nested route objects, defining sub-routes for this route.
326
- * - `NotFound` (optional function): Component to render for unmatched routes (default 404 behavior).
327
- *
328
- * @returns {Object} - An object with:
329
- * - `Children` (function): Returns the component that matches the current route, passing route parameters and query parameters as props.
330
- * - `NavLink` (component): A link component to navigate within the application without refreshing the page.
331
- * - `navigate` (function): Allows programmatically navigating to a specific path.
332
- *
333
- * @example
334
- * // Define nested routes
335
- * const routes = [
336
- * {
337
- * path: "/",
338
- * component: HomePage,
339
- * subRoutes: [
340
- * {
341
- * path: "/settings",
342
- * component: SettingsPage,
343
- * },
344
- * ],
345
- * },
346
- * {
347
- * path: "/user/:id",
348
- * component: UserProfile,
349
- * },
350
- * {
351
- * path: "*",
352
- * NotFound: NotFoundPage,
353
- * },
354
- * ];
355
- *
356
- * // Use the routing function
357
- * const { Children, NavLink } = useRouter(routes);
358
- *
359
- * // Render the matched component
360
- * const App = () => (
361
- * <>
362
- * <NavLink to="/">Home</NavLink>
363
- * <NavLink to="/settings">Settings</NavLink>
364
- * <NavLink to="/user/123">User Profile</NavLink>
365
- * <Children />
366
- * </>
367
- * );
464
+ * This function uses requestIdleCallback to perform work on a fiber tree until it is complete or the
465
+ * browser needs to yield to other tasks.
466
+ * @param deadline - The `deadline` parameter is an object that represents the amount of time the
467
+ * browser has to perform work before it needs to handle other tasks. It has a `timeRemaining()` method
468
+ * that returns the amount of time remaining before the deadline is reached. The `shouldYield` variable
469
+ * is used to determine
368
470
  */
369
- const useRouter = (routes) => {
370
- const [location, setLocation] = useStore(window.location.pathname);
471
+ const workLoop = (deadline) => {
472
+ let shouldYield = false;
473
+ while (vars.nextUnitOfWork && !shouldYield) {
474
+ vars.nextUnitOfWork = performUnitOfWork(vars.nextUnitOfWork);
475
+ shouldYield = deadline.timeRemaining() < 1;
476
+ }
371
477
 
372
- const findRoute = (routes, path) => {
373
- const pathname = path.split('?')[0];
478
+ if (!vars.nextUnitOfWork && vars.wipRoot) {
479
+ commitRoot();
480
+ }
374
481
 
375
- const notFoundRoute = routes.find((route) => route.NotFound);
376
- const notFound = notFoundRoute
377
- ? { route: { component: notFoundRoute.NotFound }, params: {} }
378
- : { route: { component: null }, params: {} };
482
+ requestIdleCallback(workLoop);
483
+ };
379
484
 
380
- for (const route of routes) {
381
- if (route.subRoutes) {
382
- const childRoute = findRoute(route.subRoutes, path);
383
- if (childRoute) return childRoute
384
- }
385
-
386
- if (route.path === '*') {
387
- return notFound
388
- }
389
-
390
- if (!route.path || typeof route.path !== 'string') {
391
- console.warn('Invalid route detected:', route);
392
- console.info(
393
- "if you are using { NotFound: NotFound } please add { path: '*', NotFound: NotFound }",
394
- );
395
- continue
396
- }
485
+ requestIdleCallback(workLoop);
397
486
 
398
- const keys = [];
399
- const pattern = new RegExp(
400
- `^${route.path.replace(/:\w+/g, (match) => {
401
- keys.push(match.substring(1));
402
- return '([^/]+)'
403
- })}$`,
404
- );
487
+ /**
488
+ * The function performs a unit of work by updating either a function component or a host component and
489
+ * returns the next fiber to be processed.
490
+ * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
491
+ * contains information about the component's type, props, and children, as well as pointers to its
492
+ * parent, child, and sibling fibers. The `performUnitOfWork` function takes a fiber as a parameter and
493
+ * performs work
494
+ * @returns The function `performUnitOfWork` returns the next fiber to be processed. If the current
495
+ * fiber has a child, it returns the child. Otherwise, it looks for the next sibling of the current
496
+ * fiber. If there are no more siblings, it goes up the tree to the parent and looks for the next
497
+ * sibling of the parent. The function returns `undefined` if there are no more fibers to process.
498
+ */
499
+ const performUnitOfWork = (fiber) => {
500
+ const isFunctionComponent = fiber.type instanceof Function;
501
+ if (isFunctionComponent) {
502
+ updateFunctionComponent(fiber);
503
+ } else {
504
+ updateHostComponent(fiber);
505
+ }
506
+ if (fiber.child) {
507
+ return fiber.child
508
+ }
509
+ let nextFiber = fiber;
510
+ while (nextFiber) {
511
+ if (nextFiber.sibling) {
512
+ return nextFiber.sibling
513
+ }
514
+ nextFiber = nextFiber.parent;
515
+ }
516
+ return undefined
517
+ };
405
518
 
406
- const match = pathname.match(pattern);
407
- if (match) {
408
- const params = keys.reduce((acc, key, index) => {
409
- acc[key] = match[index + 1];
410
- return acc
411
- }, {});
519
+ const scheduleWork = (root) => {
520
+ vars.nextUnitOfWork = root;
521
+ requestIdleCallback(workLoop);
522
+ };
412
523
 
413
- return { route, params }
414
- }
415
- }
524
+ var Workers = /*#__PURE__*/Object.freeze({
525
+ __proto__: null,
526
+ performUnitOfWork: performUnitOfWork,
527
+ scheduleWork: scheduleWork,
528
+ workLoop: workLoop
529
+ });
416
530
 
417
- return notFound
531
+ /**
532
+ * Renders an element into a container using a work-in-progress (WIP) root.
533
+ * @function render
534
+ * @param {Object|HTMLElement} element - The element to be rendered in the container. It can be a Ryunix component (custom element) or a standard DOM element.
535
+ * @param {HTMLElement} container - The container where the element will be rendered. This parameter is optional if `createRoot()` is used beforehand to set up the container.
536
+ * @description The function assigns the `container` to a work-in-progress root and sets up properties for reconciliation, including children and the reference to the current root.
537
+ * It also clears any scheduled deletions and establishes the next unit of work for incremental rendering.
538
+ */
539
+ const render = (element, container) => {
540
+ vars.wipRoot = {
541
+ dom: container,
542
+ props: {
543
+ children: [element],
544
+ },
545
+ alternate: vars.currentRoot,
418
546
  };
419
547
 
420
- const navigate = (path) => {
421
- window.history.pushState({}, '', path);
422
- updateRoute(path);
423
- };
548
+ vars.nextUnitOfWork = vars.wipRoot;
549
+ vars.deletions = [];
550
+ scheduleWork(vars.wipRoot);
551
+ return vars.wipRoot
552
+ };
424
553
 
425
- const updateRoute = (path) => {
426
- const cleanedPath = path.split('?')[0];
427
- setLocation(cleanedPath);
428
- };
554
+ /**
555
+ * Initializes the application by creating a reference to a DOM element with the specified ID and rendering the main component.
556
+ * @function init
557
+ * @param {Object} MainElement - The main component to render, typically the root component of the application.
558
+ * @param {string} [root='__ryunix'] - The ID of the HTML element that serves as the container for the root element. Defaults to `'__ryunix'` if not provided.
559
+ * @example
560
+ * Ryunix.init(App, "__ryunix"); // Initializes and renders the App component into the <div id="__ryunix"></div> element.
561
+ * @description This function retrieves the container element by its ID and invokes the `render` function to render the main component into it.
562
+ */
563
+ const init = (MainElement, root = '__ryunix') => {
564
+ vars.containerRoot = document.getElementById(root);
429
565
 
430
- useEffect(() => {
431
- const onPopState = () => updateRoute(window.location.pathname);
432
- window.addEventListener('popstate', onPopState);
566
+ const renderProcess = render(MainElement, vars.containerRoot);
433
567
 
434
- return () => window.removeEventListener('popstate', onPopState)
435
- }, []);
568
+ return renderProcess
569
+ };
436
570
 
437
- const currentRouteData = findRoute(routes, location) || {};
571
+ /**
572
+ * @description The function creates a state.
573
+ * @param initial - The initial value of the state for the hook.
574
+ * @returns The `useStore` function returns an array with two elements: the current state value and a
575
+ * `setState` function that can be used to update the state.
576
+ */
577
+ const useStore = (initial) => {
578
+ const oldHook =
579
+ vars.wipFiber.alternate &&
580
+ vars.wipFiber.alternate.hooks &&
581
+ vars.wipFiber.alternate.hooks[vars.hookIndex];
582
+ const hook = {
583
+ state: oldHook ? oldHook.state : initial,
584
+ queue: oldHook ? [...oldHook.queue] : [],
585
+ };
438
586
 
439
- const Children = () => {
440
- const query = useQuery();
441
- const { route } = currentRouteData;
587
+ hook.queue.forEach((action) => {
588
+ hook.state =
589
+ typeof action === STRINGS.function ? action(hook.state) : action;
590
+ });
442
591
 
443
- if (
444
- !route ||
445
- !route.component ||
446
- typeof route.component !== STRINGS.function
447
- ) {
448
- console.error(
449
- 'Component not found for current path or the component is not a valid function:',
450
- currentRouteData,
451
- );
452
- return null
453
- }
592
+ hook.queue = [];
454
593
 
455
- return route.component({
456
- params: currentRouteData.params || {},
457
- query,
458
- })
459
- };
594
+ const setState = (action) => {
595
+ hook.queue.push(action);
460
596
 
461
- const NavLink = ({ to, ...props }) => {
462
- const handleClick = (e) => {
463
- e.preventDefault();
464
- navigate(to);
597
+ vars.wipRoot = {
598
+ dom: vars.currentRoot.dom,
599
+ props: {
600
+ ...vars.currentRoot.props,
601
+ },
602
+ alternate: vars.currentRoot,
465
603
  };
466
- return createElement(
467
- 'a',
468
- { href: to, onClick: handleClick, ...props },
469
- props.children,
470
- )
604
+ vars.nextUnitOfWork = vars.wipRoot;
605
+ vars.deletions = [];
471
606
  };
472
607
 
473
- return { Children, NavLink, navigate }
474
- };
475
-
476
- /**
477
- * The function commits changes made to the virtual DOM to the actual DOM.
478
- */
479
- const commitRoot = () => {
480
- vars.deletions.forEach(commitWork);
481
- if (vars.wipRoot && vars.wipRoot.child) {
482
- commitWork(vars.wipRoot.child);
483
- vars.currentRoot = vars.wipRoot;
608
+ if (vars.wipFiber && vars.wipFiber.hooks) {
609
+ vars.wipFiber.hooks.push(hook);
610
+ vars.hookIndex++;
484
611
  }
485
- vars.wipRoot = undefined;
612
+
613
+ console.log(hook.state);
614
+
615
+ return [hook.state, setState]
486
616
  };
487
617
 
488
618
  /**
489
- * The function commits changes made to the DOM based on the effect tag of the fiber.
490
- * @param fiber - A fiber is a unit of work in Ryunix's reconciliation process. It represents a
491
- * component and its state at a particular point in time. The `commitWork` function takes a fiber as a
492
- * parameter to commit the changes made during the reconciliation process to the actual DOM.
493
- * @returns The function does not return anything, it performs side effects by manipulating the DOM.
619
+ * This is a function that creates a hook for managing side effects in Ryunix components.
620
+ * @param effect - The effect function that will be executed after the component has rendered or when
621
+ * the dependencies have changed. It can perform side effects such as fetching data, updating the DOM,
622
+ * or subscribing to events.
623
+ * @param deps - An array of dependencies that the effect depends on. If any of the dependencies change
624
+ * between renders, the effect will be re-run. If the array is empty, the effect will only run once on
625
+ * mount and never again.
494
626
  */
495
- const commitWork = (fiber) => {
496
- if (!fiber) return
497
-
498
- let domParentFiber = fiber.parent;
499
- while (!domParentFiber.dom) {
500
- domParentFiber = domParentFiber.parent;
501
- }
502
- const domParent = domParentFiber.dom;
503
627
 
504
- if (fiber.effectTag === EFFECT_TAGS.PLACEMENT && fiber.dom != null) {
505
- domParent.appendChild(fiber.dom);
506
- runEffects(fiber);
507
- } else if (fiber.effectTag === EFFECT_TAGS.UPDATE && fiber.dom != null) {
508
- cancelEffects(fiber);
509
- updateDom(fiber.dom, fiber.alternate.props, fiber.props);
510
- runEffects(fiber);
511
- } else if (fiber.effectTag === EFFECT_TAGS.DELETION) {
512
- commitDeletion(fiber, domParent);
513
- cancelEffects(fiber);
514
- return
515
- }
628
+ const useEffect = (callback, deps) => {
629
+ const oldHook =
630
+ vars.wipFiber.alternate &&
631
+ vars.wipFiber.alternate.hooks &&
632
+ vars.wipFiber.alternate.hooks[vars.hookIndex];
516
633
 
517
- // Recorre los "fibers" hijos y hermanos
518
- commitWork(fiber.child);
519
- commitWork(fiber.sibling);
520
- };
634
+ const hook = {
635
+ type: RYUNIX_TYPES.RYUNIX_EFFECT,
636
+ deps,
637
+ };
521
638
 
522
- /**
523
- * The function removes a fiber's corresponding DOM node from its parent node or recursively removes
524
- * its child's DOM node until it finds a node to remove.
525
- * @param fiber - a fiber node in a fiber tree, which represents a component or an element in the Ryunix
526
- * application.
527
- * @param domParent - The parent DOM element from which the fiber's DOM element needs to be removed.
528
- */
529
- const commitDeletion = (fiber, domParent) => {
530
- if (fiber.dom) {
531
- domParent.removeChild(fiber.dom);
639
+ if (!oldHook) {
640
+ // invoke callback if this is the first time
641
+ callback();
532
642
  } else {
533
- commitDeletion(fiber.child, domParent);
643
+ if (!lodash.isEqual(oldHook.deps, hook.deps)) {
644
+ callback();
645
+ }
534
646
  }
535
- };
536
647
 
537
- var Commits = /*#__PURE__*/Object.freeze({
538
- __proto__: null,
539
- commitDeletion: commitDeletion,
540
- commitRoot: commitRoot,
541
- commitWork: commitWork
542
- });
543
-
544
- const isEvent = (key) => key.startsWith('on');
545
- const isProperty = (key) => key !== STRINGS.children && !isEvent(key);
546
- const isNew = (prev, next) => (key) => prev[key] !== next[key];
547
- const isGone = (next) => (key) => !(key in next);
548
-
549
- /**
550
- * The function cancels all effect hooks in a given fiber.
551
- * @param fiber - The "fiber" parameter is likely referring to a data structure used in React.js to
552
- * represent a component and its state. It contains information about the component's props, state, and
553
- * children, as well as metadata used by React to manage updates and rendering. The function
554
- * "cancelEffects" is likely intended
555
- */
556
- const cancelEffects = (fiber) => {
557
- if (fiber.hooks) {
558
- fiber.hooks
559
- .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.cancel)
560
- .forEach((effectHook) => {
561
- effectHook.cancel();
562
- });
648
+ if (vars.wipFiber.hooks) {
649
+ vars.wipFiber.hooks.push(hook);
650
+ vars.hookIndex++;
563
651
  }
564
652
  };
565
653
 
566
- /**
567
- * The function runs all effect hooks in a given fiber.
568
- * @param fiber - The "fiber" parameter is likely referring to a data structure used in the
569
- * implementation of a fiber-based reconciliation algorithm, such as the one used in React. A fiber
570
- * represents a unit of work that needs to be performed by the reconciliation algorithm, and it
571
- * contains information about a component and its children, as
572
- */
573
- const runEffects = (fiber) => {
574
- if (fiber.hooks) {
575
- fiber.hooks
576
- .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.effect)
577
- .forEach((effectHook) => {
578
- effectHook.cancel = effectHook.effect();
579
- });
580
- }
581
- };
654
+ const useRef = (initial) => {
655
+ const oldHook =
656
+ vars.wipFiber.alternate &&
657
+ vars.wipFiber.alternate.hooks &&
658
+ vars.wipFiber.alternate.hooks[vars.hookIndex];
582
659
 
583
- /**
584
- * The function creates a new DOM element based on the given fiber object and updates its properties.
585
- * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
586
- * contains information about the element type, props, and children of the node.
587
- * @returns The `createDom` function returns a newly created DOM element based on the `fiber` object
588
- * passed as an argument. If the `fiber` object represents a text element, a text node is created using
589
- * `document.createTextNode("")`. Otherwise, a new element is created using
590
- * `document.createElement(fiber.type)`. The function then calls the `updateDom` function to update the
591
- * properties of the newly created
592
- */
593
- const createDom = (fiber) => {
594
- const dom =
595
- fiber.type == RYUNIX_TYPES.TEXT_ELEMENT
596
- ? document.createTextNode('')
597
- : document.createElement(fiber.type);
660
+ const hook = {
661
+ type: RYUNIX_TYPES.RYUNIX_REF,
662
+ value: oldHook ? oldHook.value : { current: initial },
663
+ };
598
664
 
599
- updateDom(dom, {}, fiber.props);
665
+ if (vars.wipFiber.hooks) {
666
+ vars.wipFiber.hooks.push(hook);
667
+ vars.hookIndex++;
668
+ }
600
669
 
601
- return dom
670
+ return hook.value
602
671
  };
603
672
 
604
- /**
605
- * The function updates the DOM by removing old event listeners and properties, and adding new ones
606
- * based on the previous and next props.
607
- * @param dom - The DOM element that needs to be updated with new props.
608
- * @param prevProps - An object representing the previous props (properties) of a DOM element.
609
- * @param nextProps - An object containing the new props that need to be updated in the DOM.
610
- */
611
- const updateDom = (dom, prevProps, nextProps) => {
612
- Object.keys(prevProps)
613
- .filter(isEvent)
614
- .filter((key) => isGone(nextProps)(key) || isNew(prevProps, nextProps)(key))
615
- .forEach((name) => {
616
- const eventType = name.toLowerCase().substring(2);
617
- dom.removeEventListener(eventType, prevProps[name]);
618
- });
619
-
620
- Object.keys(prevProps)
621
- .filter(isProperty)
622
- .filter(isGone(nextProps))
623
- .forEach((name) => {
624
- dom[name] = '';
625
- });
673
+ const useMemo = (comp, deps) => {
674
+ const oldHook =
675
+ vars.wipFiber.alternate &&
676
+ vars.wipFiber.alternate.hooks &&
677
+ vars.wipFiber.alternate.hooks[vars.hookIndex];
626
678
 
627
- Object.keys(nextProps)
628
- .filter(isProperty)
629
- .filter(isNew(prevProps, nextProps))
630
- .forEach((name) => {
631
- if (name === STRINGS.style) {
632
- DomStyle(dom, nextProps['ryunix-style']);
633
- } else if (name === OLD_STRINGS.style) {
634
- DomStyle(dom, nextProps.style);
635
- } else if (name === STRINGS.className) {
636
- if (nextProps['ryunix-class'] === '') {
637
- throw new Error('data-class cannot be empty.')
638
- }
679
+ const hook = {
680
+ type: RYUNIX_TYPES.RYUNIX_MEMO,
681
+ value: null,
682
+ deps,
683
+ };
639
684
 
640
- prevProps['ryunix-class'] &&
641
- dom.classList.remove(...prevProps['ryunix-class'].split(/\s+/));
642
- dom.classList.add(...nextProps['ryunix-class'].split(/\s+/));
643
- } else if (name === OLD_STRINGS.className) {
644
- if (nextProps.className === '') {
645
- throw new Error('className cannot be empty.')
646
- }
685
+ if (oldHook) {
686
+ if (lodash.isEqual(oldHook.deps, hook.deps)) {
687
+ hook.value = oldHook.value;
688
+ } else {
689
+ hook.value = comp();
690
+ }
691
+ } else {
692
+ hook.value = comp();
693
+ }
647
694
 
648
- prevProps.className &&
649
- dom.classList.remove(...prevProps.className.split(/\s+/));
650
- dom.classList.add(...nextProps.className.split(/\s+/));
651
- } else {
652
- dom[name] = nextProps[name];
653
- }
654
- });
695
+ if (vars.wipFiber.hooks) {
696
+ vars.wipFiber.hooks.push(hook);
697
+ vars.hookIndex++;
698
+ }
655
699
 
656
- Object.keys(nextProps)
657
- .filter(isEvent)
658
- .filter(isNew(prevProps, nextProps))
659
- .forEach((name) => {
660
- const eventType = name.toLowerCase().substring(2);
661
- dom.addEventListener(eventType, nextProps[name]);
662
- });
700
+ return hook.value
663
701
  };
664
702
 
665
- const DomStyle = (dom, style) => {
666
- dom.style = Object.keys(style).reduce((acc, styleName) => {
667
- const key = styleName.replace(reg, function (v) {
668
- return '-' + v.toLowerCase()
669
- });
670
- acc += `${key}: ${style[styleName]};`;
671
- return acc
672
- }, '');
703
+ const useCallback = (callback, deps) => {
704
+ return useMemo(() => callback, deps)
673
705
  };
674
706
 
675
- var Dom = /*#__PURE__*/Object.freeze({
676
- __proto__: null,
677
- DomStyle: DomStyle,
678
- createDom: createDom,
679
- updateDom: updateDom
680
- });
681
-
682
707
  /**
683
- * This function reconciles the children of a fiber node with a new set of elements, creating new
684
- * fibers for new elements, updating existing fibers for elements with the same type, and marking old
685
- * fibers for deletion if they are not present in the new set of elements.
686
- * @param wipFiber - A work-in-progress fiber object representing a component or element in the virtual
687
- * DOM tree.
688
- * @param elements - an array of elements representing the new children to be rendered in the current
689
- * fiber's subtree
708
+ * The `useQuery` function parses the query parameters from the URL and returns them as an object.
709
+ * @returns An object containing key-value pairs of the query parameters from the URLSearchParams in
710
+ * the current window's URL is being returned.
690
711
  */
691
- const shouldComponentUpdate = (oldProps, newProps) => {
692
- // Comparar las propiedades antiguas y nuevas
693
- return (
694
- !oldProps ||
695
- !newProps ||
696
- Object.keys(oldProps).length !== Object.keys(newProps).length ||
697
- Object.keys(newProps).some((key) => oldProps[key] !== newProps[key])
698
- )
699
- };
700
-
701
- const recycleFiber = (oldFiber, newProps) => {
702
- return {
703
- ...oldFiber,
704
- props: newProps,
705
- alternate: oldFiber,
706
- effectTag: EFFECT_TAGS.UPDATE,
712
+ const useQuery = () => {
713
+ const searchParams = new URLSearchParams(window.location.search);
714
+ const query = {};
715
+ for (let [key, value] of searchParams.entries()) {
716
+ query[key] = value;
707
717
  }
718
+ return query
708
719
  };
709
720
 
710
- const reconcileChildren = (wipFiber, elements) => {
711
- let index = 0;
712
- let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
713
- let prevSibling = null;
714
-
715
- const oldFibersMap = new Map();
716
- while (oldFiber) {
717
- const oldKey = oldFiber.props.key || oldFiber.type;
718
- oldFibersMap.set(oldKey, oldFiber);
719
- oldFiber = oldFiber.sibling;
720
- }
721
-
722
- while (index < elements.length) {
723
- const element = elements[index];
724
- const key = element.props.key || element.type;
725
- const oldFiber = oldFibersMap.get(key);
726
-
727
- let newFiber;
728
- const sameType = oldFiber && element && element.type === oldFiber.type;
729
-
730
- if (sameType && !shouldComponentUpdate(oldFiber.props, element.props)) {
731
- // Reutilizar fibra existente si no hay cambios
732
- newFiber = recycleFiber(oldFiber, element.props);
733
- oldFibersMap.delete(key);
734
- } else if (sameType) {
735
- // Actualizar fibra existente
736
- newFiber = {
737
- type: oldFiber.type,
738
- props: element.props,
739
- dom: oldFiber.dom,
740
- parent: wipFiber,
741
- alternate: oldFiber,
742
- effectTag: EFFECT_TAGS.UPDATE,
743
- };
744
- oldFibersMap.delete(key);
745
- } else if (element) {
746
- // Crear nueva fibra
747
- newFiber = {
748
- type: element.type,
749
- props: element.props,
750
- dom: undefined,
751
- parent: wipFiber,
752
- alternate: undefined,
753
- effectTag: EFFECT_TAGS.PLACEMENT,
754
- };
755
- }
756
-
757
- if (index === 0) {
758
- wipFiber.child = newFiber;
759
- } else if (prevSibling) {
760
- prevSibling.sibling = newFiber;
761
- }
721
+ /**
722
+ * `useRouter` is a routing function to manage navigation, nested routes, and route pre-loading.
723
+ *
724
+ * This function handles client-side routing, URL updates, and component rendering based on defined routes. It supports:
725
+ * - Dynamic routes (e.g., "/user/:id").
726
+ * - Optional nested routes with an `subRoutes` property in route objects.
727
+ * - Default pre-loading of all routes except the current active route.
728
+ *
729
+ * @param {Array} routes - An array of route objects, each containing:
730
+ * - `path` (string): The URL path to match (supports dynamic segments like "/user/:id").
731
+ * - `component` (function): The component to render when the route matches.
732
+ * - `subRoutes` (optional array): An optional array of nested route objects, defining sub-routes for this route.
733
+ * - `NotFound` (optional function): Component to render for unmatched routes (default 404 behavior).
734
+ *
735
+ * @returns {Object} - An object with:
736
+ * - `Children` (function): Returns the component that matches the current route, passing route parameters and query parameters as props.
737
+ * - `NavLink` (component): A link component to navigate within the application without refreshing the page.
738
+ * - `navigate` (function): Allows programmatically navigating to a specific path.
739
+ *
740
+ * @example
741
+ * // Define nested routes
742
+ * const routes = [
743
+ * {
744
+ * path: "/",
745
+ * component: HomePage,
746
+ * subRoutes: [
747
+ * {
748
+ * path: "/settings",
749
+ * component: SettingsPage,
750
+ * },
751
+ * ],
752
+ * },
753
+ * {
754
+ * path: "/user/:id",
755
+ * component: UserProfile,
756
+ * },
757
+ * {
758
+ * path: "*",
759
+ * NotFound: NotFoundPage,
760
+ * },
761
+ * ];
762
+ *
763
+ * // Use the routing function
764
+ * const { Children, NavLink } = useRouter(routes);
765
+ *
766
+ * // Render the matched component
767
+ * const App = () => (
768
+ * <>
769
+ * <NavLink to="/">Home</NavLink>
770
+ * <NavLink to="/settings">Settings</NavLink>
771
+ * <NavLink to="/user/123">User Profile</NavLink>
772
+ * <Children />
773
+ * </>
774
+ * );
775
+ */
776
+ const useRouter = (routes) => {
777
+ const [location, setLocation] = useStore(window.location.pathname);
762
778
 
763
- prevSibling = newFiber;
764
- index++;
765
- }
779
+ const findRoute = (routes, path) => {
780
+ const pathname = path.split('?')[0];
766
781
 
767
- oldFibersMap.forEach((oldFiber) => {
768
- oldFiber.effectTag = EFFECT_TAGS.DELETION;
769
- vars.deletions.push(oldFiber);
770
- });
771
- };
782
+ const notFoundRoute = routes.find((route) => route.NotFound);
783
+ const notFound = notFoundRoute
784
+ ? { route: { component: notFoundRoute.NotFound }, params: {} }
785
+ : { route: { component: null }, params: {} };
772
786
 
773
- let updateQueue = [];
787
+ for (const route of routes) {
788
+ if (route.subRoutes) {
789
+ const childRoute = findRoute(route.subRoutes, path);
790
+ if (childRoute) return childRoute
791
+ }
774
792
 
775
- const scheduleUpdate = (fiber, priority) => {
776
- updateQueue.push({ fiber, priority });
777
- updateQueue.sort((a, b) => a.priority - b.priority); // Ordenar por prioridad
778
- };
793
+ if (route.path === '*') {
794
+ return notFound
795
+ }
779
796
 
780
- const performConcurrentWork = () => {
781
- while (updateQueue.length > 0) {
782
- const { fiber } = updateQueue.shift();
783
- performUnitOfWork$1(fiber);
784
- }
785
- };
797
+ if (!route.path || typeof route.path !== 'string') {
798
+ console.warn('Invalid route detected:', route);
799
+ console.info(
800
+ "if you are using { NotFound: NotFound } please add { path: '*', NotFound: NotFound }",
801
+ );
802
+ continue
803
+ }
786
804
 
787
- const performUnitOfWork$1 = (fiber) => {
788
- // Procesar la unidad de trabajo
789
- reconcileChildren(fiber, fiber.props.children);
805
+ const keys = [];
806
+ const pattern = new RegExp(
807
+ `^${route.path.replace(/:\w+/g, (match) => {
808
+ keys.push(match.substring(1));
809
+ return '([^/]+)'
810
+ })}$`,
811
+ );
790
812
 
791
- if (fiber.child) {
792
- return fiber.child
793
- }
813
+ const match = pathname.match(pattern);
814
+ if (match) {
815
+ const params = keys.reduce((acc, key, index) => {
816
+ acc[key] = match[index + 1];
817
+ return acc
818
+ }, {});
794
819
 
795
- let nextFiber = fiber;
796
- while (nextFiber) {
797
- if (nextFiber.sibling) {
798
- return nextFiber.sibling
820
+ return { route, params }
821
+ }
799
822
  }
800
- nextFiber = nextFiber.parent;
801
- }
802
- };
803
823
 
804
- var Reconciler = /*#__PURE__*/Object.freeze({
805
- __proto__: null,
806
- performConcurrentWork: performConcurrentWork,
807
- reconcileChildren: reconcileChildren,
808
- scheduleUpdate: scheduleUpdate
809
- });
824
+ return notFound
825
+ };
810
826
 
811
- /**
812
- * This function updates a function component by setting up a work-in-progress fiber, resetting the
813
- * hook index, creating an empty hooks array, rendering the component, and reconciling its children.
814
- * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
815
- * contains information about the component, its props, state, and children. In this function, it is
816
- * used to update the state of the component and its children.
817
- */
818
- const updateFunctionComponent = (fiber) => {
819
- vars.wipFiber = fiber;
820
- vars.hookIndex = 0;
821
- vars.wipFiber.hooks = [];
827
+ const navigate = (path) => {
828
+ window.history.pushState({}, '', path);
829
+ updateRoute(path);
830
+ };
822
831
 
823
- const children = fiber.type(fiber.props);
824
- let childArr = Array.isArray(children) ? children : [children];
832
+ const updateRoute = (path) => {
833
+ const cleanedPath = path.split('?')[0];
834
+ setLocation(cleanedPath);
835
+ };
825
836
 
826
- reconcileChildren(fiber, childArr);
827
- };
837
+ useEffect(() => {
838
+ const onPopState = () => updateRoute(window.location.pathname);
839
+ window.addEventListener('popstate', onPopState);
828
840
 
829
- /**
830
- * This function updates a host component's DOM element and reconciles its children.
831
- * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
832
- * contains information about the component's type, props, and children, as well as pointers to other
833
- * fibers in the tree.
834
- */
835
- const updateHostComponent = (fiber) => {
836
- if (!fiber.dom) {
837
- fiber.dom = createDom(fiber);
838
- }
839
- reconcileChildren(fiber, fiber.props.children);
840
- };
841
+ return () => window.removeEventListener('popstate', onPopState)
842
+ }, []);
841
843
 
842
- var Components = /*#__PURE__*/Object.freeze({
843
- __proto__: null,
844
- updateFunctionComponent: updateFunctionComponent,
845
- updateHostComponent: updateHostComponent
846
- });
844
+ const currentRouteData = findRoute(routes, location) || {};
847
845
 
848
- /**
849
- * This function uses requestIdleCallback to perform work on a fiber tree until it is complete or the
850
- * browser needs to yield to other tasks.
851
- * @param deadline - The `deadline` parameter is an object that represents the amount of time the
852
- * browser has to perform work before it needs to handle other tasks. It has a `timeRemaining()` method
853
- * that returns the amount of time remaining before the deadline is reached. The `shouldYield` variable
854
- * is used to determine
855
- */
856
- const workLoop = (deadline) => {
857
- let shouldYield = false;
858
- while (vars.nextUnitOfWork && !shouldYield) {
859
- vars.nextUnitOfWork = performUnitOfWork(vars.nextUnitOfWork);
860
- shouldYield = deadline.timeRemaining() < 1;
861
- }
846
+ const Children = () => {
847
+ const query = useQuery();
848
+ const { route } = currentRouteData;
862
849
 
863
- if (!vars.nextUnitOfWork && vars.wipRoot) {
864
- commitRoot();
865
- }
850
+ if (
851
+ !route ||
852
+ !route.component ||
853
+ typeof route.component !== STRINGS.function
854
+ ) {
855
+ console.error(
856
+ 'Component not found for current path or the component is not a valid function:',
857
+ currentRouteData,
858
+ );
859
+ return null
860
+ }
866
861
 
867
- requestIdleCallback(workLoop);
868
- };
862
+ return route.component({
863
+ params: currentRouteData.params || {},
864
+ query,
865
+ })
866
+ };
869
867
 
870
- requestIdleCallback(workLoop);
868
+ const NavLink = ({ to, ...props }) => {
869
+ const handleClick = (e) => {
870
+ e.preventDefault();
871
+ navigate(to);
872
+ };
873
+ return createElement(
874
+ 'a',
875
+ { href: to, onClick: handleClick, ...props },
876
+ props.children,
877
+ )
878
+ };
871
879
 
872
- /**
873
- * The function performs a unit of work by updating either a function component or a host component and
874
- * returns the next fiber to be processed.
875
- * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
876
- * contains information about the component's type, props, and children, as well as pointers to its
877
- * parent, child, and sibling fibers. The `performUnitOfWork` function takes a fiber as a parameter and
878
- * performs work
879
- * @returns The function `performUnitOfWork` returns the next fiber to be processed. If the current
880
- * fiber has a child, it returns the child. Otherwise, it looks for the next sibling of the current
881
- * fiber. If there are no more siblings, it goes up the tree to the parent and looks for the next
882
- * sibling of the parent. The function returns `undefined` if there are no more fibers to process.
883
- */
884
- const performUnitOfWork = (fiber) => {
885
- const isFunctionComponent = fiber.type instanceof Function;
886
- if (isFunctionComponent) {
887
- updateFunctionComponent(fiber);
888
- } else {
889
- updateHostComponent(fiber);
890
- }
891
- if (fiber.child) {
892
- return fiber.child
893
- }
894
- let nextFiber = fiber;
895
- while (nextFiber) {
896
- if (nextFiber.sibling) {
897
- return nextFiber.sibling
898
- }
899
- nextFiber = nextFiber.parent;
900
- }
901
- return undefined
880
+ return { Children, NavLink, navigate }
902
881
  };
903
882
 
904
- var Workers = /*#__PURE__*/Object.freeze({
905
- __proto__: null,
906
- performUnitOfWork: performUnitOfWork,
907
- workLoop: workLoop
908
- });
909
-
910
883
  var Ryunix = {
911
884
  createElement,
912
885
  render,