2025-07-12 21:05:27 +07:00
|
|
|
import { jPatches } from './_internal'
|
2025-07-16 20:05:20 +07:00
|
|
|
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']
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
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>
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
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
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
export type AfterJSXCallback<E extends ElementType> = (
|
|
|
|
|
fiber: ReactElement<ElementTypeProps<E>, E>,
|
2025-07-12 21:05:27 +07:00
|
|
|
) => ReturnType<AnyJSXFactoryFunction> | null
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
type ElementTypeProps<E extends ElementType> = E extends ElementType<
|
|
|
|
|
infer Props
|
|
|
|
|
>
|
|
|
|
|
? Props
|
2025-07-16 20:05:20 +07:00
|
|
|
: E extends keyof JSX.IntrinsicElements
|
|
|
|
|
? JSX.IntrinsicElements[E]
|
|
|
|
|
: never
|
2025-07-15 22:05:11 +07:00
|
|
|
|
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.
|
|
|
|
|
*/
|
2025-07-15 22:05:11 +07:00
|
|
|
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 = [undefined, new Set(), undefined]
|
|
|
|
|
jPatches.set(type, patches)
|
2025-07-13 00:17:28 +07:00
|
|
|
} else if (!patches[1]) patches[1] = new Set()
|
2025-07-12 21:05:27 +07:00
|
|
|
|
|
|
|
|
const set = patches[1]!
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
set.add(patch as AfterJSXCallback<E>)
|
2025-07-12 21:05:27 +07:00
|
|
|
return () => {
|
2025-07-15 22:05:11 +07:00
|
|
|
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.
|
|
|
|
|
*/
|
2025-07-15 22:05:11 +07:00
|
|
|
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 = [new Set(), undefined, undefined]
|
|
|
|
|
jPatches.set(type, patches)
|
2025-07-13 00:17:28 +07:00
|
|
|
} else if (!patches[0]) patches[0] = new Set()
|
2025-07-12 21:05:27 +07:00
|
|
|
|
|
|
|
|
const set = patches[0]!
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
set.add(patch as BeforeJSXCallback<E>)
|
2025-07-12 21:05:27 +07:00
|
|
|
return () => {
|
2025-07-15 22:05:11 +07:00
|
|
|
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.
|
|
|
|
|
*/
|
2025-07-15 22:05:11 +07:00
|
|
|
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 = [undefined, undefined, new Set()]
|
|
|
|
|
jPatches.set(type, patches)
|
2025-07-13 00:17:28 +07:00
|
|
|
} else if (!patches[2]) patches[2] = new Set()
|
2025-07-12 21:05:27 +07:00
|
|
|
|
|
|
|
|
const set = patches[2]!
|
|
|
|
|
|
2025-07-15 22:05:11 +07:00
|
|
|
set.add(patch as InsteadJSXCallback<E>)
|
2025-07-12 21:05:27 +07:00
|
|
|
return () => {
|
2025-07-15 22:05:11 +07:00
|
|
|
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[0]?.size || patches[1]?.size || patches[2]?.size) return
|
|
|
|
|
jPatches.delete(type)
|
|
|
|
|
}
|