import { jPatches } from './_internal' import type { ElementType, JSX, Key, ReactElement } from 'react' import type { ReactJSXRuntime } from '..' export type AnyJSXFactoryFunction = (typeof ReactJSXRuntime)['jsx' | 'jsxs'] export type BeforeJSXCallback = ( args: [element: E, props: ElementTypeProps, key?: Key | undefined], ) => Parameters export type InsteadJSXCallback = ( args: [element: E, props: ElementTypeProps, key?: Key | undefined], jsx: AnyJSXFactoryFunction, ) => ReturnType | null export type AfterJSXCallback = ( fiber: ReactElement, E>, ) => ReturnType | null type ElementTypeProps = E extends ElementType< infer Props > ? Props : E extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[E] : never /** * Registers a hook to be called after a JSX element with the specified type is created. * * @param type The type of the element. * @param patch The hook. * @returns A function to unpatch. */ export function afterJSX( type: E, patch: AfterJSXCallback, ) { let patches = jPatches.get(type) if (!patches) { patches = [undefined, new Set(), undefined] jPatches.set(type, patches) } else if (!patches[1]) patches[1] = new Set() const set = patches[1]! set.add(patch as AfterJSXCallback) return () => { const res = set.delete(patch as AfterJSXCallback) if (res) attemptCleanup(type) return res } } /** * Registers a hook to be called before a JSX element with the specified type is created. * * @param type The type of the element. * @param patch The hook. * @returns A function to unpatch. */ export function beforeJSX( type: E, patch: BeforeJSXCallback, ) { let patches = jPatches.get(type) if (!patches) { patches = [new Set(), undefined, undefined] jPatches.set(type, patches) } else if (!patches[0]) patches[0] = new Set() const set = patches[0]! set.add(patch as BeforeJSXCallback) return () => { const res = set.delete(patch as BeforeJSXCallback) if (res) attemptCleanup(type) return res } } /** * Registers a callback to run instead when a JSX element with the specified type is created. * * @param type The type of the element. * @param patch The hook. * @returns A function to unpatch. */ export function insteadJSX( type: E, patch: InsteadJSXCallback, ) { let patches = jPatches.get(type) if (!patches) { patches = [undefined, undefined, new Set()] jPatches.set(type, patches) } else if (!patches[2]) patches[2] = new Set() const set = patches[2]! set.add(patch as InsteadJSXCallback) return () => { const res = set.delete(patch as InsteadJSXCallback) if (res) attemptCleanup(type) return res } } function attemptCleanup(type: ElementType) { const patches = jPatches.get(type)! if (patches[0]?.size || patches[1]?.size || patches[2]?.size) return jPatches.delete(type) }