First commit

Create basic render engine that only allows to add elements
This commit is contained in:
Alexander Navarro 2024-06-25 13:02:10 -04:00
parent 0d3eb3d40f
commit f411544fe9
Signed by untrusted user who does not match committer: anavarro
GPG key ID: 6426043E9FA3E3B5
14 changed files with 414 additions and 0 deletions

164
src/lib/YarJS.ts Normal file
View file

@ -0,0 +1,164 @@
import {
YarElement,
YarFiber,
YarHTMLTagName,
YarProps,
} from "./YarJs.interfaces";
export function createTextElement(text: string) {
return {
type: "TEXT",
props: {
nodeValue: text,
children: [],
},
};
}
export function createElement(
type: YarHTMLTagName,
props: YarProps,
...children: YarElement[]
) {
return {
type,
props: {
...props,
children: children.map((child) =>
typeof child === "object" ? child : createTextElement(child),
),
},
};
}
function createDom(fiber: YarFiber) {
const dom =
fiber.type === "TEXT"
? document.createTextNode("")
: document.createElement(fiber.type);
Object.keys(fiber.props)
.filter((key) => key !== "children")
.forEach((key) => {
// @ts-expect-error: I cannot figure it out how to properly type the dom props
dom[key] = fiber.props[key];
});
return dom;
}
let nextUnitOfWork: YarFiber | null | undefined = null;
let wipRoot: YarFiber | null | undefined = null;
function commitWork(fiber: YarFiber | null) {
if (!fiber) return;
const domParent = fiber.parent!.dom!;
domParent.appendChild(fiber.dom!);
commitWork(fiber.child);
commitWork(fiber.sibling);
}
function commitRoot() {
if (!wipRoot) return;
commitWork(wipRoot.child);
wipRoot = null;
}
function workLoop(deadline: IdleDeadline) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
shouldYield = deadline.timeRemaining() < 1;
}
if (!nextUnitOfWork && wipRoot) {
commitRoot();
}
requestIdleCallback(workLoop);
}
requestIdleCallback(workLoop);
function performUnitOfWork(fiber: YarFiber) {
// Process the Fiber Tree, this is a representation of the DOM
// Since this this process could be interrupted before the whole
// tree is processed, we just do the representation and computation here,
// then in "commitWork" we add the representation to the actual DOM
if (!fiber.dom) {
// Create the actual dom element
fiber.dom = createDom(fiber);
}
// Create a new fiber for each child
const elements = fiber.props.children;
let index = 0;
let prevSibling: YarFiber | null = null;
while (index < elements.length) {
const element = elements[index];
const newFiber = {
parent: fiber,
type: element.type,
props: element.props,
child: null,
sibling: null,
dom: null,
};
// Each Fiber only holds a reference to it's first child (in the Fiber Tree representation),
// so if is the first new Fiber we add it as a child to the parent,
// if is not, we add as a sibling of the last child
// Nonetheless, each fiber has a reference to it's parent
if (index === 0) {
fiber.child = newFiber;
} else if (prevSibling !== null) {
prevSibling.sibling = newFiber;
}
prevSibling = newFiber;
index++;
}
// Search for the new fiber that needs to be processed
if (fiber.child) {
// return the first child of the current fiber
return fiber.child;
}
let nextFiber = fiber;
while (nextFiber) {
if (nextFiber.sibling) {
// return the next sibling of the current fiber
return nextFiber.sibling;
}
// Reference the parent so we look at the "uncle" (parent sibling)
// in the next itereation
nextFiber = nextFiber.parent!;
}
}
export function render(element: React.JSX.Element, container: HTMLElement) {
wipRoot = {
type: "",
dom: container,
parent: null,
child: null,
sibling: null,
props: {
children: [element],
},
};
nextUnitOfWork = wipRoot;
}
export function Fragment() {}
export default { render, createElement, Fragment };

View file

@ -0,0 +1,18 @@
export type YarHTMLTagName = keyof React.JSX.IntrinsicElements | string;
export type YarProps = {
children: YarElement[];
[key: string]: unknown;
};
export interface YarElement {
type: YarHTMLTagName;
props: YarProps;
}
export interface YarFiber extends YarElement {
parent: null | YarFiber;
dom: null | HTMLElement | Text;
child: null | YarFiber;
sibling: null | YarFiber;
}