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