Skip to content

Commit

Permalink
bugfix: internal refactoring to improve reconciliation & better handl…
Browse files Browse the repository at this point in the history
…e reordering of children
  • Loading branch information
LankyMoose committed Jun 30, 2024
1 parent 31cf2bf commit e572679
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 190 deletions.
5 changes: 2 additions & 3 deletions packages/lib/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
export const SignalKey = Symbol.for("kaioken.signal")
export const componentSymbol = Symbol.for("kaioken.component")
export const contextDataSymbol = Symbol.for("kaioken.contextData")

export const EffectTag = {
UPDATE: 1,
PLACEMENT: 2,
DELETION: 3,
} as const

export const componentSymbol = Symbol.for("kaioken.component")
export const elementFreezeSymbol = Symbol.for("kaioken.freezeElement")

export const elementTypes = {
text: "#text",
fragment: "KAIOKEN_FRAGMENT",
Expand Down
3 changes: 1 addition & 2 deletions packages/lib/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { contextDataSymbol } from "./constants.js"
import { fragment } from "./index.js"

const contextDataSymbol = Symbol.for("kaioken.contextData")

export function createContext<T>(defaultValue: T): Kaioken.Context<T> {
const ctx = {
Provider: ({ value, children = [] }: Kaioken.ProviderProps<T>) => {
Expand Down
3 changes: 1 addition & 2 deletions packages/lib/src/hooks/useContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useHook } from "./utils.js"

const contextDataSymbol = Symbol.for("kaioken.contextData")
import { contextDataSymbol } from "../constants.js"

type ContextNode<T> = Kaioken.VNode & {
props: {
Expand Down
31 changes: 10 additions & 21 deletions packages/lib/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AppContext, AppContextOptions } from "./appContext.js"
import {
isValidChild,
isVNode,
propFilters,
propToHtmlAttr,
Expand Down Expand Up @@ -67,34 +66,22 @@ function mount<T extends Record<string, unknown>>(

function createElement(
type: string | Function | typeof Component,
props = {},
...children: JSX.Element[]
props: null | Record<string, unknown> = null,
...children: unknown[]
): VNode {
const node = {
const node: VNode = {
type,
index: 0,
props: {
...props,
children: children.flat().filter(isValidChild).map(createChildElement),
},
props: children.length ? { ...props, children } : props ?? {},
}
nodeToCtxMap.set(node, ctx.current)
return node
}

function createChildElement(child: JSX.Element): VNode {
if (isVNode(child)) return child
return createTextElement(String(child))
}

function createTextElement(nodeValue: string): VNode {
return createElement(et.text, { nodeValue })
}

function fragment({
children,
...rest
}: { children: JSX.Element[] } & Record<string, unknown>) {
}: { children: unknown[] } & Record<string, unknown>) {
return createElement(et.fragment, rest, ...children)
}

Expand All @@ -113,15 +100,16 @@ function renderToString<T extends Record<string, unknown>>(
}

function renderToString_internal<T extends Record<string, unknown>>(
el: JSX.Element,
el: unknown,
parent?: VNode | undefined,
elProps = {} as T
): string {
if (el === null) return ""
if (el === undefined) return ""
if (typeof el === "boolean") return ""
if (typeof el === "string") return encodeHtmlEntities(el)
if (typeof el === "number") return encodeHtmlEntities(el.toString())
if (typeof el === "number" || typeof el === "bigint")
return encodeHtmlEntities(el.toString())
if (typeof el === "function")
return renderToString_internal(createElement(el, elProps))
if (el instanceof Array) {
Expand All @@ -132,6 +120,7 @@ function renderToString_internal<T extends Record<string, unknown>>(
return s
}
if (Signal.isSignal(el)) return encodeHtmlEntities(el.value.toString())
if (!isVNode(el)) return String(el)

el.parent = parent
nodeToCtxMap.set(el, ctx.current)
Expand Down Expand Up @@ -168,7 +157,7 @@ function renderToString_internal<T extends Record<string, unknown>>(
? Signal.isSignal(props.innerHTML)
? props.innerHTML.value
: props.innerHTML
: children.map((c) => renderToString_internal(c, el, c.props)).join("")
: children.map((c) => renderToString_internal(c, el)).join("")

return `<${type}${attrs.length ? " " + attrs : ""}${isSelfClosing ? "/>" : `>${inner}</${type}>`}`
}
7 changes: 4 additions & 3 deletions packages/lib/src/memo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { elementFreezeSymbol } from "./constants.js"
import { createElement } from "./index.js"

type Rec = Record<string, unknown>
Expand All @@ -20,15 +19,17 @@ export function memo<Props extends Record<string, unknown>>(
return Object.assign(
(props: Props) => {
if (node && arePropsEqual(oldProps, props)) {
return Object.assign(node, { [elementFreezeSymbol]: true })
node.frozen = true
return node
}
oldProps = props
if (!node) {
node = createElement(fn, props)
} else {
Object.assign(node.props, props)
}
return Object.assign(node, { [elementFreezeSymbol]: false })
node.frozen = false
return node
},
{ displayName: "Kaioken.memo" }
)
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/src/props.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export function assertValidElementProps(vNode: Kaioken.VNode) {
if (vNode.props.children.length && vNode.props.innerHTML) {
if (vNode.props.children?.length && vNode.props.innerHTML) {
throw new Error(
"[kaioken]: Cannot use both children and innerHTML on an element"
)
Expand Down
Loading

0 comments on commit e572679

Please sign in to comment.