feat(lib/react): add jsx-runtime

This commit is contained in:
PalmDevs 2025-07-10 21:25:46 +07:00
parent 546bbc080a
commit 2a0d91878f
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
7 changed files with 131 additions and 4 deletions

View file

@ -15,8 +15,11 @@
"./preinit": {
"default": "./src/preinit.ts"
},
"./init": {
"default": "./src/init.ts"
},
"./types": {
"types": "./src/types.ts"
}
}
}
}

1
lib/react/src/init.ts Normal file
View file

@ -0,0 +1 @@
import './jsx-runtime/patches'

View file

@ -0,0 +1,12 @@
import type { ElementType } from 'react'
import type { AfterJSXCallback, BeforeJSXCallback, InsteadJSXCallback } from '.'
export const beforeCallbacks = new Map<
ElementType,
Set<BeforeJSXCallback<any, any>>
>()
export const insteadCallbacks = new Map<
ElementType,
Set<InsteadJSXCallback<any, any>>
>()
export const afterCallbacks = new Map<ElementType, Set<AfterJSXCallback>>()

View file

@ -1,2 +1,83 @@
// TODO(react/jsx-runtime): add lib
export {}
import { afterCallbacks, beforeCallbacks, insteadCallbacks } from './_internal'
import type { ComponentProps, ElementType, Key, ReactElement } from 'react'
import type { ReactJSXRuntime } from '..'
export type AnyJSXFactoryFunction = (typeof ReactJSXRuntime)['jsx' | 'jsxs']
export type BeforeJSXCallback<
E extends ElementType = ElementType,
P = ComponentProps<E>,
> = (
args: [element: E, props: P, key?: Key | undefined],
) => Parameters<AnyJSXFactoryFunction>
export type InsteadJSXCallback<
E extends ElementType = ElementType,
P = ComponentProps<E>,
> = (
args: [element: E, props: P, key?: Key | undefined],
) => ReturnType<AnyJSXFactoryFunction> | null
export type AfterJSXCallback = (
tree: ReactElement,
) => ReturnType<AnyJSXFactoryFunction> | null
/**
* 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: ElementType, patch: AfterJSXCallback) {
let set = afterCallbacks.get(type)
if (!set) {
set = new Set()
afterCallbacks.set(type, set)
}
set.add(patch as AfterJSXCallback)
return () => set.delete(patch as AfterJSXCallback)
}
/**
* 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 = ElementType,
P = ComponentProps<E>,
>(type: E, patch: BeforeJSXCallback<E, P>) {
let set = beforeCallbacks.get(type)
if (!set) {
set = new Set()
beforeCallbacks.set(type, set)
}
set.add(patch as BeforeJSXCallback)
return () => set.delete(patch as BeforeJSXCallback)
}
/**
* 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 = ElementType,
P = ComponentProps<E>,
>(type: E, patch: InsteadJSXCallback<E, P>) {
let set = insteadCallbacks.get(type)
if (!set) {
set = new Set()
insteadCallbacks.set(type, set)
}
set.add(patch as InsteadJSXCallback)
return () => set.delete(patch as InsteadJSXCallback)
}

View file

@ -0,0 +1,30 @@
import { instead } from '@revenge-mod/patcher'
import { ReactJSXRuntime } from '..'
import { afterCallbacks, beforeCallbacks, insteadCallbacks } from './_internal'
import type { ElementType, ReactElement } from 'react'
import type { AnyJSXFactoryFunction } from '.'
const patch = (
args: Parameters<AnyJSXFactoryFunction>,
orig: AnyJSXFactoryFunction,
) => {
const Comp = args[0] as any
const type: ElementType = Comp.type ?? Comp
const before = beforeCallbacks.get(type)
if (before) for (const cb of before) args = cb(args)
let tree: ReactElement | null | undefined
const instead = insteadCallbacks.get(type)
if (instead) for (const cb of instead) tree = cb(args)
const after = afterCallbacks.get(type)
if (after) for (const cb of after) tree = cb(tree!)
if (tree !== undefined) return tree! // have to cast non-nullish even if returning null works
return Reflect.apply(orig, ReactJSXRuntime, args)
}
instead(ReactJSXRuntime, 'jsx', patch)
instead(ReactJSXRuntime, 'jsxs', patch)

View file

@ -2,4 +2,3 @@
import '.'
import './native/patches'
import './jsx-runtime/patches'

View file

@ -37,6 +37,7 @@ onModuleFirstRequired(IndexModuleId, function onIndexRequired() {
)
// Initialize init libraries
require('@revenge-mod/react/init')
require('@revenge-mod/storage/init')
require('@revenge-mod/externals/init')
require('@revenge-mod/discord/init')