diff --git a/src/lib/metro/filters.ts b/src/lib/metro/filters.ts index b45b359..815ab05 100644 --- a/src/lib/metro/filters.ts +++ b/src/lib/metro/filters.ts @@ -6,9 +6,12 @@ export type PropIntellisense

= Record & Record(...props: T[]) => PropIntellisense; export type PropsFinderAll = (...props: T[]) => PropIntellisense[]; +const blacklistedIds = new Set(); + /** Makes the module associated with the specified ID non-enumberable. */ function blacklistModule(id: Metro.ModuleID) { Object.defineProperty(modules, id, { enumerable: false }); + blacklistedIds.add(String(id)); } const functionToString = Function.prototype.toString; @@ -36,22 +39,24 @@ for (const id in modules) { }; origFunc(...args); - if (moduleObject.exports) onModuleRequire(moduleObject.exports); + if (moduleObject.exports) onModuleRequire(moduleObject.exports, id); }) as any); // If only spitroast had better types } } // Blacklist any "bad-actor" modules, e.g. the dreaded null proxy, the window itself, or undefined modules for (const id in modules) { + if (blacklistedIds.has(id)) continue; const moduleExports = requireModule(id); - if (!moduleExports || moduleExports === window || moduleExports["Revenge EOL when?"] === null) + if (!moduleExports || moduleExports === window || moduleExports["i think pyoncord eol tomorrow idk tho"] === null) blacklistModule(id); } +const noopHandler = () => undefined; let patchedInspectSource = false; -function onModuleRequire(moduleExports: any) { +function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { // Temporary moduleExports.initSentry &&= () => undefined; if (moduleExports.default?.track && moduleExports.default.trackMaker) @@ -89,9 +94,17 @@ function onModuleRequire(moduleExports: any) { window["__core-js_shared__"].inspectSource = inspect; patchedInspectSource = true; } -} -const noopHandler = () => undefined; + // 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(numberedId + inc); + } + } +} function requireModule(id: Metro.ModuleID) { if (modules[id]!.isInitialized) return metroRequire(id); diff --git a/src/lib/metro/types.ts b/src/lib/metro/types.ts index 3b2d6b6..7e5f356 100644 --- a/src/lib/metro/types.ts +++ b/src/lib/metro/types.ts @@ -2,11 +2,11 @@ import type { Nullish } from "@types"; /** @see {@link https://github.com/facebook/metro/blob/c2d7539dfc10aacb2f99fcc2f268a3b53e867a90/packages/metro-runtime/src/polyfills/require.js} */ export namespace Metro { - export interface DependencyMap { - readonly [indexer: number]: ModuleID; + export type DependencyMap = Array & { readonly paths?: Readonly> | undefined; - } + }; + /** Only available on Discord's development environment, will never be defined on release builds */ export type InverseDependencyMap = Record; export type FactoryFn = ( @@ -21,6 +21,7 @@ export namespace Metro { dependencyMap: DependencyMap | Nullish, ) => void; + /** Only available on Discord's development environment, will never be defined on release builds */ export interface HotModuleReloadingData { _acceptCallback: (() => void) | Nullish; _disposeCallback: (() => void) | Nullish; @@ -38,16 +39,27 @@ export namespace Metro { } export interface ModuleDefinition { + /** Set to undefined once module is initialized */ dependencyMap: DependencyMap | Nullish; + /** Error.value thrown by the factory */ error?: any; + /** Set to undefined once module is initialized */ factory: FactoryFn | undefined; + /** If factory thrown any error */ hasError: boolean; + /** Only available on Discord's development environment, will never be defined on release builds */ hot?: HotModuleReloadingData | undefined; + /** Cached `import *` imports in Metro, always an empty object as Bunny prevents outdated import cache */ importedAll: any; + /** Cached `import module from "./module"` imports in Metro, always an empty object as Bunny prevents outdated import cache */ importedDefault: any; + /** If factory has been successfully called */ isInitialized: boolean; + /** Only available on Discord's development environment, will never be defined on release builds */ path?: string | undefined; + /** Acts as CJS module in the bundler */ publicModule: Module; + /** Only available on Discord's development environment, will never be defined on release builds */ verboseName?: string | undefined; } @@ -59,7 +71,9 @@ export namespace Metro { factory: FactoryFn, moduleId: ModuleID, dependencyMap?: DependencyMap | undefined, + /** Only available on Discord's development environment, will never be defined on release builds */ verboseName?: string | undefined, + /** Only available on Discord's development environment, will never be defined on release builds */ inverseDependencies?: InverseDependencyMap | undefined ) => void; @@ -76,9 +90,9 @@ export namespace Metro { export interface Require extends RequireFn { importDefault: RequireFn; importAll: RequireFn; - /** @throws {Error} */ + /** @throws {Error} A macro, will always throws an error at runtime */ context: () => never; - /** @throws {Error} */ + /** @throws {Error} A macro, will always throws an error at runtime */ resolveWeak: () => never; unpackModuleId: (moduleId: ModuleID) => { localId: number; diff --git a/src/lib/metro/utils.ts b/src/lib/metro/utils.ts index e6cd4f7..3ffb54d 100644 --- a/src/lib/metro/utils.ts +++ b/src/lib/metro/utils.ts @@ -1,5 +1,6 @@ import type { Metro } from "@metro/types"; +// eslint-disable-next-line prefer-destructuring export const modules: Metro.ModuleList = window.modules; export const metroRequire: Metro.Require = window.__r;