improved cr by very basic placement commit
This commit is contained in:
parent
98b0752441
commit
f4427f7b3f
|
|
@ -10,6 +10,18 @@
|
|||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,44 @@
|
|||
interface FiberNode {
|
||||
child?: FiberNode;
|
||||
sibling?: FiberNode;
|
||||
parent?: FiberNode;
|
||||
}
|
||||
import { domFiber, ElementSymbol, FiberNodeSymbol } from "./symbols";
|
||||
import {
|
||||
EffectTag,
|
||||
isFunctionFiber,
|
||||
isHostFiber,
|
||||
type Child,
|
||||
type FiberNode,
|
||||
type FunctionFiber,
|
||||
type HostFiber,
|
||||
type JSXElement,
|
||||
type RootFiber,
|
||||
} from "./types";
|
||||
|
||||
let unitOfWork: FiberNode | null = null;
|
||||
let wipFiber: FiberNode;
|
||||
let hookIndex: number;
|
||||
let effectFiber: FiberNode | null = null;
|
||||
let wipRoot: RootFiber | null = null;
|
||||
|
||||
const textElementType = "$$TEXT_ELEMENT$$";
|
||||
const createTextElement = (text: string): JSXElement => ({
|
||||
$$typeof: ElementSymbol,
|
||||
type: textElementType,
|
||||
props: {
|
||||
nodeValue: text,
|
||||
children: [],
|
||||
},
|
||||
});
|
||||
|
||||
function performUnitOfWork(fiber: FiberNode) {
|
||||
console.log("performUnitOfWork", fiber);
|
||||
if (isFunctionFiber(fiber)) {
|
||||
updateFunctionComponent(fiber);
|
||||
} else if (isHostFiber(fiber)) {
|
||||
updateHostComponent(fiber);
|
||||
}
|
||||
|
||||
if (fiber.child) {
|
||||
return fiber.child;
|
||||
}
|
||||
|
||||
let nextFiber: FiberNode | undefined = fiber;
|
||||
let nextFiber: FiberNode | RootFiber | null = fiber;
|
||||
while (nextFiber) {
|
||||
if (nextFiber.sibling) {
|
||||
return nextFiber.sibling;
|
||||
|
|
@ -31,19 +56,266 @@ function workLoop(deadline: IdleDeadline) {
|
|||
|
||||
if (unitOfWork) {
|
||||
requestIdleCallback(workLoop);
|
||||
} else {
|
||||
// Commit all the changes that have accumulated.
|
||||
} else if (wipRoot) {
|
||||
commitRoot();
|
||||
}
|
||||
}
|
||||
|
||||
function linkParents(fiber: FiberNode, parent?: FiberNode) {
|
||||
fiber.parent = parent;
|
||||
if (fiber.child) {
|
||||
linkParents(fiber.child, fiber);
|
||||
function createFiberFromElement(element: JSXElement): FiberNode {
|
||||
return {
|
||||
$$typeof: FiberNodeSymbol,
|
||||
type: element.type,
|
||||
key: element.key ?? null,
|
||||
child: null,
|
||||
sibling: null,
|
||||
parent: null,
|
||||
index: 0,
|
||||
dom: null,
|
||||
hooks: [],
|
||||
pendingProps: element.props,
|
||||
effectTag: EffectTag.NoEffect,
|
||||
alternate: null,
|
||||
nextEffect: null,
|
||||
};
|
||||
}
|
||||
if (fiber.sibling) {
|
||||
linkParents(fiber.sibling, fiber);
|
||||
|
||||
function updateFunctionComponent(fiber: FunctionFiber) {
|
||||
wipFiber = fiber;
|
||||
hookIndex = 0;
|
||||
wipFiber.hooks = [];
|
||||
wipFiber.effectTag = EffectTag.NoEffect;
|
||||
const children = [fiber.type(fiber.pendingProps)];
|
||||
reconcileChildren(fiber, children);
|
||||
}
|
||||
|
||||
function updateHostComponent(fiber: HostFiber) {
|
||||
wipFiber = fiber;
|
||||
reconcileChildren(fiber, fiber.pendingProps.children);
|
||||
}
|
||||
|
||||
function pushEffect(fiber: FiberNode) {
|
||||
if (!wipRoot) {
|
||||
throw "pushEffect() must be called inside a root";
|
||||
}
|
||||
if (effectFiber) {
|
||||
effectFiber.nextEffect = fiber;
|
||||
effectFiber = fiber;
|
||||
} else {
|
||||
effectFiber = fiber;
|
||||
// @ts-ignore // HACK: Only for debugging
|
||||
window.effectFiber = fiber;
|
||||
unitOfWork = fiber;
|
||||
}
|
||||
if (!wipRoot.firstEffect) {
|
||||
wipRoot.firstEffect = fiber;
|
||||
}
|
||||
wipRoot.lastEffect = fiber;
|
||||
}
|
||||
|
||||
function reconcileChildren(wipFiber: FiberNode, children: Child[] = []) {
|
||||
let index = 0;
|
||||
let oldFiber = wipFiber.alternate?.child;
|
||||
let prevSibling: FiberNode | null = null;
|
||||
const elements = children
|
||||
.filter((child) => child !== null && child !== undefined)
|
||||
.map((child) => {
|
||||
if (typeof child === "string") {
|
||||
return createTextElement(child);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
|
||||
while (index < elements.length || oldFiber) {
|
||||
const element = elements[index];
|
||||
let newFiber: FiberNode | null = null;
|
||||
|
||||
const isSameType = oldFiber && element && element.type === oldFiber.type;
|
||||
if (isSameType && oldFiber) {
|
||||
newFiber = {
|
||||
$$typeof: FiberNodeSymbol,
|
||||
type: oldFiber.type,
|
||||
key: element.key ?? null,
|
||||
child: null,
|
||||
sibling: null,
|
||||
parent: wipFiber,
|
||||
index: 0,
|
||||
dom: oldFiber.dom,
|
||||
hooks: [],
|
||||
pendingProps: element.props,
|
||||
effectTag: EffectTag.Update,
|
||||
alternate: oldFiber,
|
||||
nextEffect: null,
|
||||
};
|
||||
pushEffect(newFiber);
|
||||
}
|
||||
|
||||
if (element && !isSameType) {
|
||||
newFiber = {
|
||||
$$typeof: FiberNodeSymbol,
|
||||
type: element.type,
|
||||
key: element.key ?? null,
|
||||
child: null,
|
||||
sibling: null,
|
||||
parent: wipFiber,
|
||||
index: 0,
|
||||
dom: null,
|
||||
hooks: [],
|
||||
pendingProps: element.props,
|
||||
effectTag: EffectTag.Placement,
|
||||
alternate: null,
|
||||
nextEffect: null,
|
||||
};
|
||||
pushEffect(newFiber);
|
||||
}
|
||||
|
||||
if (oldFiber && !isSameType) {
|
||||
oldFiber.effectTag = EffectTag.Deletion;
|
||||
pushEffect(oldFiber);
|
||||
}
|
||||
|
||||
if (oldFiber) {
|
||||
oldFiber = oldFiber.sibling;
|
||||
}
|
||||
|
||||
if (index === 0) {
|
||||
wipFiber.child = newFiber;
|
||||
} else if (element && prevSibling) {
|
||||
prevSibling.sibling = newFiber;
|
||||
}
|
||||
|
||||
prevSibling = newFiber;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
requestIdleCallback(workLoop);
|
||||
|
||||
// TODO: Maybe store next parent HostFiber
|
||||
function findDomParent(fiber: FiberNode) {
|
||||
let parent = fiber.parent;
|
||||
while (parent) {
|
||||
if (parent.dom) {
|
||||
return parent;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function commitRoot() {
|
||||
if (!wipRoot) return;
|
||||
effectFiber = wipRoot.firstEffect;
|
||||
while (effectFiber) {
|
||||
// Do effect
|
||||
if (effectFiber.effectTag & EffectTag.Placement) {
|
||||
let dom: Node;
|
||||
if (effectFiber.type === textElementType) {
|
||||
dom = document.createTextNode(effectFiber.pendingProps.nodeValue);
|
||||
} else {
|
||||
dom = document.createElement(effectFiber.type as string);
|
||||
// TODO: Set props
|
||||
// TODO: Maybe add event section
|
||||
// TODO: Maybe add ref in far future might consider a general on mount and on unmount event
|
||||
}
|
||||
effectFiber.dom = dom;
|
||||
const parent = findDomParent(effectFiber);
|
||||
if (parent?.dom) {
|
||||
console.log("Appending", dom, "into", parent.dom);
|
||||
parent.dom.appendChild(dom);
|
||||
} else {
|
||||
throw "Can't commit without parent";
|
||||
}
|
||||
}
|
||||
effectFiber = effectFiber.nextEffect;
|
||||
}
|
||||
wipRoot = null;
|
||||
}
|
||||
|
||||
const events: (keyof GlobalEventHandlersEventMap)[] = [
|
||||
"click",
|
||||
"mousemove",
|
||||
"mouseenter",
|
||||
"mouseleave",
|
||||
"mouseover",
|
||||
"mouseout",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"pointermove",
|
||||
"pointerenter",
|
||||
"pointerleave",
|
||||
"pointerover",
|
||||
"pointerout",
|
||||
"blur",
|
||||
"focus",
|
||||
"drag",
|
||||
"dragend",
|
||||
"dragenter",
|
||||
"dragleave",
|
||||
"dragover",
|
||||
"dragstart",
|
||||
"drop",
|
||||
"touchcancel",
|
||||
"touchend",
|
||||
"touchmove",
|
||||
"touchstart",
|
||||
];
|
||||
const getEventProp = (eventType: string) => {
|
||||
return `on${eventType.substring(0, 1).toUpperCase()}${eventType.substring(1)}`;
|
||||
};
|
||||
function handleEvent(event: Event, root: RootFiber) {
|
||||
const target = event.target as HTMLElement;
|
||||
const type = event.type;
|
||||
let node: FiberNode | RootFiber | null = target[domFiber] ?? null;
|
||||
let propagationStopped = false;
|
||||
const ogStopPropagation = event.stopPropagation;
|
||||
event.stopPropagation = () => {
|
||||
propagationStopped = true;
|
||||
ogStopPropagation.call(event);
|
||||
};
|
||||
do {
|
||||
node?.pendingProps[getEventProp(type)]?.(event);
|
||||
if (propagationStopped) {
|
||||
break;
|
||||
}
|
||||
if (event.bubbles) {
|
||||
node = node?.parent ?? null;
|
||||
}
|
||||
} while (node?.dom !== root.dom);
|
||||
}
|
||||
|
||||
export function createRootFiber(root: HTMLElement): RootFiber {
|
||||
const rootFiber: RootFiber = {
|
||||
$$typeof: FiberNodeSymbol,
|
||||
type: undefined,
|
||||
key: null,
|
||||
child: null,
|
||||
sibling: null,
|
||||
parent: null,
|
||||
index: 0,
|
||||
dom: root,
|
||||
hooks: [],
|
||||
pendingProps: null,
|
||||
effectTag: EffectTag.NoEffect,
|
||||
alternate: null,
|
||||
nextEffect: null,
|
||||
isRoot: true,
|
||||
firstEffect: null,
|
||||
lastEffect: null,
|
||||
};
|
||||
for (const event of events) {
|
||||
rootFiber.dom.addEventListener(event, (event) =>
|
||||
handleEvent(event, rootFiber),
|
||||
);
|
||||
}
|
||||
return rootFiber;
|
||||
}
|
||||
|
||||
export function render(rootFiber: RootFiber, element: JSXElement) {
|
||||
wipRoot = rootFiber;
|
||||
const fiber = createFiberFromElement(element);
|
||||
fiber.parent = rootFiber;
|
||||
rootFiber.child = fiber;
|
||||
wipRoot.child = fiber;
|
||||
unitOfWork = fiber;
|
||||
console.log(fiber);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
namespace JSX {
|
||||
interface FC {
|
||||
(props: any): JSX.Element | string | null;
|
||||
}
|
||||
type Children =
|
||||
| JSX.Element
|
||||
| (string | JSX.Element | JSX.Element[])[]
|
||||
| string
|
||||
| null
|
||||
| undefined;
|
||||
type BaseElementPropsWithoutChildren = {
|
||||
className?: string;
|
||||
id?: string;
|
||||
tabindex?: number | string;
|
||||
style?: import("./types").Styles;
|
||||
[`data-${string}`]?: string | boolean;
|
||||
onClick?: (event: MouseEvent) => void;
|
||||
} & Partial<ARIAMixin>;
|
||||
type BaseElementProps = BaseElementPropsWithoutChildren & {
|
||||
children?: JSX.Children;
|
||||
};
|
||||
type SvgProps = BaseElementProps & {
|
||||
["xml:lang"]?: string;
|
||||
["xml:space"]?: string;
|
||||
xmlns?: string;
|
||||
// XLink attributes
|
||||
["xlink:hrefDeprecated"]?: string;
|
||||
["xlink:type"]?: string;
|
||||
["xlink:role"]?: string;
|
||||
["xlink:arcrole"]?: string;
|
||||
["xlink:title"]?: string;
|
||||
["xlink:show"]?: string;
|
||||
["xlink:actuate"]?: string;
|
||||
// Presentation attributes
|
||||
["alignment-baseline"]?: string;
|
||||
["baseline-shift"]?: string;
|
||||
["clip"]?: string;
|
||||
["clipPath"]?: string;
|
||||
["clipRule"]?: string;
|
||||
["color"]?: string;
|
||||
["colorInterpolation"]?: string;
|
||||
["colorInterpolationFilters"]?: string;
|
||||
["cursor"]?: string;
|
||||
["cx"]?: string;
|
||||
["cy"]?: string;
|
||||
["d"]?: string;
|
||||
["direction"]?: string;
|
||||
["display"]?: string;
|
||||
["dominantBaseline"]?: string;
|
||||
["fill"]?: string;
|
||||
["fillOpacity"]?: string;
|
||||
["fillRule"]?: string;
|
||||
["filter"]?: string;
|
||||
["floodColor"]?: string;
|
||||
["floodOpacity"]?: string;
|
||||
["fontFamily"]?: string;
|
||||
["fontSize"]?: string;
|
||||
["fontSize-adjust"]?: string;
|
||||
["fontStretch"]?: string;
|
||||
["fontStyle"]?: string;
|
||||
["fontVariant"]?: string;
|
||||
["fontWeight"]?: string;
|
||||
["glyphOrientation-horizontal"]?: string;
|
||||
["glyphOrientation-vertical"]?: string;
|
||||
["height"]?: string;
|
||||
["imageRendering"]?: string;
|
||||
["letterSpacing"]?: string;
|
||||
["lightingColor"]?: string;
|
||||
["markerEnd"]?: string;
|
||||
["markerMid"]?: string;
|
||||
["markerStart"]?: string;
|
||||
["mask"]?: string;
|
||||
["maskType"]?: string;
|
||||
["opacity"]?: string;
|
||||
["overflow"]?: string;
|
||||
["pointerEvents"]?: string;
|
||||
["r"]?: string;
|
||||
["rx"]?: string;
|
||||
["ry"]?: string;
|
||||
["shapeRendering"]?: string;
|
||||
["stopColor"]?: string;
|
||||
["stopOpacity"]?: string;
|
||||
["stroke"]?: string;
|
||||
["strokeDasharray"]?: string;
|
||||
["strokeDashoffset"]?: string;
|
||||
["strokeLinecap"]?: string;
|
||||
["strokeLinejoin"]?: string;
|
||||
["strokeMiterlimit"]?: string;
|
||||
["strokeOpacity"]?: string;
|
||||
["strokeWidth"]?: string;
|
||||
["textAnchor"]?: string;
|
||||
["textDecoration"]?: string;
|
||||
["textOverflow"]?: string;
|
||||
["textRendering"]?: string;
|
||||
["transform"]?: string;
|
||||
["transformOrigin"]?: string;
|
||||
["unicodeBidi"]?: string;
|
||||
["vectorEffect"]?: string;
|
||||
["visibility"]?: string;
|
||||
["whiteSpace"]?: string;
|
||||
["width"]?: string;
|
||||
["wordSpacing"]?: string;
|
||||
["writingMode"]?: string;
|
||||
["x"]?: string;
|
||||
["y"]?: string;
|
||||
viewbox?: string;
|
||||
};
|
||||
interface IntrinsicElements {
|
||||
a: BaseElementProps & {
|
||||
href?: string;
|
||||
target?: HTMLAnchorElement["target"];
|
||||
};
|
||||
img: BaseElementProps & {
|
||||
src: string;
|
||||
alt?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
// Void IntrinsicElements
|
||||
// https://github.com/wooorm/html-void-elements
|
||||
area: BaseElementPropsWithoutChildren;
|
||||
base: BaseElementPropsWithoutChildren;
|
||||
basefont: BaseElementPropsWithoutChildren;
|
||||
bgsound: BaseElementPropsWithoutChildren;
|
||||
br: BaseElementPropsWithoutChildren;
|
||||
col: BaseElementPropsWithoutChildren;
|
||||
command: BaseElementPropsWithoutChildren;
|
||||
embed: BaseElementPropsWithoutChildren;
|
||||
frame: BaseElementPropsWithoutChildren;
|
||||
hr: BaseElementPropsWithoutChildren;
|
||||
image: BaseElementPropsWithoutChildren;
|
||||
img: BaseElementPropsWithoutChildren;
|
||||
input: BaseElementPropsWithoutChildren;
|
||||
keygen: BaseElementPropsWithoutChildren;
|
||||
link: BaseElementPropsWithoutChildren;
|
||||
meta: BaseElementPropsWithoutChildren;
|
||||
param: BaseElementPropsWithoutChildren;
|
||||
source: BaseElementPropsWithoutChildren;
|
||||
track: BaseElementPropsWithoutChildren;
|
||||
wbr: BaseElementPropsWithoutChildren;
|
||||
svg: SvgProps;
|
||||
path: SvgProps;
|
||||
g: SvgProps;
|
||||
[tagName: string]: BaseElementProps;
|
||||
}
|
||||
interface Element {
|
||||
$$typeof: symbol;
|
||||
type: string | FC | null;
|
||||
key?: string;
|
||||
ref?: string;
|
||||
children?: JSX.Children;
|
||||
props: any;
|
||||
}
|
||||
interface ElementChildrenAttribute {
|
||||
children: {};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { createRootFiber, render } from "./cr";
|
||||
|
||||
const App = () => {
|
||||
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div>Hello world</div>
|
||||
<ul>
|
||||
{items.map((item) => (
|
||||
<li key={item}>{String(item)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
console.log("foo");
|
||||
const root = createRootFiber(document.getElementById("app")!);
|
||||
console.log(<div>Hello World</div>);
|
||||
render(root, <App />);
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { jsx, Fragment } from "./jsx-runtime";
|
||||
export const jsxDEV = jsx;
|
||||
export { jsx, Fragment };
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { ElementSymbol } from "~/symbols";
|
||||
import type { FC, JSXElement } from "~/types";
|
||||
|
||||
export const jsx = function (type: string | FC, fullProps: any): JSXElement {
|
||||
const { key, children, ref, ...props } = fullProps;
|
||||
return {
|
||||
$$typeof: ElementSymbol,
|
||||
type,
|
||||
props: {
|
||||
...props,
|
||||
children: Array.isArray(children) ? children : [children],
|
||||
},
|
||||
key: key,
|
||||
ref: ref,
|
||||
};
|
||||
};
|
||||
|
||||
export const Fragment = function (fullProps: any): JSXElement {
|
||||
const { children, ...props } = fullProps;
|
||||
return {
|
||||
$$typeof: ElementSymbol,
|
||||
type: null,
|
||||
props,
|
||||
key: props.key,
|
||||
ref: props.ref,
|
||||
children,
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export const ElementSymbol = Symbol.for("cr.element");
|
||||
export const FragmentSymbol = Symbol.for("cr.fragment");
|
||||
export const FiberNodeSymbol = Symbol.for("cr.fiber-node");
|
||||
export type DomFiberKey = "__cr_fiber";
|
||||
export type DomPropsKey = "__cr_props";
|
||||
export const domFiber = "__cr_fiber" satisfies DomFiberKey;
|
||||
export const domProps = "__cr_props" satisfies DomPropsKey;
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import { domProps, ElementSymbol, FiberNodeSymbol, domFiber } from "./symbols";
|
||||
|
||||
export type FC = (props: any) => JSXElement | string | null;
|
||||
|
||||
export type Child = JSXElement | string | null | undefined;
|
||||
export type Children = Child | Child[];
|
||||
export type ElementType = string | FC | null;
|
||||
|
||||
export interface JSXElement {
|
||||
$$typeof: typeof ElementSymbol;
|
||||
type: ElementType;
|
||||
key?: string;
|
||||
ref?: string;
|
||||
props: any;
|
||||
children?: Children;
|
||||
}
|
||||
|
||||
export enum EffectTag {
|
||||
NoEffect = 0x0,
|
||||
Placement = 0x1,
|
||||
Update = 0x2,
|
||||
Deletion = 0x4,
|
||||
Hydration = 0x8,
|
||||
Passive = 0x10,
|
||||
Layout = 0x20,
|
||||
}
|
||||
|
||||
export const StateHook = Symbol.for("cr.state-hook");
|
||||
export const EffectHook = Symbol.for("cr.effect-hook");
|
||||
type Hook =
|
||||
| {
|
||||
type: typeof StateHook;
|
||||
}
|
||||
| {
|
||||
type: typeof EffectHook;
|
||||
};
|
||||
|
||||
export interface FiberNode {
|
||||
$$typeof: typeof FiberNodeSymbol;
|
||||
// Identity
|
||||
type: ElementType;
|
||||
key: string | null;
|
||||
isRoot?: boolean;
|
||||
|
||||
child: FiberNode | null;
|
||||
sibling: FiberNode | null;
|
||||
parent: FiberNode | RootFiber | null;
|
||||
index: number;
|
||||
|
||||
// State / Instance
|
||||
dom: Node | null;
|
||||
hooks: Hook[];
|
||||
pendingProps: any;
|
||||
|
||||
effectTag: EffectTag;
|
||||
|
||||
// Used for double buffering
|
||||
alternate: FiberNode | null;
|
||||
nextEffect: FiberNode | null;
|
||||
}
|
||||
|
||||
export type FunctionFiber = FiberNode & {
|
||||
type: FC;
|
||||
};
|
||||
|
||||
export type HostFiber = FiberNode & {
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type RootFiber = Omit<FiberNode, "type"> & {
|
||||
type: undefined;
|
||||
dom: HTMLElement;
|
||||
isRoot: true;
|
||||
firstEffect: FiberNode | null;
|
||||
lastEffect: FiberNode | null;
|
||||
};
|
||||
|
||||
export const isFiberNode = (fiber: any): fiber is FiberNode =>
|
||||
fiber && fiber.$$typeof === FiberNodeSymbol;
|
||||
|
||||
export const isElement = (fiber: any): fiber is JSXElement =>
|
||||
fiber && fiber.$$typeof === ElementSymbol;
|
||||
|
||||
export const isFunctionFiber = (fiber: any): fiber is FunctionFiber =>
|
||||
fiber.type instanceof Function;
|
||||
|
||||
export const isHostFiber = (fiber: any): fiber is HostFiber =>
|
||||
typeof fiber.type === "string";
|
||||
|
||||
declare global {
|
||||
interface HTMLElement {
|
||||
[domFiber]?: FiberNode;
|
||||
[domProps]?: any;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "~/runtime",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
|
|
@ -24,6 +25,9 @@
|
|||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue