/* eslint-disable no-extend-native */ /* eslint-disable @typescript-eslint/ban-types */ /** * make hooked methods "native" */ export const makeNative = (() => { const l = new Map(); hookNative( Function.prototype, "toString", (_toString) => { return function () { if (l.has(this)) { const _fn = l.get(this) || parseInt; // "function () {\n [native code]\n}" if (l.has(_fn)) { // nested return _fn.toString(); } else { return _toString.call(_fn) as string; } } return _toString.call(this) as string; }; }, true ); return (fn: Function, original: Function) => { l.set(fn, original); }; })(); export function hookNative( target: T, method: M, hook: (originalFn: T[M], detach: () => void) => T[M], async = false ): void { // reserve for future hook update const _fn = target[method]; const detach = () => { target[method] = _fn; // detach }; // This script can run before anything on the page, // so setting this function to be non-configurable and non-writable is no use. const hookedFn = hook(_fn, detach); target[method] = hookedFn; if (!async) { makeNative(hookedFn as any, _fn as any); } else { setTimeout(() => { makeNative(hookedFn as any, _fn as any); }); } }