revenge-bundle-next/lib/react/src/jsx-runtime/index.ts

115 lines
3.1 KiB
TypeScript
Raw Normal View History

2025-07-12 21:05:27 +07:00
import { jPatches } from './_internal'
import type { ElementType, JSX, Key, ReactElement } from 'react'
2025-07-12 21:05:27 +07:00
import type { ReactJSXRuntime } from '..'
export type AnyJSXFactoryFunction = (typeof ReactJSXRuntime)['jsx' | 'jsxs']
export type BeforeJSXCallback<E extends ElementType> = (
args: [element: E, props: ElementTypeProps<E>, key?: Key | undefined],
2025-07-12 21:05:27 +07:00
) => Parameters<AnyJSXFactoryFunction>
export type InsteadJSXCallback<E extends ElementType> = (
args: [element: E, props: ElementTypeProps<E>, key?: Key | undefined],
2025-07-12 21:05:27 +07:00
jsx: AnyJSXFactoryFunction,
) => ReturnType<AnyJSXFactoryFunction> | null
export type AfterJSXCallback<E extends ElementType> = (
fiber: ReactElement<ElementTypeProps<E>, E>,
2025-07-12 21:05:27 +07:00
) => ReturnType<AnyJSXFactoryFunction> | null
type ElementTypeProps<E extends ElementType> = E extends ElementType<
infer Props
>
? Props
: E extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[E]
: never
2025-07-12 21:05:27 +07:00
/**
* 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<E extends ElementType>(
type: E,
patch: AfterJSXCallback<E>,
) {
2025-07-12 21:05:27 +07:00
let patches = jPatches.get(type)
if (!patches) {
patches = {}
2025-07-12 21:05:27 +07:00
jPatches.set(type, patches)
}
2025-07-12 21:05:27 +07:00
const set = (patches.after ??= new Set())
2025-07-12 21:05:27 +07:00
set.add(patch as AfterJSXCallback<E>)
2025-07-12 21:05:27 +07:00
return () => {
const res = set.delete(patch as AfterJSXCallback<E>)
2025-07-12 21:05:27 +07:00
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<E extends ElementType>(
type: E,
patch: BeforeJSXCallback<E>,
) {
2025-07-12 21:05:27 +07:00
let patches = jPatches.get(type)
if (!patches) {
patches = {}
2025-07-12 21:05:27 +07:00
jPatches.set(type, patches)
}
2025-07-12 21:05:27 +07:00
const set = (patches.before ??= new Set())
2025-07-12 21:05:27 +07:00
set.add(patch as BeforeJSXCallback<E>)
2025-07-12 21:05:27 +07:00
return () => {
const res = set.delete(patch as BeforeJSXCallback<E>)
2025-07-12 21:05:27 +07:00
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<E extends ElementType>(
type: E,
patch: InsteadJSXCallback<E>,
) {
2025-07-12 21:05:27 +07:00
let patches = jPatches.get(type)
if (!patches) {
patches = {}
2025-07-12 21:05:27 +07:00
jPatches.set(type, patches)
}
2025-07-12 21:05:27 +07:00
const set = (patches.instead ??= new Set())
2025-07-12 21:05:27 +07:00
set.add(patch as InsteadJSXCallback<E>)
2025-07-12 21:05:27 +07:00
return () => {
const res = set.delete(patch as InsteadJSXCallback<E>)
2025-07-12 21:05:27 +07:00
if (res) attemptCleanup(type)
return res
}
}
function attemptCleanup(type: ElementType) {
const patches = jPatches.get(type)!
if (patches.before?.size || patches.after?.size || patches.instead?.size)
return
2025-07-12 21:05:27 +07:00
jPatches.delete(type)
}