Files
reacord/src/reconciler.ts
2021-12-22 10:35:55 -06:00

106 lines
2.7 KiB
TypeScript

/* eslint-disable unicorn/no-null */
import { inspect } from "node:util"
import ReactReconciler from "react-reconciler"
import { raise } from "./helpers/raise.js"
import type { MessageNode, Node, TextNode } from "./node-tree.js"
import type { MessageRenderer } from "./renderer.js"
type ElementTag = string
type Props = Record<string, unknown>
const createInstance = (type: string, props: Props): Node => {
if (type !== "reacord-element") {
raise(`createInstance: unknown type: ${type}`)
}
if (typeof props.createNode !== "function") {
const actual = inspect(props.createNode)
raise(`invalid createNode function, received: ${actual}`)
}
return props.createNode()
}
type ChildSet = MessageNode
export const reconciler = ReactReconciler<
string, // Type (jsx tag),
Props, // Props,
MessageRenderer, // Container,
Node, // Instance,
TextNode, // TextInstance,
never, // SuspenseInstance,
never, // HydratableInstance,
never, // PublicInstance,
null, // HostContext,
[], // UpdatePayload,
ChildSet, // ChildSet,
unknown, // TimeoutHandle,
unknown // NoTimeout
>({
now: Date.now,
isPrimaryRenderer: true,
supportsMutation: false,
supportsPersistence: true,
supportsHydration: false,
scheduleTimeout: setTimeout,
cancelTimeout: clearTimeout,
noTimeout: -1,
getRootHostContext: () => null,
getChildHostContext: (parentContext) => parentContext,
shouldSetTextContent: () => false,
createInstance,
createTextInstance: (text) => ({ type: "text", text }),
createContainerChildSet: (): ChildSet => ({
type: "message",
children: [],
}),
appendChildToContainerChildSet: (childSet: ChildSet, child: Node) => {
childSet.children.push(child)
},
finalizeContainerChildren: (container: MessageRenderer, children: ChildSet) =>
false,
replaceContainerChildren: (
container: MessageRenderer,
children: ChildSet,
) => {
container.render(children)
},
appendInitialChild: (parent, child) => {
if ("children" in parent) {
parent.children.push(child)
} else {
raise(`${parent.type} cannot have children`)
}
},
cloneInstance: (
instance: Node,
_: unknown,
type: ElementTag,
oldProps: Props,
newProps: Props,
) => createInstance(type, newProps),
// returning a non-null value tells react to re-render the whole thing
// on any prop change
//
// we can probably optimize this to actually compare old/new props though
prepareUpdate: () => [],
finalizeInitialChildren: () => false,
prepareForCommit: (container) => null,
resetAfterCommit: () => null,
getPublicInstance: () => raise("Not implemented"),
preparePortalMount: () => raise("Not implemented"),
})