Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 23 additions & 53 deletions src/MiniReact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,28 @@ export type {
FunctionalComponent,
ElementType,
MiniReactElement,
} from "./core/types";

// Hooks
export {
useState,
useEffect,
useReducer,
useRef,
useMemo,
useCallback,
} from "./hooks";
export type {
UseStateHook,
UseEffectHook,
UseReducerHook,
UseRefHook,
EffectCallback,
DependencyList,
Reducer,
MutableRefObject,
} from "./hooks/types";
} from "./core/types";

// Fiber-based hooks
export {
useStateFiber as useState,
useEffectFiber as useEffect,
useReducerFiber as useReducer,
useRefFiber as useRef,
useMemoFiber as useMemo,
useCallbackFiber as useCallback,
} from "./fiber";

// Context
export { createContext, useContext } from "./context";
export type { MiniReactContext } from "./context/types";

// Fragments
export { Fragment } from "./fragments";
export { FRAGMENT as Fragment } from "./core/types";

// Portals
export { createPortal } from "./portals";
Expand All @@ -57,18 +51,17 @@ export { jsx, jsxs, jsxDEV } from "./jsx-runtime";
export type { SyntheticEvent } from "./events/types";

import { createContext, useContext } from "./context";
// Higher-order components
import { createElement, render } from "./core";
import type { FunctionalComponent, VDOMInstance } from "./core/types";
import { Fragment } from "./fragments";
import { FRAGMENT as Fragment } from "./core/types";
import type { FunctionalComponent } from "./core/types";
import {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
useState,
} from "./hooks";
useCallbackFiber as useCallback,
useEffectFiber as useEffect,
useMemoFiber as useMemo,
useReducerFiber as useReducer,
useRefFiber as useRef,
useStateFiber as useState,
} from "./fiber";
import { jsx, jsxDEV, jsxs } from "./jsx-runtime";
import {
getPerformanceMetrics,
Expand All @@ -90,7 +83,7 @@ export function memo<P extends Record<string, unknown>>(
};

// Store the comparison function and original component for reconciliation
(MemoizedComponent as unknown as Record<string, unknown>).__memo = {
(MemoizedComponent as unknown as Record<string, unknown>)["__memo"] = {
Component,
areEqual: areEqual || shallowEqual,
};
Expand Down Expand Up @@ -122,30 +115,7 @@ function shallowEqual<P extends Record<string, unknown>>(
return true;
}

import {
popContextValues,
pushContextValues,
setContextRenderInstance,
} from "./context";
// Set up cross-module dependencies
import { scheduleEffect, setHookContext } from "./hooks";
import {
setContextHooks,
setHookContext as setReconcilerHookContext,
setScheduleEffect,
} from "./reconciler";

// Initialize cross-module dependencies
setScheduleEffect(scheduleEffect);
setContextHooks(pushContextValues, popContextValues);
// Connect context render instance to hooks render instance
setReconcilerHookContext((instance: VDOMInstance | null) => {
setHookContext(instance);
setContextRenderInstance(instance);
});

// Export internal utilities for advanced usage
export { setHookContext } from "./hooks";
// Export event system for advanced usage
export { eventSystem } from "./events";

// Constants
Expand Down
146 changes: 33 additions & 113 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@
/* Core Functionality */
/* ****************** */

import { eventSystem } from "../events";
import { trackRenderEnd, trackRenderStart } from "../performance";
import { reconcile } from "../reconciler";
import {
type FiberRoot,
createRoot as createFiberRoot,
flushSync,
updateContainer,
} from "../fiber";
import {
type AnyMiniReactElement,
type ElementType,
type JSXElementType,
PORTAL,
TEXT_ELEMENT,
type VDOMInstance,
} from "./types";

// Store root instances for each container
const rootInstances = new Map<HTMLElement, VDOMInstance | null>();
// Store original root elements for re-rendering
const rootElements = new Map<HTMLElement, AnyMiniReactElement | null>();
// Store fiber roots for each container
const fiberRoots = new Map<HTMLElement, FiberRoot>();

/**
* Creates a MiniReact element (virtual DOM node)
Expand Down Expand Up @@ -59,128 +58,49 @@ export function createElement(
}

/**
* Renders a MiniReact element into a container using the reconciler
* Renders a MiniReact element into a container using the fiber reconciler
* @param element The element to render (can be null to clear)
* @param containerNode The container DOM node
*/
export function render(
element: AnyMiniReactElement | null | undefined,
containerNode: HTMLElement,
): void {
// Initialize event system with the container
eventSystem.initialize(containerNode);

const newElement = element || null;

// Get the old instance BEFORE potentially deleting it
const oldInstance = rootInstances.get(containerNode) || null;

if (newElement === null) {
// When unmounting, remove original element from map to prevent memory leaks
rootElements.delete(containerNode);
} else {
// Store the original element for re-renders
rootElements.set(containerNode, newElement);
// Get or create fiber root for this container
let root = fiberRoots.get(containerNode);
if (!root) {
root = createFiberRoot(containerNode);
fiberRoots.set(containerNode, root);
}

// Track render performance
trackRenderStart();
const newInstance = reconcile(containerNode, newElement, oldInstance);
trackRenderEnd();
// Render tracking is handled inside performSyncWorkOnRoot
updateContainer(newElement, root);
flushSync();

if (newElement === null) {
// Ensure container is completely cleared when rendering null
containerNode.innerHTML = "";
// Clean up rootInstances after reconciliation
rootInstances.delete(containerNode);
} else {
rootInstances.set(containerNode, newInstance);
// Clean up fiber root when unmounting
fiberRoots.delete(containerNode);
}
}

/**
* Finds the root container for a given VDOM instance
* @param instance The VDOM instance
* @returns The root container element or null
* Gets the fiber root for a container
* @param container The container element
* @returns The fiber root or undefined
*/
export function findRootContainer(instance: VDOMInstance): HTMLElement | null {
// Strategy 1: Walk up the parent chain and validate rootContainer references
let current: VDOMInstance | undefined = instance;
let depth = 0;
while (current) {
if (current.rootContainer) {
// Verify this rootContainer is actually a real root by checking our rootInstances map
for (const [container, rootInstance] of rootInstances) {
if (container === current.rootContainer && rootInstance) {
return container;
}
}
}
current = current.parent;
depth++;
if (depth > 10) {
console.warn(
"Parent chain depth exceeded 10, breaking to avoid infinite loop",
);
break;
}
}

// Strategy 2: Search through all root instances to find the one containing this instance
for (const [container, rootInstance] of rootInstances) {
if (rootInstance && isInstanceInTree(instance, rootInstance)) {
return container;
}
}

// Strategy 3: If not found in main trees, check if this instance is part of a portal tree
current = instance;
while (current) {
if (
current.element &&
typeof current.element === "object" &&
"type" in current.element &&
current.element.type === PORTAL
) {
// Found a portal parent - now find which root tree contains this portal
for (const [container, rootInstance] of rootInstances) {
if (rootInstance && isInstanceInTree(current, rootInstance)) {
return container;
}
}
}
current = current.parent;
}

return null;
export function getFiberRoot(container: HTMLElement): FiberRoot | undefined {
return fiberRoots.get(container);
}

/**
* Checks if a given instance is part of a VDOM tree
* @param targetInstance The instance to find
* @param rootInstance The root of the tree to search
* @returns True if the instance is in the tree
*/
function isInstanceInTree(
targetInstance: VDOMInstance,
rootInstance: VDOMInstance,
): boolean {
if (targetInstance === rootInstance) {
return true;
}
// Re-export fiber-based hydration for SSR
export { hydrateRoot, type HydrateRootOptions } from "../fiber/hydration";

return rootInstance.childInstances.some((child) =>
isInstanceInTree(targetInstance, child),
);
}

/**
* Gets the root element for re-rendering
* @param container The container element
* @returns The root element or null
*/
export function getRootElement(
container: HTMLElement,
): AnyMiniReactElement | null {
return rootElements.get(container) || null;
}
// Re-export fiber-based resumability for state persistence
export {
serializeFiberTree,
dehydrateRoot,
parseSerializedRoot,
type SerializedFiberRoot,
} from "../fiber/resumability";
86 changes: 0 additions & 86 deletions src/dom-renderer/index.ts

This file was deleted.

10 changes: 0 additions & 10 deletions src/fragments/index.ts

This file was deleted.

Loading
Loading