@unsetsoft/ryunixjs 1.1.6 → 1.1.7-canary.10

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