@unsetsoft/ryunixjs 1.0.0-alpha.0 → 1.0.2
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/README.md +19 -0
- package/dist/Ryunix.js +722 -172
- package/dist/cj/Ryunix.js +702 -0
- package/package.json +31 -7
- package/src/lib/commits.js +71 -0
- package/src/lib/components.js +41 -0
- package/src/lib/createElement.js +78 -0
- package/src/lib/dom.js +96 -0
- package/src/lib/effects.js +55 -0
- package/src/lib/hooks.js +202 -0
- package/src/lib/index.js +39 -0
- package/src/lib/reconciler.js +62 -0
- package/src/lib/render.js +34 -0
- package/src/lib/workers.js +61 -0
- package/src/main.js +15 -0
- package/src/utils/index.js +41 -0
- package/lib/component.js +0 -16
- package/lib/dom-utils.js +0 -25
- package/lib/element.js +0 -37
- package/lib/reconciler.js +0 -89
- package/lib/ryunix.js +0 -9
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { vars } from '../utils/index'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The function renders an element into a container using a work-in-progress root.
|
|
5
|
+
* @param element - The element parameter is the component or element that needs to be rendered in the
|
|
6
|
+
* container. It could be a Ryunix component or a DOM element.
|
|
7
|
+
* @param container - The container parameter is the DOM element where the rendered element will be
|
|
8
|
+
* appended to. this parameter is optional if you use createRoot().
|
|
9
|
+
*/
|
|
10
|
+
const render = (element, container) => {
|
|
11
|
+
vars.wipRoot = {
|
|
12
|
+
dom: vars.containerRoot || container,
|
|
13
|
+
props: {
|
|
14
|
+
children: [element],
|
|
15
|
+
},
|
|
16
|
+
alternate: vars.currentRoot,
|
|
17
|
+
}
|
|
18
|
+
vars.deletions = []
|
|
19
|
+
vars.nextUnitOfWork = vars.wipRoot
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @description The function creates a reference to a DOM element with the specified ID. This will be used to initialize the app.
|
|
24
|
+
* @example Ryunix.init("root") -> <div id="root" />
|
|
25
|
+
* @param root - The parameter "root" is the id of the HTML element that will serve as the container
|
|
26
|
+
* for the root element.
|
|
27
|
+
*/
|
|
28
|
+
const init = (root) => {
|
|
29
|
+
const rootElement = root || '__ryunix'
|
|
30
|
+
vars.containerRoot = document.getElementById(rootElement)
|
|
31
|
+
return this
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { render, init }
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { commitRoot } from './commits'
|
|
2
|
+
import { updateFunctionComponent, updateHostComponent } from './components'
|
|
3
|
+
import { vars } from '../utils/index'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This function uses requestIdleCallback to perform work on a fiber tree until it is complete or the
|
|
7
|
+
* browser needs to yield to other tasks.
|
|
8
|
+
* @param deadline - The `deadline` parameter is an object that represents the amount of time the
|
|
9
|
+
* browser has to perform work before it needs to handle other tasks. It has a `timeRemaining()` method
|
|
10
|
+
* that returns the amount of time remaining before the deadline is reached. The `shouldYield` variable
|
|
11
|
+
* is used to determine
|
|
12
|
+
*/
|
|
13
|
+
const workLoop = (deadline) => {
|
|
14
|
+
let shouldYield = false
|
|
15
|
+
while (vars.nextUnitOfWork && !shouldYield) {
|
|
16
|
+
vars.nextUnitOfWork = performUnitOfWork(vars.nextUnitOfWork)
|
|
17
|
+
shouldYield = deadline.timeRemaining() < 1
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!vars.nextUnitOfWork && vars.wipRoot) {
|
|
21
|
+
commitRoot()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
requestIdleCallback(workLoop)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
requestIdleCallback(workLoop)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The function performs a unit of work by updating either a function component or a host component and
|
|
31
|
+
* returns the next fiber to be processed.
|
|
32
|
+
* @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
|
|
33
|
+
* contains information about the component's type, props, and children, as well as pointers to its
|
|
34
|
+
* parent, child, and sibling fibers. The `performUnitOfWork` function takes a fiber as a parameter and
|
|
35
|
+
* performs work
|
|
36
|
+
* @returns The function `performUnitOfWork` returns the next fiber to be processed. If the current
|
|
37
|
+
* fiber has a child, it returns the child. Otherwise, it looks for the next sibling of the current
|
|
38
|
+
* fiber. If there are no more siblings, it goes up the tree to the parent and looks for the next
|
|
39
|
+
* sibling of the parent. The function returns `undefined` if there are no more fibers to process.
|
|
40
|
+
*/
|
|
41
|
+
const performUnitOfWork = (fiber) => {
|
|
42
|
+
const isFunctionComponent = fiber.type instanceof Function
|
|
43
|
+
if (isFunctionComponent) {
|
|
44
|
+
updateFunctionComponent(fiber)
|
|
45
|
+
} else {
|
|
46
|
+
updateHostComponent(fiber)
|
|
47
|
+
}
|
|
48
|
+
if (fiber.child) {
|
|
49
|
+
return fiber.child
|
|
50
|
+
}
|
|
51
|
+
let nextFiber = fiber
|
|
52
|
+
while (nextFiber) {
|
|
53
|
+
if (nextFiber.sibling) {
|
|
54
|
+
return nextFiber.sibling
|
|
55
|
+
}
|
|
56
|
+
nextFiber = nextFiber.parent
|
|
57
|
+
}
|
|
58
|
+
return undefined
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { performUnitOfWork, workLoop }
|
package/src/main.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const vars = {
|
|
2
|
+
containerRoot: undefined,
|
|
3
|
+
nextUnitOfWork: undefined,
|
|
4
|
+
currentRoot: undefined,
|
|
5
|
+
wipRoot: undefined,
|
|
6
|
+
deletions: undefined,
|
|
7
|
+
wipFiber: undefined,
|
|
8
|
+
hookIndex: undefined,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const reg = /[A-Z]/g
|
|
12
|
+
|
|
13
|
+
const RYUNIX_TYPES = Object.freeze({
|
|
14
|
+
TEXT_ELEMENT: Symbol('text.element'),
|
|
15
|
+
RYUNIX_EFFECT: Symbol('ryunix.effect'),
|
|
16
|
+
RYUNIX_MEMO: Symbol('ryunix.memo'),
|
|
17
|
+
RYUNIX_URL_QUERY: Symbol('ryunix.urlQuery'),
|
|
18
|
+
RYUNIX_REF: Symbol('ryunix.ref'),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const STRINGS = Object.freeze({
|
|
22
|
+
object: 'object',
|
|
23
|
+
function: 'function',
|
|
24
|
+
style: 'ryunix-style',
|
|
25
|
+
className: 'ryunix-class',
|
|
26
|
+
children: 'children',
|
|
27
|
+
boolean: 'boolean',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const OLD_STRINGS = Object.freeze({
|
|
31
|
+
style: 'style',
|
|
32
|
+
className: 'className',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const EFFECT_TAGS = Object.freeze({
|
|
36
|
+
PLACEMENT: Symbol(),
|
|
37
|
+
UPDATE: Symbol(),
|
|
38
|
+
DELETION: Symbol(),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export { vars, reg, RYUNIX_TYPES, EFFECT_TAGS, STRINGS, OLD_STRINGS }
|
package/lib/component.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { reconcile } from "./reconciler";
|
|
2
|
-
export class Component {
|
|
3
|
-
constructor(props) {
|
|
4
|
-
this.props = props;
|
|
5
|
-
this.state = this.state || {};
|
|
6
|
-
}
|
|
7
|
-
setState(partialState) {
|
|
8
|
-
this.state = Object.assign({}, this.state, partialState);
|
|
9
|
-
updateInstance(this.__internalInstance);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
function updateInstance(internalInstance) {
|
|
13
|
-
const parentDom = internalInstance.dom.parentNode;
|
|
14
|
-
const element = internalInstance.element;
|
|
15
|
-
reconcile(parentDom, internalInstance, element);
|
|
16
|
-
}
|
package/lib/dom-utils.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This function updates the properties and event listeners of a DOM element based on the previous and
|
|
3
|
-
* next props passed as arguments.
|
|
4
|
-
* @param dom - The DOM element that needs to be updated with new properties.
|
|
5
|
-
* @param prevProps - An object representing the previous properties of a DOM element.
|
|
6
|
-
* @param nextProps - An object containing the new props that need to be updated on the DOM element.
|
|
7
|
-
*/
|
|
8
|
-
export function updateDomProperties(dom, prevProps, nextProps) {
|
|
9
|
-
const isEvent = name => name.startsWith("on");
|
|
10
|
-
const isAttribute = name => !isEvent(name) && name != "children";
|
|
11
|
-
Object.keys(prevProps).filter(isEvent).forEach(name => {
|
|
12
|
-
const eventType = name.toLowerCase().substring(2);
|
|
13
|
-
dom.removeEventListener(eventType, prevProps[name]);
|
|
14
|
-
});
|
|
15
|
-
Object.keys(prevProps).filter(isAttribute).forEach(name => {
|
|
16
|
-
dom[name] = null;
|
|
17
|
-
});
|
|
18
|
-
Object.keys(nextProps).filter(isAttribute).forEach(name => {
|
|
19
|
-
dom[name] = nextProps[name];
|
|
20
|
-
});
|
|
21
|
-
Object.keys(nextProps).filter(isEvent).forEach(name => {
|
|
22
|
-
const eventType = name.toLowerCase().substring(2);
|
|
23
|
-
dom.addEventListener(eventType, nextProps[name]);
|
|
24
|
-
});
|
|
25
|
-
}
|
package/lib/element.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
const TEXT_ELEMENT = "TEXT";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This function creates a new element with the given type, configuration object, and children.
|
|
5
|
-
* @param type - The type of the element being created (e.g. "div", "span", "h1", etc.).
|
|
6
|
-
* @param configObject - The `configObject` parameter is an object that contains the properties and
|
|
7
|
-
* values for the element's attributes. These attributes can include things like `className`, `id`,
|
|
8
|
-
* `style`, and any other custom attributes that the user wants to add to the element.
|
|
9
|
-
* @param args - The `args` parameter is a rest parameter that allows the function to accept any number
|
|
10
|
-
* of additional arguments after the `configObject` parameter. These additional arguments are used as
|
|
11
|
-
* children elements for the created element.
|
|
12
|
-
* @returns An object with two properties: "type" and "props".
|
|
13
|
-
*/
|
|
14
|
-
export function createElement(type, configObject, ...args) {
|
|
15
|
-
const props = Object.assign({}, configObject);
|
|
16
|
-
const hasChildren = args.length > 0;
|
|
17
|
-
const nodeChildren = hasChildren ? [...args] : [];
|
|
18
|
-
props.children = nodeChildren.filter(Boolean).map(c => c instanceof Object ? c : createTextElement(c));
|
|
19
|
-
return {
|
|
20
|
-
type,
|
|
21
|
-
props
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The function creates a text element with a given node value and an empty array of children.
|
|
27
|
-
* @param nodeValue - The value of the text node that will be created.
|
|
28
|
-
* @returns The function `createTextElement` is returning an element object with a `nodeValue` property
|
|
29
|
-
* and an empty `children` array. This element object represents a text node in the virtual DOM.
|
|
30
|
-
*/
|
|
31
|
-
function createTextElement(nodeValue) {
|
|
32
|
-
return createElement(TEXT_ELEMENT, {
|
|
33
|
-
nodeValue,
|
|
34
|
-
children: []
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
export { TEXT_ELEMENT };
|
package/lib/reconciler.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { updateDomProperties } from "./dom-utils";
|
|
2
|
-
import { TEXT_ELEMENT } from "./element";
|
|
3
|
-
let rootInstance = null;
|
|
4
|
-
export function render(element, parentDom) {
|
|
5
|
-
const prevInstance = rootInstance;
|
|
6
|
-
const nextInstance = reconcile(parentDom, prevInstance, element);
|
|
7
|
-
rootInstance = nextInstance;
|
|
8
|
-
}
|
|
9
|
-
export function reconcile(parentDom, instance, element) {
|
|
10
|
-
if (instance === null) {
|
|
11
|
-
const newInstance = instantiate(element);
|
|
12
|
-
parentDom.appendChild(newInstance.dom);
|
|
13
|
-
return newInstance;
|
|
14
|
-
} else if (element == null) {
|
|
15
|
-
parentDom.removeChild(instance.dom);
|
|
16
|
-
return null;
|
|
17
|
-
} else if (instance.element.type !== element.type) {
|
|
18
|
-
const newInstance = instantiate(element);
|
|
19
|
-
parentDom.replaceChild(newInstance.dom, instance.dom);
|
|
20
|
-
return newInstance;
|
|
21
|
-
} else if (typeof element.type === "string") {
|
|
22
|
-
instance.childInstances = reconcileChildren(instance, element);
|
|
23
|
-
instance.element = element;
|
|
24
|
-
return instance;
|
|
25
|
-
} else {
|
|
26
|
-
instance.publicInstance.props = element.props;
|
|
27
|
-
const childElement = instance.publicInstance.render();
|
|
28
|
-
const oldChildInstance = instance.childInstance;
|
|
29
|
-
const childInstance = reconcile(parentDom, oldChildInstance, childElement);
|
|
30
|
-
instance.dom = childInstance.dom;
|
|
31
|
-
instance.childInstance = childInstance;
|
|
32
|
-
instance.element = element;
|
|
33
|
-
return instance;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function instantiate(element) {
|
|
37
|
-
const { type, props } = element;
|
|
38
|
-
const isDomElement = typeof type === "string";
|
|
39
|
-
if (isDomElement) {
|
|
40
|
-
const isTextElement = type === TEXT_ELEMENT;
|
|
41
|
-
const dom = isTextElement
|
|
42
|
-
? document.createTextNode("")
|
|
43
|
-
: document.createElement(type);
|
|
44
|
-
updateDomProperties(dom, [], props);
|
|
45
|
-
const childElements = props.children || [];
|
|
46
|
-
const childInstances = childElements.map(instantiate);
|
|
47
|
-
const childDoms = childInstances.map((childInstance) => childInstance.dom);
|
|
48
|
-
childDoms.forEach((childDom) => dom.appendChild(childDom));
|
|
49
|
-
const instance = {
|
|
50
|
-
dom,
|
|
51
|
-
element,
|
|
52
|
-
childInstances,
|
|
53
|
-
};
|
|
54
|
-
return instance;
|
|
55
|
-
} else {
|
|
56
|
-
const instance = {};
|
|
57
|
-
const publicInstance = createPublicInstance(element, instance);
|
|
58
|
-
const childElement = publicInstance.render();
|
|
59
|
-
const childInstance = instantiate(childElement);
|
|
60
|
-
const dom = childInstance.dom;
|
|
61
|
-
Object.assign(instance, {
|
|
62
|
-
dom,
|
|
63
|
-
element,
|
|
64
|
-
childInstance,
|
|
65
|
-
publicInstance,
|
|
66
|
-
});
|
|
67
|
-
return instance;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function createPublicInstance(element, internalInstance) {
|
|
71
|
-
const { type, props } = element;
|
|
72
|
-
const publicInstance = new type(props);
|
|
73
|
-
publicInstance.__internalInstance = internalInstance;
|
|
74
|
-
return publicInstance;
|
|
75
|
-
}
|
|
76
|
-
function reconcileChildren(instance, element) {
|
|
77
|
-
const dom = instance.dom;
|
|
78
|
-
const childInstances = instance.childInstances;
|
|
79
|
-
const nextChildElements = element.props.children || [];
|
|
80
|
-
const newChildInstances = [];
|
|
81
|
-
const count = Math.max(childInstances.length, nextChildElements.length);
|
|
82
|
-
for (let i = 0; i < count; i++) {
|
|
83
|
-
const childInstance = childInstances[i];
|
|
84
|
-
const childElement = nextChildElements[i];
|
|
85
|
-
const newChildInstance = reconcile(dom, childInstance, childElement);
|
|
86
|
-
newChildInstances.push(newChildInstance);
|
|
87
|
-
}
|
|
88
|
-
return newChildInstances.filter((instance) => instance != null);
|
|
89
|
-
}
|
package/lib/ryunix.js
DELETED