diff --git a/lib/discord/src/flux/stores.ts b/lib/discord/src/flux/stores.ts index b825f70..00eb471 100644 --- a/lib/discord/src/flux/stores.ts +++ b/lib/discord/src/flux/stores.ts @@ -132,7 +132,7 @@ export type WithStoreName = FilterGenerator< */ export const withStoreName = createFilterGenerator( ([name], _, exports) => - exports.getName?.length === 0 && exports.getName() === name, + exports?.getName?.length === 0 && exports.getName() === name, ([name]) => `revenge.discord.storeName(${name})`, FilterFlag.RequiresExports, FilterScopes.Uninitialized | FilterScopes.Initialized, diff --git a/lib/modules/src/finders/_internal.ts b/lib/modules/src/finders/_internal.ts index 32fbab0..6266d69 100644 --- a/lib/modules/src/finders/_internal.ts +++ b/lib/modules/src/finders/_internal.ts @@ -2,7 +2,6 @@ import { getCurrentStack } from '@revenge-mod/utils/error' import { cacheFilterResultForId } from '../caches' import { mInitialized } from '../metro/patches' import { metroRequire } from '../metro/runtime' -import { isModuleExportBad } from '../metro/utils' import { FilterFlag } from './filters' import type { If } from '@revenge-mod/utils/types' import type { Metro } from '../types' @@ -66,6 +65,8 @@ export const FilterResultFlagToHumanReadable: Record = [FilterResultFlags.Found]: '\u001b[96mexportsless\u001b[0m', } +const noDefaultExportsCache = new Set() + // The reason this returns a flag is because flags are never falsy, while exports may be falsy when using ID-only filters (eg. `withDependencies`). // Currently, we only have options that are relevant for checking exports @@ -92,7 +93,13 @@ export function runFilter( if (exports === undefined) { if (filter.flags & FilterFlag.RequiresExports) return - if (filter(id)) { + const filter_ = filter as Filter<{ + Result: any + RequiresExports: false + Scopes: any[] + }> + + if (filter_(id)) { if (options?.initialize ?? true) { const module = metroRequire(id) // Check if the required module is not blacklisted @@ -111,7 +118,13 @@ export function runFilter( return } - if (filter(id, exports)) + const filter_ = filter as Filter<{ + Result: any + RequiresExports: true + Scopes: any[] + }> + + if (filter_(id, exports)) return cacheFilterResultForId( filter.key, id, @@ -119,9 +132,14 @@ export function runFilter( ) if (options?.skipDefault) return + if (noDefaultExportsCache.has(id)) return + if (!('default' in exports)) { + noDefaultExportsCache.add(id) + return + } const { default: defaultExport } = exports - if (!isModuleExportBad(defaultExport) && filter(id, defaultExport)) + if (filter_(id, defaultExport)) return cacheFilterResultForId(filter.key, id, FilterResultFlags.Default) } diff --git a/lib/modules/src/finders/filters/index.ts b/lib/modules/src/finders/filters/index.ts index 4944170..a8439eb 100644 --- a/lib/modules/src/finders/filters/index.ts +++ b/lib/modules/src/finders/filters/index.ts @@ -1,3 +1,4 @@ +import { isModuleExportBad } from '@revenge-mod/modules/metro/utils' import { FilterFlag, FilterScopes } from './constants' import { createFilterGenerator } from './utils' import type { Filter, FilterGenerator } from './utils' @@ -26,6 +27,8 @@ type FilterRequiringExports = Filter<{ export const withProps = createFilterGenerator>( (props, _, exports) => { const type = typeof exports + if (isModuleExportBad(exports)) return false + if (type === 'object' || type === 'function') { for (const prop of props) { if (prop in exports) continue @@ -58,7 +61,7 @@ export type WithProps = FilterGenerator< export const withoutProps = createFilterGenerator>( (props, _, exports) => { const type = typeof exports - if (type === 'object' || type === 'function') + if ((type === 'object' && exports !== null) || type === 'function') for (const prop of props) if (prop in exports) return false return true @@ -88,8 +91,8 @@ export type WithoutProps = FilterGenerator< */ export const withSingleProp = createFilterGenerator>( ([prop], _, exports) => { - if (typeof exports === 'object' && prop in exports) - return Object.keys(exports).length === 1 + if (typeof exports === 'object' && exports !== null && prop in exports) + return Reflect.ownKeys(exports).length === 1 return false }, @@ -133,7 +136,7 @@ export type WithSingleProp = FilterGenerator< * // const SomeClass: { new(): SomeClass, name: 'SomeClass' } */ export const withName = createFilterGenerator>( - ([name], _, exports) => exports.name === name, + ([name], _, exports) => exports?.name === name, ([name]) => `revenge.name(${name})`, FilterFlag.RequiresExports, FilterScopes.Initialized, diff --git a/lib/modules/src/finders/filters/utils.ts b/lib/modules/src/finders/filters/utils.ts index e3eb5cc..b09ae32 100644 --- a/lib/modules/src/finders/filters/utils.ts +++ b/lib/modules/src/finders/filters/utils.ts @@ -157,6 +157,7 @@ const Helpers: FilterHelpers = Object.setPrototypeOf( * ```ts * const custom = createFilterGenerator<[arg1: number, arg2: string]>( * ([arg1, arg2], id, exports) => { + * // WARNING: exports can be Proxy or nullish, so be careful when using it * // filter logic * return true * }, diff --git a/lib/modules/src/metro/utils.ts b/lib/modules/src/metro/utils.ts index d64bc47..ab33bd3 100644 --- a/lib/modules/src/metro/utils.ts +++ b/lib/modules/src/metro/utils.ts @@ -1,4 +1,3 @@ -import { isProxy } from '@revenge-mod/utils/proxy' import { mDeps, mList } from './patches' import { Initialized } from './runtime' import type { Metro } from '../types' @@ -38,8 +37,11 @@ export function getInitializedModuleExports( return mList.get(id)?.module?.exports } +const CATCH_ALL_TEST = Symbol() + /** - * Returns whether a particular module export is bad. This is used for filter functions to check whether an export is filterable. + * Returns whether a particular module export is bad. + * This is used for filter functions to check whether an export is filterable. * @param exp The export to check. */ export function isModuleExportBad( @@ -48,9 +50,7 @@ export function isModuleExportBad( return ( // Nullish? exp == null || - // Is it a proxy? (discord-intl has proxy exports) - isProxy(exp) || // Does it have some non-existent key? (Turbo modules) - (typeof exp === 'object' && '\u0001' in exp) + (typeof exp === 'object' && CATCH_ALL_TEST in exp) ) } diff --git a/lib/utils/src/discord.ts b/lib/utils/src/discord.ts index 74a79f3..b058856 100644 --- a/lib/utils/src/discord.ts +++ b/lib/utils/src/discord.ts @@ -6,6 +6,7 @@ import { createFilterGenerator, withDependencies, } from '@revenge-mod/modules/finders/filters' +import { isModuleExportBad } from '@revenge-mod/modules/metro/utils' import { ReactJSXRuntimeModuleId, ReactModuleId, @@ -86,7 +87,7 @@ export const withGeneratedIconComponent = createFilterGenerator< Parameters >( (names, id, exports) => { - if (typeof exports === 'object') { + if (typeof exports === 'object' && isModuleExportBad(exports)) { if (typeof exports[names[0]] === 'function') return Object.keys(exports).length === 1 } else {