Skip to content

Commit fa26293

Browse files
committed
feat: switch public API to fiber architecture
Update core/index.ts, MiniReact.ts, and jsx-runtime to export fiber-based implementations. Remove old dom-renderer, fragments, hooks, and reconciler modules.
1 parent 7398236 commit fa26293

11 files changed

Lines changed: 69 additions & 1938 deletions

File tree

src/MiniReact.ts

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,28 @@ export type {
1010
FunctionalComponent,
1111
ElementType,
1212
MiniReactElement,
13-
} from "./core/types";
14-
15-
// Hooks
16-
export {
17-
useState,
18-
useEffect,
19-
useReducer,
20-
useRef,
21-
useMemo,
22-
useCallback,
23-
} from "./hooks";
24-
export type {
25-
UseStateHook,
26-
UseEffectHook,
27-
UseReducerHook,
28-
UseRefHook,
2913
EffectCallback,
3014
DependencyList,
3115
Reducer,
3216
MutableRefObject,
33-
} from "./hooks/types";
17+
} from "./core/types";
18+
19+
// Fiber-based hooks
20+
export {
21+
useStateFiber as useState,
22+
useEffectFiber as useEffect,
23+
useReducerFiber as useReducer,
24+
useRefFiber as useRef,
25+
useMemoFiber as useMemo,
26+
useCallbackFiber as useCallback,
27+
} from "./fiber";
3428

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

3933
// Fragments
40-
export { Fragment } from "./fragments";
34+
export { FRAGMENT as Fragment } from "./core/types";
4135

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

