@unsetsoft/ryunixjs 0.1.10 → 0.1.12-beta.0
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/lib/component.js +25 -6
- package/lib/reconciler.js +612 -102
- package/lib/ryunix.js +13 -10
- package/package.json +1 -1
package/lib/component.js
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
import { scheduleUpdate } from "./reconciler";
|
|
2
|
+
|
|
3
|
+
export class Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
this.props = props;
|
|
6
|
+
this.state = this.state || {};
|
|
7
|
+
}
|
|
8
|
+
setState(partialState) {
|
|
9
|
+
// this.state = Object.assign({}, this.state, partialState);
|
|
10
|
+
// scheduleUpdate(this.__internalInstance, partialState);
|
|
11
|
+
throw Error(
|
|
12
|
+
"This function is not implemented yet, has a lot of bugs. You can check https://github.com/UnSetSoft/Ryunixjs/issues/10 for more information."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class Fragment {
|
|
18
|
+
constructor(props) {
|
|
19
|
+
this.props = props;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
render() {
|
|
23
|
+
return this.props.children;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/lib/reconciler.js
CHANGED
|
@@ -1,102 +1,612 @@
|
|
|
1
|
-
import { updateDomProperties } from "./dom-utils";
|
|
2
|
-
import { TEXT_ELEMENT } from "./element";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
1
|
+
import { updateDomProperties } from "./dom-utils";
|
|
2
|
+
import { TEXT_ELEMENT } from "./element";
|
|
3
|
+
|
|
4
|
+
let rootInstance = null;
|
|
5
|
+
const ENOUGH_TIME = 1;
|
|
6
|
+
let workQueue = [];
|
|
7
|
+
let nextUnitOfWork = null;
|
|
8
|
+
const CLASS_COMPONENT = "class";
|
|
9
|
+
const HOST_ROOT = "root";
|
|
10
|
+
const HOST_COMPONENT = "host";
|
|
11
|
+
const PLACEMENT = "PLACEMENT"; // this is for a child that needs to be added
|
|
12
|
+
const DELETION = "DELETION"; //for a child that needs to be deleted.
|
|
13
|
+
const UPDATE = "UPDATE"; // for a child that needs to be updated. refresh the props
|
|
14
|
+
let pendingCommit = null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The function adds a task to a work queue and requests idle callback to perform the work.
|
|
18
|
+
* @param task - The task parameter is a function that represents the work that needs to be done. It
|
|
19
|
+
* will be added to the workQueue array, which is a queue of tasks waiting to be executed. The
|
|
20
|
+
* requestIdleCallback function will be used to schedule the execution of the performWork function,
|
|
21
|
+
* which will process the tasks
|
|
22
|
+
*/
|
|
23
|
+
function schedule(task) {
|
|
24
|
+
workQueue.push(task);
|
|
25
|
+
requestIdleCallback(performWork);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* This function performs work in a loop using requestIdleCallback and commits any pending changes.
|
|
30
|
+
* @param deadline - The `deadline` parameter is a time value representing the deadline by which the
|
|
31
|
+
* `performWork` function should finish its work. It is used to ensure that the function does not
|
|
32
|
+
* exceed a certain amount of time and cause the browser to become unresponsive. The function will stop
|
|
33
|
+
* working on the current task when
|
|
34
|
+
*/
|
|
35
|
+
function performWork(deadline) {
|
|
36
|
+
if (!nextUnitOfWork) {
|
|
37
|
+
initialUnitOfWork();
|
|
38
|
+
}
|
|
39
|
+
loopThroughWork(deadline);
|
|
40
|
+
if (nextUnitOfWork || workQueue.length > 0) {
|
|
41
|
+
requestIdleCallback(performWork);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (pendingCommit) {
|
|
45
|
+
commitAllWork(pendingCommit);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The function commits all the effects of a fiber and updates the root container fiber.
|
|
51
|
+
* @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
|
|
52
|
+
* contains information about the component, its state, props, and children. The fiber also has a
|
|
53
|
+
* reference to its parent, child, and sibling fibers.
|
|
54
|
+
*/
|
|
55
|
+
function commitAllWork(fiber) {
|
|
56
|
+
fiber.effects.forEach((f) => {
|
|
57
|
+
commitWork(f);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
fiber.stateNode._rootContainerFiber = fiber;
|
|
61
|
+
nextUnitOfWork = null;
|
|
62
|
+
pendingCommit = null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The function commits changes to the DOM based on the effect tag of the fiber.
|
|
67
|
+
* @param fiber - A fiber is a lightweight unit of work that represents a component and its state
|
|
68
|
+
* during the reconciliation process in React's virtual DOM. It contains information about the
|
|
69
|
+
* component's type, props, state, and children, as well as pointers to its parent, child, and sibling
|
|
70
|
+
* fibers.
|
|
71
|
+
* @returns If the fiber tag is HOST_ROOT, nothing is being returned.
|
|
72
|
+
*/
|
|
73
|
+
function commitWork(fiber) {
|
|
74
|
+
if (fiber.tag == HOST_ROOT) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let domParentFiber = fiber.parent;
|
|
78
|
+
while (domParentFiber.tag == CLASS_COMPONENT) {
|
|
79
|
+
domParentFiber = domParentFiber.parent;
|
|
80
|
+
}
|
|
81
|
+
const domParent = domParentFiber.stateNode;
|
|
82
|
+
if (fiber.effectTag == PLACEMENT && fiber.tag == HOST_COMPONENT) {
|
|
83
|
+
domParent.appendChild(fiber.stateNode);
|
|
84
|
+
} else if (fiber.effectTag == UPDATE) {
|
|
85
|
+
updateDomProperties(fiber.stateNode, fiber.alternate.props, fiber.props);
|
|
86
|
+
} else if (fiber.effectTag == DELETION) {
|
|
87
|
+
commitDeletion(fiber, domParent);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The function removes a fiber and its corresponding DOM node from the parent DOM node.
|
|
93
|
+
* @param fiber - The fiber is a data structure used by React to represent a component and its state
|
|
94
|
+
* during the rendering process. It contains information about the component's type, props, children,
|
|
95
|
+
* and other metadata.
|
|
96
|
+
* @param domParent - The DOM element that is the parent of the component being deleted.
|
|
97
|
+
* @returns The function does not explicitly return anything, but it will exit the function and return
|
|
98
|
+
* control to the calling function when the condition `if (node == fiber)` is met.
|
|
99
|
+
*/
|
|
100
|
+
function commitDeletion(fiber, domParent) {
|
|
101
|
+
let node = fiber;
|
|
102
|
+
while (true) {
|
|
103
|
+
if (node.tag == CLASS_COMPONENT) {
|
|
104
|
+
node = node.child;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
domParent.removeChild(node.stateNode);
|
|
108
|
+
while (node != fiber && !node.sibling) {
|
|
109
|
+
node = node.parent;
|
|
110
|
+
}
|
|
111
|
+
if (node == fiber) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
node = node.sibling;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The function begins the work of updating a fiber either for a class component or a host component.
|
|
120
|
+
* @param wipFiber - wipFiber is a fiber object that represents the work-in-progress (WIP) component
|
|
121
|
+
* being worked on by the reconciler in a React application. It contains information about the
|
|
122
|
+
* component's type, props, state, and children, as well as pointers to its parent, sibling, and child
|
|
123
|
+
* fibers
|
|
124
|
+
*/
|
|
125
|
+
function beginWork(wipFiber) {
|
|
126
|
+
if (wipFiber.tag == CLASS_COMPONENT) {
|
|
127
|
+
updateClassFiber(wipFiber);
|
|
128
|
+
} else {
|
|
129
|
+
updateHostFiber(wipFiber);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* This function updates the host fiber by creating a new DOM element and reconciling its children.
|
|
135
|
+
* @param wipFiber - wipFiber is a work-in-progress fiber object that represents a component or element
|
|
136
|
+
* in the virtual DOM tree. It contains information about the component's type, props, state, and
|
|
137
|
+
* children. The function `updateHostFiber` uses this wipFiber object to update the corresponding DOM
|
|
138
|
+
* element
|
|
139
|
+
*/
|
|
140
|
+
function updateHostFiber(wipFiber) {
|
|
141
|
+
if (!wipFiber.stateNode) {
|
|
142
|
+
wipFiber.stateNode = createDomElement(wipFiber);
|
|
143
|
+
}
|
|
144
|
+
const newChildElements = wipFiber.props.children;
|
|
145
|
+
reconcileChildrenArray(wipFiber, newChildElements);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* This function updates the state and props of a fiber node and reconciles its child elements.
|
|
150
|
+
* @param wipFiber - wipFiber is a work-in-progress fiber that represents a component in the fiber
|
|
151
|
+
* tree. It contains information about the component's type, props, state, and children. The function
|
|
152
|
+
* `updateClassFiber` updates the state and props of the component represented by the wipFiber.
|
|
153
|
+
* @returns Nothing is being returned explicitly in this function. It either updates the instance and
|
|
154
|
+
* child fibers or clones the child fibers and returns nothing.
|
|
155
|
+
*/
|
|
156
|
+
function updateClassFiber(wipFiber) {
|
|
157
|
+
let instance = wipFiber.stateNode;
|
|
158
|
+
if (instance == null) {
|
|
159
|
+
instance = wipFiber.stateNode = createInstance(wipFiber);
|
|
160
|
+
} else if (wipFiber.props == instance.props && !wipFiber.partialState) {
|
|
161
|
+
cloneChildFibers(wipFiber);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
instance.props = wipFiber.props;
|
|
166
|
+
instance.state = Object.assign({}, instance.state, wipFiber.partialState);
|
|
167
|
+
wipFiber.partialState = null;
|
|
168
|
+
|
|
169
|
+
const newChildElements = wipFiber.stateNode.render();
|
|
170
|
+
reconcileChildrenArray(wipFiber, newChildElements);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The function creates an instance of a component using the given fiber.
|
|
175
|
+
* @param fiber - The "fiber" parameter is an object that represents a node in the fiber tree. It
|
|
176
|
+
* contains information about the component type, props, children, and other metadata needed for
|
|
177
|
+
* rendering and updating the component.
|
|
178
|
+
* @returns The function `createInstance` returns an instance of the component class specified in the
|
|
179
|
+
* `fiber` argument, with the props passed to it. The instance also has a reference to the `fiber`
|
|
180
|
+
* object.
|
|
181
|
+
*/
|
|
182
|
+
function createInstance(fiber) {
|
|
183
|
+
const instance = new fiber.type(fiber.props);
|
|
184
|
+
instance.__fiber = fiber;
|
|
185
|
+
return instance;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* This function creates a new DOM element based on the given fiber object.
|
|
190
|
+
* @param fiber - The "fiber" parameter is an object that represents a node in the virtual DOM tree. It
|
|
191
|
+
* contains information about the element's type, props, and children.
|
|
192
|
+
* @returns The function `createDomElement` returns a DOM element created using the
|
|
193
|
+
* `document.createElement` method. If the `fiber` object represents a text element, then a text node
|
|
194
|
+
* is created using `document.createTextNode` method. The properties of the element are updated using
|
|
195
|
+
* the `updateDomProperties` function.
|
|
196
|
+
*/
|
|
197
|
+
function createDomElement(fiber) {
|
|
198
|
+
const isTextElement = fiber.type === TEXT_ELEMENT;
|
|
199
|
+
const dom = isTextElement
|
|
200
|
+
? document.createTextNode("")
|
|
201
|
+
: document.createElement(fiber.type);
|
|
202
|
+
updateDomProperties(dom, [], fiber.props);
|
|
203
|
+
return dom;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* This function performs work on a fiber and its children, completing each unit of work before moving
|
|
208
|
+
* on to the next.
|
|
209
|
+
* @param wipFiber - wipFiber stands for "work-in-progress fiber". In React, a fiber is a lightweight
|
|
210
|
+
* representation of a component or element in the component tree. The wipFiber parameter represents
|
|
211
|
+
* the current fiber that is being worked on by the reconciler during the rendering process. The
|
|
212
|
+
* performUnitOfWork function performs
|
|
213
|
+
* @returns the next unit of work to be performed, which is either the first child of the current fiber
|
|
214
|
+
* (if it has one), or the next sibling of the current fiber (if it has one), or the parent of the
|
|
215
|
+
* current fiber (if it has no more siblings).
|
|
216
|
+
*/
|
|
217
|
+
function performUnitOfWork(wipFiber) {
|
|
218
|
+
beginWork(wipFiber);
|
|
219
|
+
if (wipFiber.child) {
|
|
220
|
+
return wipFiber.child;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let uow = wipFiber;
|
|
224
|
+
while (uow) {
|
|
225
|
+
completeWork(uow);
|
|
226
|
+
|
|
227
|
+
if (uow.sibling) {
|
|
228
|
+
return uow.sibling;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
uow = uow.parent;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* The function initializes a unit of work by dequeuing an update from a work queue and setting the
|
|
237
|
+
* next unit of work based on the update's properties.
|
|
238
|
+
* @returns The function does not have a return statement, so it returns undefined.
|
|
239
|
+
*/
|
|
240
|
+
function initialUnitOfWork() {
|
|
241
|
+
const update = workQueue.shift();
|
|
242
|
+
|
|
243
|
+
if (!update) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (update.partialState) {
|
|
248
|
+
update.instance.__fiber.partialState = update.partialState;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const root =
|
|
252
|
+
update.from === HOST_ROOT
|
|
253
|
+
? update.dom._rootContainerFiber
|
|
254
|
+
: getRootNode(update.instance.__fiber);
|
|
255
|
+
|
|
256
|
+
nextUnitOfWork = {
|
|
257
|
+
tag: HOST_ROOT,
|
|
258
|
+
stateNode: update.dom || root.stateNode,
|
|
259
|
+
props: update.newProps || root.props,
|
|
260
|
+
alternate: root,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The function returns the root node of a given fiber by traversing up the parent chain.
|
|
266
|
+
* @param fiber - The "fiber" parameter is likely referring to a data structure used in React.js to
|
|
267
|
+
* represent a component and its state. It is used in the function to traverse the component tree and
|
|
268
|
+
* find the root node of the tree.
|
|
269
|
+
* @returns The function `getRootNode` returns the root node of a given fiber by traversing up the
|
|
270
|
+
* fiber tree until it reaches the topmost parent node.
|
|
271
|
+
*/
|
|
272
|
+
function getRootNode(fiber) {
|
|
273
|
+
let node = fiber;
|
|
274
|
+
while (node.parent) {
|
|
275
|
+
node = node.parent;
|
|
276
|
+
}
|
|
277
|
+
return node;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* This function loops through work while there is still time remaining before the deadline.
|
|
282
|
+
* @param deadline - The `deadline` parameter is an object that represents a deadline by which the
|
|
283
|
+
* current task should be completed. It has a `timeRemaining()` method that returns the amount of time
|
|
284
|
+
* left until the deadline, in milliseconds. The `loopThroughWork()` function uses this `deadline`
|
|
285
|
+
* object to check if there
|
|
286
|
+
*/
|
|
287
|
+
function loopThroughWork(deadline) {
|
|
288
|
+
while (nextUnitOfWork && deadline.timeRemaining() > ENOUGH_TIME) {
|
|
289
|
+
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The function creates an array of children elements, either from an array or a single element.
|
|
295
|
+
* @param children - The parameter `children` is expected to be a value that represents the children of
|
|
296
|
+
* a parent element in a web page. It can be an array of child elements or a single child element. If
|
|
297
|
+
* `children` is not provided or is falsy, an empty array is returned.
|
|
298
|
+
* @returns The function `createArrayOfChildren` is returning an array. If the `children` parameter is
|
|
299
|
+
* falsy (e.g. `null`, `undefined`, `false`, `0`, `NaN`, or an empty string), it returns an empty
|
|
300
|
+
* array. If `children` is already an array, it returns that array. Otherwise, it returns an array with
|
|
301
|
+
* `children` as its only element.
|
|
302
|
+
*/
|
|
303
|
+
function createArrayOfChildren(children) {
|
|
304
|
+
return !children ? [] : Array.isArray(children) ? children : [children];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* This function reconciles the children of a fiber node with a new array of child elements.
|
|
309
|
+
* @param wipFiber - The work-in-progress fiber, which represents the current state of the component
|
|
310
|
+
* being rendered or updated.
|
|
311
|
+
* @param newChildElements - an array of new child elements to be reconciled with the existing children
|
|
312
|
+
* of the current fiber (wipFiber).
|
|
313
|
+
*/
|
|
314
|
+
function reconcileChildrenArray(wipFiber, newChildElements) {
|
|
315
|
+
const elements = createArrayOfChildren(newChildElements);
|
|
316
|
+
|
|
317
|
+
let index = 0;
|
|
318
|
+
|
|
319
|
+
let oldFiber = wipFiber.alternate ? wipFiber.alternate.child : null;
|
|
320
|
+
let newFiber = null;
|
|
321
|
+
while (index < elements.length || oldFiber != null) {
|
|
322
|
+
const prevFiber = newFiber;
|
|
323
|
+
|
|
324
|
+
const element = index < elements.length && elements[index];
|
|
325
|
+
|
|
326
|
+
const sameType = oldFiber && element && element.type == oldFiber.type;
|
|
327
|
+
|
|
328
|
+
if (sameType) {
|
|
329
|
+
newFiber = {
|
|
330
|
+
type: oldFiber.type,
|
|
331
|
+
tag: oldFiber.tag,
|
|
332
|
+
stateNode: oldFiber.stateNode,
|
|
333
|
+
props: element.props,
|
|
334
|
+
parent: wipFiber,
|
|
335
|
+
alternate: oldFiber,
|
|
336
|
+
partialState: oldFiber.partialState,
|
|
337
|
+
effectTag: UPDATE,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (element && !sameType) {
|
|
342
|
+
newFiber = {
|
|
343
|
+
type: element.type,
|
|
344
|
+
tag:
|
|
345
|
+
typeof element.type === "string" ? HOST_COMPONENT : CLASS_COMPONENT,
|
|
346
|
+
props: element.props,
|
|
347
|
+
parent: wipFiber,
|
|
348
|
+
effectTag: PLACEMENT,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (oldFiber && !sameType) {
|
|
353
|
+
oldFiber.effectTag = DELETION;
|
|
354
|
+
wipFiber.effects = wipFiber.effects || [];
|
|
355
|
+
|
|
356
|
+
wipFiber.effects.push(oldFiber);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (oldFiber) {
|
|
360
|
+
oldFiber = oldFiber.sibling;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (index == 0) {
|
|
364
|
+
wipFiber.child = newFiber;
|
|
365
|
+
} else if (prevFiber && element) {
|
|
366
|
+
prevFiber.sibling = newFiber;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
index++;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The function clones child fibers from a parent fiber.
|
|
375
|
+
* @param parentFiber - The parent fiber is an object that represents a component or element in the
|
|
376
|
+
* React tree. It contains information about the component or element, such as its type, props, and
|
|
377
|
+
* children. The function `cloneChildFibers` is used to clone the child fibers of the parent fiber.
|
|
378
|
+
* @returns If the `oldFiber` does not have a child, then nothing is returned. Otherwise, a new set of
|
|
379
|
+
* child fibers is created based on the `oldFiber` and attached to the `parentFiber`. No value is
|
|
380
|
+
* explicitly returned from the function.
|
|
381
|
+
*/
|
|
382
|
+
function cloneChildFibers(parentFiber) {
|
|
383
|
+
const oldFiber = parentFiber.alternate;
|
|
384
|
+
|
|
385
|
+
if (!oldFiber.child) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
let oldChild = oldFiber.child;
|
|
390
|
+
|
|
391
|
+
let prevChild = null;
|
|
392
|
+
|
|
393
|
+
while (oldChild) {
|
|
394
|
+
const newChild = {
|
|
395
|
+
type: oldChild.type,
|
|
396
|
+
tag: oldChild.tag,
|
|
397
|
+
stateNode: oldChild.stateNode,
|
|
398
|
+
props: oldChild.props,
|
|
399
|
+
partialState: oldChild.partialState,
|
|
400
|
+
alternate: oldChild,
|
|
401
|
+
parent: parentFiber,
|
|
402
|
+
};
|
|
403
|
+
if (prevChild) {
|
|
404
|
+
prevChild.sibling = newChild;
|
|
405
|
+
} else {
|
|
406
|
+
parentFiber.child = newChild;
|
|
407
|
+
}
|
|
408
|
+
prevChild = newChild;
|
|
409
|
+
oldChild = oldChild.sibling;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* The function completes work on a fiber and adds its effects to its parent's effects list or sets it
|
|
415
|
+
* as the pending commit.
|
|
416
|
+
* @param fiber - a fiber object representing a component or element in the React tree
|
|
417
|
+
*/
|
|
418
|
+
function completeWork(fiber) {
|
|
419
|
+
if (fiber.tag == CLASS_COMPONENT) {
|
|
420
|
+
fiber.stateNode.__fiber = fiber;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (fiber.parent) {
|
|
424
|
+
const childEffects = fiber.effects || [];
|
|
425
|
+
|
|
426
|
+
const thisEffect = fiber.effectTag != null ? [fiber] : [];
|
|
427
|
+
const parentEffects = fiber.parent.effects || [];
|
|
428
|
+
|
|
429
|
+
fiber.parent.effects = parentEffects.concat(childEffects, thisEffect);
|
|
430
|
+
} else {
|
|
431
|
+
pendingCommit = fiber;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* This function schedules an update for a class component with the given instance and partial state.
|
|
437
|
+
* @param instance - The instance parameter refers to an instance of a class component in React. It is
|
|
438
|
+
* used to identify which component needs to be updated with the new state.
|
|
439
|
+
* @param partialState - partialState is an object that contains the updated state values for a
|
|
440
|
+
* component. When a component's state changes, the partialState object is passed to the scheduleUpdate
|
|
441
|
+
* function to schedule a re-render of the component with the updated state values.
|
|
442
|
+
*/
|
|
443
|
+
export function scheduleUpdate(instance, partialState) {
|
|
444
|
+
schedule({
|
|
445
|
+
from: CLASS_COMPONENT,
|
|
446
|
+
instance: instance,
|
|
447
|
+
partialState: partialState,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let rootElement;
|
|
452
|
+
|
|
453
|
+
export const createRoot = (parentDom) => {
|
|
454
|
+
rootElement = parentDom;
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* The `render` function takes in elements and a parent DOM node, and schedules a reconciliation
|
|
459
|
+
* process to update the DOM with the new elements.
|
|
460
|
+
* @param elements - an array of elements to be rendered in the parent DOM.
|
|
461
|
+
* @param parentDom - parentDom is a reference to the DOM element where the rendered elements will be
|
|
462
|
+
* appended as children. It is the container element for the rendered components.
|
|
463
|
+
*/
|
|
464
|
+
|
|
465
|
+
export function rootClient(elements) {
|
|
466
|
+
workQueue.push({
|
|
467
|
+
from: "root",
|
|
468
|
+
dom: rootElement,
|
|
469
|
+
newProps: { children: elements },
|
|
470
|
+
});
|
|
471
|
+
requestIdleCallback(performWork);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export function render(elements, parentDom) {
|
|
475
|
+
workQueue.push({
|
|
476
|
+
from: HOST_ROOT,
|
|
477
|
+
dom: parentDom,
|
|
478
|
+
newProps: { children: elements },
|
|
479
|
+
});
|
|
480
|
+
requestIdleCallback(performWork);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* The function reconciles the differences between the previous and current instances of a component
|
|
485
|
+
* and updates the DOM accordingly.
|
|
486
|
+
* @param parentDom - The DOM element that serves as the parent container for the rendered component
|
|
487
|
+
* tree.
|
|
488
|
+
* @param instance - An object representing the current state of the component instance being
|
|
489
|
+
* reconciled. It contains information about the component's rendered DOM node, its element type, and
|
|
490
|
+
* its child instances. If this is the first time the component is being rendered, this parameter will
|
|
491
|
+
* be null.
|
|
492
|
+
* @param element - The element parameter represents the new element that needs to be rendered or
|
|
493
|
+
* updated in the DOM. It contains information about the type of element (e.g. div, span, custom
|
|
494
|
+
* component), its props (e.g. className, onClick), and its children (if any).
|
|
495
|
+
* @returns an instance object that represents the updated state of the component after reconciling the
|
|
496
|
+
* changes made to the virtual DOM.
|
|
497
|
+
*/
|
|
498
|
+
export function reconcile(parentDom, instance, element) {
|
|
499
|
+
if (instance === null) {
|
|
500
|
+
const newInstance = instantiate(element);
|
|
501
|
+
parentDom.appendChild(newInstance.dom);
|
|
502
|
+
return newInstance;
|
|
503
|
+
} else if (element == null) {
|
|
504
|
+
parentDom.removeChild(instance.dom);
|
|
505
|
+
return null;
|
|
506
|
+
} else if (instance.element.type !== element.type) {
|
|
507
|
+
const newInstance = instantiate(element);
|
|
508
|
+
parentDom.replaceChild(newInstance.dom, instance.dom);
|
|
509
|
+
return newInstance;
|
|
510
|
+
} else if (typeof element.type === "string") {
|
|
511
|
+
instance.childInstances = reconcileChildren(instance, element);
|
|
512
|
+
instance.element = element;
|
|
513
|
+
return instance;
|
|
514
|
+
} else {
|
|
515
|
+
instance.publicInstance.props = element.props;
|
|
516
|
+
const childElement = instance.publicInstance.render();
|
|
517
|
+
const oldChildInstance = instance.childInstance;
|
|
518
|
+
const childInstance = reconcile(parentDom, oldChildInstance, childElement);
|
|
519
|
+
instance.dom = childInstance.dom;
|
|
520
|
+
instance.childInstance = childInstance;
|
|
521
|
+
instance.element = element;
|
|
522
|
+
return instance;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* The function creates an instance of a DOM element or a custom component element and returns it.
|
|
528
|
+
* @param element - The element to be instantiated, which can be a DOM element or a custom component.
|
|
529
|
+
* It contains information about the type of the element (string for DOM elements, function for custom
|
|
530
|
+
* components) and its props (attributes and children).
|
|
531
|
+
* @returns The function `instantiate` returns an instance object that contains a reference to the
|
|
532
|
+
* corresponding DOM node, the element that was passed in, and an array of child instances. The exact
|
|
533
|
+
* properties of the instance object depend on whether the element is a DOM element or a custom
|
|
534
|
+
* component.
|
|
535
|
+
*/ function instantiate(element) {
|
|
536
|
+
const { type, props } = element;
|
|
537
|
+
const isDomElement = typeof type === "string";
|
|
538
|
+
if (isDomElement) {
|
|
539
|
+
const isTextElement = type === TEXT_ELEMENT;
|
|
540
|
+
const dom = isTextElement
|
|
541
|
+
? document.createTextNode("")
|
|
542
|
+
: document.createElement(type);
|
|
543
|
+
updateDomProperties(dom, [], props);
|
|
544
|
+
const childElements = props.children || [];
|
|
545
|
+
const childInstances = childElements.map(instantiate);
|
|
546
|
+
const childDoms = childInstances.map((childInstance) => childInstance.dom);
|
|
547
|
+
childDoms.forEach((childDom) => dom.appendChild(childDom));
|
|
548
|
+
const instance = {
|
|
549
|
+
dom,
|
|
550
|
+
element,
|
|
551
|
+
childInstances,
|
|
552
|
+
};
|
|
553
|
+
return instance;
|
|
554
|
+
} else {
|
|
555
|
+
const instance = {};
|
|
556
|
+
const publicInstance = createPublicInstance(element, instance);
|
|
557
|
+
const childElement = publicInstance.render();
|
|
558
|
+
const childInstance = instantiate(childElement);
|
|
559
|
+
const dom = childInstance.dom;
|
|
560
|
+
Object.assign(instance, {
|
|
561
|
+
dom,
|
|
562
|
+
element,
|
|
563
|
+
childInstance,
|
|
564
|
+
publicInstance,
|
|
565
|
+
});
|
|
566
|
+
return instance;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* The function creates a public instance of a given element with its corresponding props and internal
|
|
571
|
+
* instance.
|
|
572
|
+
* @param element - An object that represents a React element, typically created using JSX syntax or
|
|
573
|
+
* React.createElement() function.
|
|
574
|
+
* @param internalInstance - The internalInstance parameter is an object that represents the internal
|
|
575
|
+
* instance of a component. It may contain information such as the component's state, context, and
|
|
576
|
+
* lifecycle methods. This parameter is used to associate the public instance of a component with its
|
|
577
|
+
* internal instance.
|
|
578
|
+
* @returns a newly created public instance of a given element type with its props.
|
|
579
|
+
*/
|
|
580
|
+
function createPublicInstance(element, internalInstance) {
|
|
581
|
+
const { type, props } = element;
|
|
582
|
+
const publicInstance = new type(props);
|
|
583
|
+
publicInstance.__internalInstance = internalInstance;
|
|
584
|
+
return publicInstance;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* This function reconciles the child instances of a component with its new child elements.
|
|
589
|
+
* @param instance - an object representing the current instance of a component
|
|
590
|
+
* @param element - The element is a React element that represents the new version of the component
|
|
591
|
+
* being rendered. It contains information about the component's props and children.
|
|
592
|
+
* @returns an array of new child instances that have been reconciled with the given parent instance
|
|
593
|
+
* and child elements. The filter method is used to remove any falsy values from the array, such as
|
|
594
|
+
* null or undefined.
|
|
595
|
+
*/
|
|
596
|
+
function reconcileChildren(instance, element) {
|
|
597
|
+
const { dom, childInstances } = instance;
|
|
598
|
+
const nextChildElements = element.props.children || [];
|
|
599
|
+
|
|
600
|
+
const newChildInstances = [];
|
|
601
|
+
|
|
602
|
+
const count = Math.max(childInstances.length, nextChildElements.length);
|
|
603
|
+
|
|
604
|
+
for (let i = 0; i < count; i++) {
|
|
605
|
+
const childInstance = childInstances[i];
|
|
606
|
+
const childElement = nextChildElements[i];
|
|
607
|
+
const newChildInstance = reconcile(dom, childInstance, childElement);
|
|
608
|
+
newChildInstances.push(newChildInstance);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return newChildInstances.filter(Boolean);
|
|
612
|
+
}
|
package/lib/ryunix.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { render } from "./reconciler";
|
|
2
|
-
import { createElement } from "./element";
|
|
3
|
-
import { Component } from "./component";
|
|
4
|
-
import { useLoaded } from "./hooks";
|
|
5
|
-
export { createElement, render, Component, useLoaded };
|
|
6
|
-
export default {
|
|
7
|
-
render,
|
|
8
|
-
createElement,
|
|
9
|
-
Component,
|
|
10
|
-
|
|
1
|
+
import { render, rootClient, createRoot } from "./reconciler";
|
|
2
|
+
import { createElement } from "./element";
|
|
3
|
+
import { Component, Fragment } from "./component";
|
|
4
|
+
import { useLoaded } from "./hooks";
|
|
5
|
+
export { createElement, render, Component, useLoaded, Fragment };
|
|
6
|
+
export default {
|
|
7
|
+
render,
|
|
8
|
+
createElement,
|
|
9
|
+
Component,
|
|
10
|
+
Fragment,
|
|
11
|
+
rootClient,
|
|
12
|
+
createRoot,
|
|
13
|
+
};
|