Skip to content

Commit

Permalink
refactor(jsx/dom): assign type and ref to node object directly. `Obje…
Browse files Browse the repository at this point in the history
…ct.defineProperties` is too slow
  • Loading branch information
usualoma committed Aug 17, 2024
1 parent a32bfb1 commit 7fd73ef
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 46 deletions.
4 changes: 2 additions & 2 deletions src/jsx/dom/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Child } from '../base'
import { DOM_ERROR_HANDLER } from '../constants'
import type { Context } from '../context'
import { globalContexts } from '../context'
import { newJSXNode, setInternalTagFlag } from './utils'
import { setInternalTagFlag } from './utils'

export const createContextProviderFunction =
<T>(values: T[]): Function =>
Expand Down Expand Up @@ -33,7 +33,7 @@ export const createContextProviderFunction =
}),
props: {},
})
const res = newJSXNode({ tag: '', props })
const res = { tag: '', props, type: '' }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(res as any)[DOM_ERROR_HANDLER] = (err: unknown) => {
values.pop()
Expand Down
54 changes: 35 additions & 19 deletions src/jsx/dom/intrinsic-element/components.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Props } from '../../base'
import type { FC, JSXNode, PropsWithChildren, RefObject } from '../../types'
import { newJSXNode } from '../utils'
import { createPortal, getNameSpaceContext } from '../render'
import type { PreserveNodeType } from '../render'
import { useContext } from '../../context'
Expand Down Expand Up @@ -58,10 +57,12 @@ const documentMetadataTag = (
supportBlocking: boolean
) => {
if (props?.itemProp) {
return newJSXNode({
return {
tag,
props,
})
type: tag,
ref: props.ref,
}
}

const head = document.head
Expand Down Expand Up @@ -192,13 +193,15 @@ const documentMetadataTag = (
}
}

const jsxNode = newJSXNode({
const jsxNode = {
tag,
type: tag,
props: {
...restProps,
ref,
},
}) as JSXNode & { e?: HTMLElement; p?: PreserveNodeType }
ref,
} as unknown as JSXNode & { e?: HTMLElement; p?: PreserveNodeType }

jsxNode.p = preserveNodeType // preserve for unmounting
if (element) {
Expand All @@ -215,30 +218,37 @@ export const title: FC<PropsWithChildren> = (props) => {
const nameSpaceContext = getNameSpaceContext()
const ns = nameSpaceContext && useContext(nameSpaceContext)
if (ns?.endsWith('svg')) {
return newJSXNode({
return {
tag: 'title',
props,
})
type: 'title',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ref: (props as any).ref,
} as unknown as JSXNode
}
return documentMetadataTag('title', props, undefined, false, false)
}

export const script: FC<PropsWithChildren<IntrinsicElements['script']>> = (props) => {
if (!props || ['src', 'async'].some((k) => !props[k])) {
return newJSXNode({
return {
tag: 'style',
props,
})
type: 'style',
ref: props.ref,
} as unknown as JSXNode
}
return documentMetadataTag('script', props, 1, false, true)
}

export const style: FC<PropsWithChildren<IntrinsicElements['style']>> = (props) => {
if (!props || !['href', 'precedence'].every((k) => k in props)) {
return newJSXNode({
return {
tag: 'style',
props,
})
type: 'style',
ref: props.ref,
} as unknown as JSXNode
}
props['data-href'] = props.href
delete props.href
Expand All @@ -251,10 +261,12 @@ export const link: FC<PropsWithChildren<IntrinsicElements['link']>> = (props) =>
['onLoad', 'onError'].some((k) => k in props) ||
(props.rel === 'stylesheet' && (!('precedence' in props) || 'disabled' in props))
) {
return newJSXNode({
return {
tag: 'link',
props,
})
type: 'link',
ref: props.ref,
} as unknown as JSXNode
}
return documentMetadataTag('link', props, 1, 'precedence' in props, true)
}
Expand Down Expand Up @@ -309,7 +321,7 @@ export const form: FC<

const [data, isDirty] = state
state[1] = false
return newJSXNode({
return {
tag: FormContext as unknown as Function,
props: {
value: {
Expand All @@ -318,17 +330,19 @@ export const form: FC<
method: data ? 'post' : null,
action: data ? action : null,
},
children: newJSXNode({
children: {
tag: 'form',
props: {
...restProps,
ref,
},
}),
type: 'form',
ref,
},
},
f: isDirty,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any) as any
} as any
}

const formActionableElement = (
Expand Down Expand Up @@ -357,11 +371,13 @@ const formActionableElement = (
})
}

return newJSXNode({
return {
tag,
props,
type: tag,
ref: props.ref,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as any
} as any
}

export const input: FC<PropsWithChildren<IntrinsicElements['input']>> = (props) =>
Expand Down
14 changes: 8 additions & 6 deletions src/jsx/dom/jsx-dev-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
*/

import type { JSXNode, Props } from '../base'
import { newJSXNode } from './utils'
import * as intrinsicElementTags from './intrinsic-element/components'

export const jsxDEV = (tag: string | Function, props: Props, key?: string): JSXNode => {
return newJSXNode({
tag:
(typeof tag === 'string' && intrinsicElementTags[tag as keyof typeof intrinsicElementTags]) ||
tag,
if (typeof tag === 'string' && intrinsicElementTags[tag as keyof typeof intrinsicElementTags]) {
tag = intrinsicElementTags[tag as keyof typeof intrinsicElementTags]
}
return {
tag,
type: tag,
props,
key,
})
ref: props.ref,
} as JSXNode
}

export const Fragment = (props: Record<string, unknown>): JSXNode => jsxDEV('', props, undefined)
7 changes: 4 additions & 3 deletions src/jsx/dom/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { EffectData } from '../hooks'
import { STASH_EFFECT } from '../hooks'
import { normalizeIntrinsicElementKey, styleObjectForEach } from '../utils'
import { createContext } from './context' // import dom-specific versions
import { newJSXNode } from './utils'

const HONO_PORTAL_ELEMENT = '_hp'

Expand Down Expand Up @@ -548,13 +547,15 @@ export const buildNode = (node: Child): Node | undefined => {
return { t: node.toString(), d: true } as NodeString
} else {
if ('vR' in node) {
node = newJSXNode({
node = {
tag: (node as NodeObject).tag,
props: (node as NodeObject).props,
key: (node as NodeObject).key,
f: (node as NodeObject).f,
type: (node as NodeObject).tag,
ref: (node as NodeObject).props.ref,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any)
} as any
}
if (typeof (node as JSXNode).tag === 'function') {
;(node as NodeObject)[DOM_STASH] = [0, []]
Expand Down
16 changes: 0 additions & 16 deletions src/jsx/dom/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,3 @@ export const setInternalTagFlag = (fn: Function): Function => {
;(fn as any)[DOM_INTERNAL_TAG] = true
return fn
}

const JSXNodeCompatPrototype = {
type: {
get(this: { tag: string | Function }): string | Function {
return this.tag
},
},
ref: {
get(this: { props?: { ref: unknown } }): unknown {
return this.props?.ref
},
},
}

export const newJSXNode = (obj: { tag: string | Function; props?: Props; key?: string }): JSXNode =>
Object.defineProperties(obj, JSXNodeCompatPrototype) as JSXNode

0 comments on commit 7fd73ef

Please sign in to comment.