5953
import { createContext, useContext } from "./context";
60-
// Higher-order components
6154
import { createElement, render } from "./core";
62-
import type { FunctionalComponent, VDOMInstance } from "./core/types";
63-
import { Fragment } from "./fragments";
55+
import { FRAGMENT as Fragment } from "./core/types";
56+
import type { FunctionalComponent } from "./core/types";
6457
import {
65-
useCallback,
66-
useEffect,
67-
useMemo,
68-
useReducer,
69-
useRef,
70-
useState,
71-
} from "./hooks";
58+
useCallbackFiber as useCallback,
59+
useEffectFiber as useEffect,
60+
useMemoFiber as useMemo,
61+
useReducerFiber as useReducer,
62+
useRefFiber as useRef,
63+
useStateFiber as useState,
64+
} from "./fiber";
7265
import { jsx, jsxDEV, jsxs } from "./jsx-runtime";
7366
import {
7467
getPerformanceMetrics,
@@ -90,7 +83,7 @@ export function memo<P extends Record<string, unknown>>(
9083
};
9184

9285
// Store the comparison function and original component for reconciliation
93-
(MemoizedComponent as unknown as Record<string, unknown>).__memo = {
86+
(MemoizedComponent as unknown as Record<string, unknown>)["__memo"] = {
9487
Component,
9588
areEqual: areEqual || shallowEqual,
9689
};
@@ -122,30 +115,7 @@ function shallowEqual<P extends Record<string, unknown>>(
122115
return true;
123116
}
124117

125-
import {
126-
popContextValues,
127-
pushContextValues,
128-
setContextRenderInstance,
129-
} from "./context";
130-
// Set up cross-module dependencies
131-
import { scheduleEffect, setHookContext } from "./hooks";
132-
import {
133-
setContextHooks,
134-
setHookContext as setReconcilerHookContext,
135-
setScheduleEffect,
136-
} from "./reconciler";
137-
138-
// Initialize cross-module dependencies
139-
setScheduleEffect(scheduleEffect);
140-
setContextHooks(pushContextValues, popContextValues);
141-
// Connect context render instance to hooks render instance
142-
setReconcilerHookContext((instance: VDOMInstance | null) => {
143-
setHookContext(instance);
144-
setContextRenderInstance(instance);
145-
});
146-
147-
// Export internal utilities for advanced usage
148-
export { setHookContext } from "./hooks";
118+
// Export event system for advanced usage
149119
export { eventSystem } from "./events";
150120

151121
// Constants

src/core/index.ts

Lines changed: 33 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,21 @@
22
/* Core Functionality */
33
/* ****************** */
44

5-
import { eventSystem } from "../events";
6-
import { trackRenderEnd, trackRenderStart } from "../performance";
7-
import { reconcile } from "../reconciler";
5+
import {
6+
type FiberRoot,
7+
createRoot as createFiberRoot,
8+
flushSync,
9+
updateContainer,
10+
} from "../fiber";
811
import {
912
type AnyMiniReactElement,
1013
type ElementType,
1114
type JSXElementType,
12-
PORTAL,
1315
TEXT_ELEMENT,
14-
type VDOMInstance,
1516
} from "./types";
1617

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

2221
/**
2322
* Creates a MiniReact element (virtual DOM node)
@@ -59,128 +58,49 @@ export function createElement(
5958
}
6059

6160
/**
62-
* Renders a MiniReact element into a container using the reconciler
61+
* Renders a MiniReact element into a container using the fiber reconciler
6362
* @param element The element to render (can be null to clear)
6463
* @param containerNode The container DOM node
6564
*/
6665
export function render(
6766
element: AnyMiniReactElement | null | undefined,
6867
containerNode: HTMLElement,
6968
): void {
70-
// Initialize event system with the container
71-
eventSystem.initialize(containerNode);
72-
7369
const newElement = element || null;
7470

75-
// Get the old instance BEFORE potentially deleting it
76-
const oldInstance = rootInstances.get(containerNode) || null;
77-
78-
if (newElement === null) {
79-
// When unmounting, remove original element from map to prevent memory leaks
80-
rootElements.delete(containerNode);
81-
} else {
82-
// Store the original element for re-renders
83-
rootElements.set(containerNode, newElement);
71+
// Get or create fiber root for this container
72+
let root = fiberRoots.get(containerNode);
73+
if (!root) {
74+
root = createFiberRoot(containerNode);
75+
fiberRoots.set(containerNode, root);
8476
}
8577

86-
// Track render performance
87-
trackRenderStart();
88-
const newInstance = reconcile(containerNode, newElement, oldInstance);
89-
trackRenderEnd();
78+
// Render tracking is handled inside performSyncWorkOnRoot
79+
updateContainer(newElement, root);
80+
flushSync();
9081

9182
if (newElement === null) {
92-
// Ensure container is completely cleared when rendering null
93-
containerNode.innerHTML = "";
94-
// Clean up rootInstances after reconciliation
95-
rootInstances.delete(containerNode);
96-
} else {
97-
rootInstances.set(containerNode, newInstance);
83+
// Clean up fiber root when unmounting
84+
fiberRoots.delete(containerNode);
9885
}
9986
}
10087

10188
/**
102-
* Finds the root container for a given VDOM instance
103-
* @param instance The VDOM instance
104-
* @returns The root container element or null
89+
* Gets the fiber root for a container
90+
* @param container The container element
91+
* @returns The fiber root or undefined
10592
*/
106-
export function findRootContainer(instance: VDOMInstance): HTMLElement | null {
107-
// Strategy 1: Walk up the parent chain and validate rootContainer references
108-
let current: VDOMInstance | undefined = instance;
109-
let depth = 0;
110-
while (current) {
111-
if (current.rootContainer) {
112-
// Verify this rootContainer is actually a real root by checking our rootInstances map
113-
for (const [container, rootInstance] of rootInstances) {
114-
if (container === current.rootContainer && rootInstance) {
115-
return container;
116-
}
117-
}
118-
}
119-
current = current.parent;
120-
depth++;
121-
if (depth > 10) {
122-
console.warn(
123-
"Parent chain depth exceeded 10, breaking to avoid infinite loop",
124-
);
125-
break;
126-
}
127-
}
128-
129-
// Strategy 2: Search through all root instances to find the one containing this instance
130-
for (const [container, rootInstance] of rootInstances) {
131-
if (rootInstance && isInstanceInTree(instance, rootInstance)) {
132-
return container;
133-
}
134-
}
135-
136-
// Strategy 3: If not found in main trees, check if this instance is part of a portal tree
137-
current = instance;
138-
while (current) {
139-
if (
140-
current.element &&
141-
typeof current.element === "object" &&
142-
"type" in current.element &&
143-
current.element.type === PORTAL
144-
) {
145-
// Found a portal parent - now find which root tree contains this portal
146-
for (const [container, rootInstance] of rootInstances) {
147-
if (rootInstance && isInstanceInTree(current, rootInstance)) {
148-
return container;
149-
}
150-
}
151-
}
152-
current = current.parent;
153-
}
154-
155-
return null;
93+
export function getFiberRoot(container: HTMLElement): FiberRoot | undefined {
94+
return fiberRoots.get(container);
15695
}
15796

158-
/**
159-
* Checks if a given instance is part of a VDOM tree
160-
* @param targetInstance The instance to find
161-
* @param rootInstance The root of the tree to search
162-
* @returns True if the instance is in the tree
163-
*/
164-
function isInstanceInTree(
165-
targetInstance: VDOMInstance,
166-
rootInstance: VDOMInstance,
167-
): boolean {
168-
if (targetInstance === rootInstance) {
169-
return true;
170-
}
97+
// Re-export fiber-based hydration for SSR
98+
export { hydrateRoot, type HydrateRootOptions } from "../fiber/hydration";
17199

172-
return rootInstance.childInstances.some((child) =>
173-
isInstanceInTree(targetInstance, child),
174-
);
175-
}
176-
177-
/**
178-
* Gets the root element for re-rendering
179-
* @param container The container element
180-
* @returns The root element or null
181-
*/
182-
export function getRootElement(
183-
container: HTMLElement,
184-
): AnyMiniReactElement | null {
185-
return rootElements.get(container) || null;
186-
}
100+
// Re-export fiber-based resumability for state persistence
101+
export {
102+
serializeFiberTree,
103+
dehydrateRoot,
104+
parseSerializedRoot,
105+
type SerializedFiberRoot,
106+
} from "../fiber/resumability";

src/dom-renderer/index.ts

Lines changed: 0 additions & 86 deletions
This file was deleted.

src/fragments/index.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)