2025-03-30 00:28:06 +07:00
|
|
|
import {
|
2025-06-03 16:08:37 +07:00
|
|
|
createPatchedFunctionProxy,
|
2025-04-03 23:00:51 +07:00
|
|
|
type FunctionProxyState,
|
|
|
|
|
type InsteadHookNode,
|
|
|
|
|
type PatchedFunctionProxyState,
|
2025-03-30 00:28:06 +07:00
|
|
|
patchedFunctionProxyHandler,
|
|
|
|
|
patchedFunctionProxyStates,
|
|
|
|
|
unproxy,
|
2025-03-30 01:39:44 +07:00
|
|
|
} from '../_internal'
|
2025-03-30 00:28:06 +07:00
|
|
|
|
2025-06-03 16:08:37 +07:00
|
|
|
import type {
|
|
|
|
|
AbstractNewable,
|
|
|
|
|
Callable,
|
|
|
|
|
FiniteDomain,
|
|
|
|
|
InsteadHook,
|
|
|
|
|
UnknownFunction,
|
|
|
|
|
} from '../types'
|
|
|
|
|
|
2025-03-30 00:28:06 +07:00
|
|
|
const insteadHookProxyHandler = {
|
|
|
|
|
...patchedFunctionProxyHandler,
|
2025-06-03 16:08:37 +07:00
|
|
|
apply<T extends Callable>(
|
|
|
|
|
hookNode: InsteadHookNode<T>,
|
|
|
|
|
receiver: ThisParameterType<T>,
|
|
|
|
|
args: Parameters<T>,
|
|
|
|
|
) {
|
2025-03-30 00:28:06 +07:00
|
|
|
const { next } = hookNode
|
2025-06-03 16:08:37 +07:00
|
|
|
return Reflect.apply(hookNode.hook, receiver, [
|
|
|
|
|
args,
|
|
|
|
|
next === undefined ? hookNode.target : next.proxy,
|
|
|
|
|
])
|
2025-03-30 00:28:06 +07:00
|
|
|
},
|
|
|
|
|
construct<T extends AbstractNewable<never, object>>(
|
|
|
|
|
hookNode: InsteadHookNode<T>,
|
|
|
|
|
args: ConstructorParameters<T>,
|
|
|
|
|
ctor: AbstractNewable,
|
|
|
|
|
) {
|
|
|
|
|
const { next } = hookNode
|
2025-06-03 16:08:37 +07:00
|
|
|
return Reflect.construct(
|
|
|
|
|
hookNode.hook,
|
|
|
|
|
[args, next === undefined ? hookNode.target : next.proxy],
|
|
|
|
|
ctor,
|
|
|
|
|
)
|
2025-03-30 00:28:06 +07:00
|
|
|
},
|
|
|
|
|
} as const satisfies Required<ProxyHandler<FunctionProxyState>>
|
|
|
|
|
|
|
|
|
|
function unpatchInstead<T extends UnknownFunction>(
|
|
|
|
|
state: PatchedFunctionProxyState<PropertyKey, T>,
|
|
|
|
|
hookNode: InsteadHookNode<T>,
|
|
|
|
|
) {
|
|
|
|
|
if (hookNode.unpatched) return
|
|
|
|
|
|
|
|
|
|
hookNode.unpatched = true
|
|
|
|
|
|
|
|
|
|
const { prev, next } = hookNode
|
|
|
|
|
if (prev === undefined) {
|
|
|
|
|
state.instead = next
|
|
|
|
|
if (next === undefined) {
|
2025-06-03 16:08:37 +07:00
|
|
|
if (state.before === undefined && state.after === undefined)
|
|
|
|
|
unproxy(state)
|
2025-03-30 00:28:06 +07:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
prev.next = next
|
|
|
|
|
hookNode.prev = undefined
|
|
|
|
|
if (next === undefined) return
|
|
|
|
|
}
|
|
|
|
|
next.prev = prev
|
|
|
|
|
hookNode.next = undefined
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-03 16:08:37 +07:00
|
|
|
export function instead<
|
|
|
|
|
Parent extends Record<Key, UnknownFunction>,
|
|
|
|
|
Key extends PropertyKey,
|
|
|
|
|
>(
|
2025-03-30 00:28:06 +07:00
|
|
|
parent: Parent,
|
|
|
|
|
key: FiniteDomain<Key>,
|
|
|
|
|
hook: InsteadHook<Parent[Key]>,
|
|
|
|
|
): () => void
|
|
|
|
|
export function instead<Key extends PropertyKey, Value extends UnknownFunction>(
|
|
|
|
|
parent: Record<Key, Value>,
|
|
|
|
|
key: FiniteDomain<Key>,
|
|
|
|
|
hook: InsteadHook<Value>,
|
|
|
|
|
) {
|
|
|
|
|
const target = parent[key]
|
|
|
|
|
|
|
|
|
|
let state = patchedFunctionProxyStates.get(target)
|
|
|
|
|
|
|
|
|
|
// biome-ignore lint/complexity/useArrowFunction: We need a function that has a constructor
|
|
|
|
|
const hookNode = function () {}
|
|
|
|
|
// The handler makes the proxy behave as target.
|
|
|
|
|
hookNode.proxy = new Proxy<any>(hookNode, insteadHookProxyHandler) as Value
|
|
|
|
|
hookNode.target = target
|
|
|
|
|
hookNode.unpatched = false
|
|
|
|
|
hookNode.hook = hook
|
|
|
|
|
hookNode.prev = undefined
|
|
|
|
|
|
|
|
|
|
if (state !== undefined && state.parent === parent && state.key === key) {
|
|
|
|
|
const head = state.instead
|
|
|
|
|
hookNode.next = head
|
|
|
|
|
if (head !== undefined) head.prev = hookNode
|
|
|
|
|
state.instead = hookNode
|
|
|
|
|
} else {
|
|
|
|
|
hookNode.next = undefined
|
2025-06-03 16:08:37 +07:00
|
|
|
state = createPatchedFunctionProxy(
|
|
|
|
|
target,
|
|
|
|
|
parent,
|
|
|
|
|
key,
|
|
|
|
|
undefined,
|
|
|
|
|
hookNode,
|
|
|
|
|
undefined,
|
|
|
|
|
)
|
2025-03-30 00:28:06 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return unpatchInstead.bind(undefined, state, hookNode)
|
|
|
|
|
}
|