GoonCord/src/metro/modules.ts

170 lines
5.5 KiB
TypeScript
Raw Normal View History

2024-05-20 15:36:33 +08:00
import { getMetroCache } from "@metro/caches";
import { Metro } from "@metro/types";
import { instead } from "spitroast";
2024-05-20 15:36:33 +08:00
// eslint-disable-next-line prefer-destructuring
const modules: Metro.ModuleList = window.modules;
const metroRequire: Metro.Require = window.__r;
2024-02-23 15:11:35 +08:00
metroRequire(0);
const blacklistedIds = new Set<String>();
2024-05-10 02:15:32 +08:00
const noopHandler = () => undefined;
const functionToString = Function.prototype.toString;
let patchedInspectSource = false;
let _importingModuleId: string | null = null;
/** Makes the module associated with the specified ID non-enumberable. */
function blacklistModule(id: string) {
Object.defineProperty(modules, id, { enumerable: false });
blacklistedIds.add(id);
}
function isBadExports(exports: any) {
return !exports || exports === window || exports["<!@ pylix was here :fuyusquish: !@>"] === null;
}
for (const id in modules) {
const metroModule = modules[id];
2024-04-02 19:53:23 +08:00
if (metroModule!.factory) {
instead("factory", metroModule, ((args: Parameters<Metro.FactoryFn>, origFunc: Metro.FactoryFn) => {
2024-05-10 02:15:32 +08:00
_importingModuleId = id;
const { 1: metroRequire, 4: moduleObject } = args;
args[2 /* metroImportDefault */] = id => {
const exps = metroRequire(id);
return exps && exps.__esModule ? exps.default : exps;
};
args[3 /* metroImportAll */] = id => {
const exps = metroRequire(id);
if (exps && exps.__esModule) return exps;
const importAll: Record<string, any> = {};
if (exps) Object.assign(importAll, exps);
importAll.default = exps;
return importAll;
};
origFunc(...args);
if (!isBadExports(moduleObject.exports)) {
onModuleRequire(moduleObject.exports, id);
} else {
blacklistModule(id);
}
2024-05-10 02:15:32 +08:00
_importingModuleId = null;
}) as any); // If only spitroast had better types
2024-04-02 19:53:23 +08:00
}
}
function onModuleRequire(moduleExports: any, id: Metro.ModuleID) {
2024-04-30 20:41:04 +08:00
// Temporary
moduleExports.initSentry &&= () => undefined;
if (moduleExports.default?.track && moduleExports.default.trackMaker)
moduleExports.default.track = () => Promise.resolve();
2024-04-30 20:41:04 +08:00
2024-05-10 02:15:32 +08:00
if (moduleExports.registerAsset) {
require("@lib/api/assets").patchAssets(moduleExports);
}
2024-04-02 19:53:23 +08:00
// There are modules registering the same native component
if (moduleExports?.default?.name === "requireNativeComponent") {
instead("default", moduleExports, (args, origFunc) => {
try {
return origFunc(...args);
} catch {
return args[0];
}
2024-03-06 01:57:59 +08:00
});
}
2024-04-02 19:53:23 +08:00
// Hook DeveloperExperimentStore
if (moduleExports?.default?.constructor?.displayName === "DeveloperExperimentStore") {
moduleExports.default = new Proxy(moduleExports.default, {
get(target, property, receiver) {
if (property === "isDeveloper") {
// Hopefully won't explode accessing it here :3
const { settings } = require("@lib/settings");
return settings.enableDiscordDeveloperSettings ?? false;
}
return Reflect.get(target, property, receiver);
}
});
}
2024-04-02 19:53:23 +08:00
// Funny infinity recursion caused by a race condition
if (!patchedInspectSource && window["__core-js_shared__"]) {
const inspect = (f: unknown) => typeof f === "function" && functionToString.apply(f, []);
2024-04-02 19:53:23 +08:00
window["__core-js_shared__"].inspectSource = inspect;
patchedInspectSource = true;
}
2024-03-09 05:27:55 +08:00
// Explosion (no, I can't explain this, don't ask) ((hi rosie))
if (moduleExports.findHostInstance_DEPRECATED) {
const numberedId = Number(id);
const prevExports = modules[numberedId - 1]?.publicModule.exports;
const inc = prevExports.default?.reactProfilingEnabled ? 1 : -1;
if (!modules[numberedId + inc]?.isInitialized) {
blacklistModule(String(numberedId + inc));
}
}
// Hindi timestamps moment
if (moduleExports.isMoment) {
instead("defineLocale", moduleExports, (args, orig) => {
const origLocale = moduleExports.locale();
orig(...args);
moduleExports.locale(origLocale);
});
}
}
2024-05-10 02:15:32 +08:00
export function getImportingModuleId() {
return _importingModuleId;
}
export function requireModule(id: Metro.ModuleID) {
if (blacklistedIds.has(String(id))) return undefined;
if (modules[id]?.isInitialized && !modules[id]?.hasError) {
return metroRequire(id);
}
2024-03-09 05:27:55 +08:00
2024-04-02 19:53:23 +08:00
// Disable Internal Metro error reporting logic
const originalHandler = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler(noopHandler);
2024-03-09 05:27:55 +08:00
let moduleExports;
2024-04-02 19:53:23 +08:00
try {
moduleExports = metroRequire(id);
2024-04-02 19:53:23 +08:00
} catch {
blacklistModule(String(id));
moduleExports = undefined;
2024-04-02 19:53:23 +08:00
}
2024-03-09 05:27:55 +08:00
2024-04-02 19:53:23 +08:00
// Done initializing! Now, revert our hacks
ErrorUtils.setGlobalHandler(originalHandler);
2024-03-09 05:27:55 +08:00
return moduleExports;
2024-03-09 05:27:55 +08:00
}
2024-05-20 15:36:33 +08:00
export function* getModules(uniqueId: string | null, all = false) {
yield [-1, require("@metro/polyfills/redesign")];
2024-05-20 15:36:33 +08:00
let cache = null;
if (uniqueId) {
cache = getMetroCache().findCache[uniqueId];
if (all && !cache?._) cache = undefined;
}
for (const id in cache ?? modules) {
const exports = requireModule(id);
if (isBadExports(exports)) continue;
yield [id, exports];
}
}