feat(lib/modules/finders)!: initialized boolean for filter predicates + remove FilterFlag and weird assumptions + fix partial matches causing modules skip in lookup
This commit is contained in:
parent
5b44ad0846
commit
9258695ec7
9 changed files with 145 additions and 221 deletions
|
|
@ -17,10 +17,7 @@ import {
|
|||
Uncached,
|
||||
} from '../../../modules/src/caches'
|
||||
import { FilterResultFlags } from '../../../modules/src/finders/_internal'
|
||||
import {
|
||||
FilterFlag,
|
||||
FilterScopes,
|
||||
} from '../../../modules/src/finders/filters/constants'
|
||||
import { FilterScopes } from '../../../modules/src/finders/filters/constants'
|
||||
import type {
|
||||
Filter,
|
||||
FilterGenerator,
|
||||
|
|
@ -91,7 +88,6 @@ const withLeadingFluxStoreDeps = withDependencies(
|
|||
export type WithStore = FilterGenerator<
|
||||
<T>() => Filter<{
|
||||
Result: DiscordModules.Flux.Store<T>
|
||||
RequiresExports: boolean
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
|
|
@ -103,27 +99,21 @@ export type WithStore = FilterGenerator<
|
|||
* A dynamic filter that matches all Flux stores.
|
||||
*/
|
||||
export const withStore = createFilterGenerator(
|
||||
(_, id, exports) => {
|
||||
if (exports) return Boolean(exports._dispatchToken)
|
||||
else {
|
||||
(_, id, exports, initialized) => {
|
||||
if (initialized) return Boolean(exports?._dispatchToken)
|
||||
|
||||
if (!withLeadingFluxStoreDeps(id)) return false
|
||||
const deps = getModuleDependencies(id)!
|
||||
return deps[deps.length - 1] === ImportTrackerModuleId
|
||||
}
|
||||
},
|
||||
() => 'revenge.discord.store',
|
||||
FilterFlag.Dynamic,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithStore
|
||||
|
||||
export type WithStoreName = FilterGenerator<
|
||||
<T>(name: string) => Filter<{
|
||||
Result: DiscordModules.Flux.Store<T>
|
||||
RequiresExports: true
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
]
|
||||
Scopes: [typeof FilterScopes.Initialized]
|
||||
}>
|
||||
>
|
||||
|
||||
|
|
@ -132,10 +122,12 @@ export type WithStoreName = FilterGenerator<
|
|||
*/
|
||||
export const withStoreName = createFilterGenerator(
|
||||
([name], _, exports) =>
|
||||
exports?.getName?.length === 0 && exports.getName() === name,
|
||||
typeof exports?.getName === 'function' &&
|
||||
exports.getName.length === 0 &&
|
||||
exports.getName() === name,
|
||||
([name]) => `revenge.discord.storeName(${name})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
// Uninitialized to make sure cached results resolve immediately
|
||||
FilterScopes.Initialized | FilterScopes.Uninitialized,
|
||||
) as WithStoreName
|
||||
|
||||
/// STORE CACHING
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { getCurrentStack } from '@revenge-mod/utils/error'
|
|||
import { cacheFilterResultForId } from '../caches'
|
||||
import { mInitialized } from '../metro/patches'
|
||||
import { metroRequire } from '../metro/runtime'
|
||||
import { FilterFlag } from './filters'
|
||||
import { isModuleInitialized } from '../metro/utils'
|
||||
import type { If } from '@revenge-mod/utils/types'
|
||||
import type { Metro } from '../types'
|
||||
import type { Filter } from './filters'
|
||||
|
|
@ -72,34 +72,14 @@ const noDefaultExportsCache = new Set<Metro.ModuleID>()
|
|||
// Currently, we only have options that are relevant for checking exports
|
||||
// If we add more options later on, do NOT forget about
|
||||
// adding them here, and passing them in the lookup* functions
|
||||
export function runFilter(
|
||||
filter: Filter<{ Result: any; RequiresExports: false; Scopes: any[] }>,
|
||||
id: Metro.ModuleID,
|
||||
): FilterResultFlag | undefined
|
||||
|
||||
export function runFilter(
|
||||
filter: Filter,
|
||||
id: Metro.ModuleID,
|
||||
exports: Metro.ModuleExports,
|
||||
options?: RunFilterOptions,
|
||||
): FilterResultFlag | undefined
|
||||
|
||||
export function runFilter(
|
||||
filter: Filter,
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
options?: RunFilterOptions,
|
||||
): FilterResultFlag | undefined {
|
||||
if (exports === undefined) {
|
||||
if (filter.flags & FilterFlag.RequiresExports) return
|
||||
|
||||
const filter_ = filter as Filter<{
|
||||
Result: any
|
||||
RequiresExports: false
|
||||
Scopes: any[]
|
||||
}>
|
||||
|
||||
if (filter_(id)) {
|
||||
if (!isModuleInitialized(id)) {
|
||||
if (filter(id, undefined, false)) {
|
||||
if (options?.initialize ?? true) {
|
||||
const module = metroRequire(id)
|
||||
// Check if the required module is not blacklisted
|
||||
|
|
@ -118,13 +98,7 @@ export function runFilter(
|
|||
return
|
||||
}
|
||||
|
||||
const filter_ = filter as Filter<{
|
||||
Result: any
|
||||
RequiresExports: true
|
||||
Scopes: any[]
|
||||
}>
|
||||
|
||||
if (filter_(id, exports))
|
||||
if (filter(id, exports, true))
|
||||
return cacheFilterResultForId(
|
||||
filter.key,
|
||||
id,
|
||||
|
|
@ -133,13 +107,15 @@ export function runFilter(
|
|||
|
||||
if (options?.skipDefault) return
|
||||
if (noDefaultExportsCache.has(id)) return
|
||||
if (!('default' in exports)) {
|
||||
|
||||
const type = typeof exports
|
||||
if (type !== 'object' || exports === null || !('default' in exports)) {
|
||||
noDefaultExportsCache.add(id)
|
||||
return
|
||||
}
|
||||
|
||||
const { default: defaultExport } = exports
|
||||
if (filter_(id, defaultExport))
|
||||
if (filter(id, defaultExport, true))
|
||||
return cacheFilterResultForId(filter.key, id, FilterResultFlags.Default)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { FilterFlag } from './constants'
|
||||
import { FilterScopes } from '.'
|
||||
import { createFilterGenerator } from './utils'
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type {
|
||||
|
|
@ -6,33 +6,48 @@ import type {
|
|||
FilterBase,
|
||||
FilterGenerator,
|
||||
FilterInfoOf,
|
||||
FilterScopeValue,
|
||||
MergeFilterInfo,
|
||||
UnionFilterInfo,
|
||||
} from '.'
|
||||
|
||||
/** {@link FilterScopes.All} sets no predicate assumptions. Only {@link FilterScopes.Uninitialized} implies exportless. */
|
||||
const canFilterUninitialized = (scopes: FilterScopeValue) =>
|
||||
Boolean(scopes & FilterScopes.Uninitialized)
|
||||
|
||||
/**
|
||||
* If both or neither can filter uninitialized, positional order is preserved.
|
||||
*
|
||||
* If only one can, the one that can will be used as a prefilter for uninitialized modules,
|
||||
* and the other will only run on initialized modules.
|
||||
*/
|
||||
const compositeHandler = <G extends (a: FilterBase, b: FilterBase) => any>(
|
||||
sameHandler: G,
|
||||
dynamicHandler: G,
|
||||
directHandler: G,
|
||||
prefilteredHandler: G,
|
||||
) =>
|
||||
((a, b) =>
|
||||
a.flags === b.flags
|
||||
? sameHandler(a, b)
|
||||
: a.flags === FilterFlag.RequiresExports
|
||||
? dynamicHandler(a, b)
|
||||
: dynamicHandler(b, a)) as G
|
||||
((a, b) => {
|
||||
const ea = canFilterUninitialized(a.scopes)
|
||||
return ea === canFilterUninitialized(b.scopes)
|
||||
? directHandler(a, b)
|
||||
: ea
|
||||
? prefilteredHandler(b, a)
|
||||
: prefilteredHandler(a, b)
|
||||
}) as G
|
||||
|
||||
const compositeArrayHandler = <
|
||||
G extends (args: [a: FilterBase, b: FilterBase]) => any,
|
||||
>(
|
||||
sameHandler: G,
|
||||
dynamicHandler: G,
|
||||
directHandler: G,
|
||||
prefilteredHandler: G,
|
||||
) =>
|
||||
(([a, b]) =>
|
||||
a.flags === b.flags
|
||||
? sameHandler([a, b])
|
||||
: a.flags === FilterFlag.RequiresExports
|
||||
? dynamicHandler([a, b])
|
||||
: dynamicHandler([b, a])) as G
|
||||
(([a, b]) => {
|
||||
const ea = canFilterUninitialized(a.scopes)
|
||||
return ea === canFilterUninitialized(b.scopes)
|
||||
? directHandler([a, b])
|
||||
: ea
|
||||
? prefilteredHandler([b, a])
|
||||
: prefilteredHandler([a, b])
|
||||
}) as G
|
||||
|
||||
export type And = FilterGenerator<
|
||||
<F1 extends FilterBase, F2 extends FilterBase>(
|
||||
|
|
@ -46,49 +61,49 @@ const andKeyGenerator = ([a, b]: Parameters<And>) =>
|
|||
|
||||
const andScopesGenerator = ([a, b]: Parameters<And>) => a.scopes | b.scopes
|
||||
|
||||
const sameFlagsAnd = createFilterGenerator(
|
||||
([a, b], id, exports) => a(id, exports) && b(id, exports),
|
||||
const directAnd = createFilterGenerator(
|
||||
([a, b], id, exports, initialized) =>
|
||||
a(id, exports, initialized) && b(id, exports, initialized),
|
||||
andKeyGenerator,
|
||||
([a]) => a.flags,
|
||||
andScopesGenerator,
|
||||
) as And
|
||||
|
||||
const andFallbackCache = new WeakMap<FilterBase, Set<Metro.ModuleID>>()
|
||||
const andPrefilterCache = new WeakMap<FilterBase, Set<Metro.ModuleID>>()
|
||||
|
||||
const dynamicFlagsAnd = createFilterGenerator(
|
||||
([filter, fallbackFilter], id, exports) => {
|
||||
if (exports) {
|
||||
if (filter(id, exports)) {
|
||||
// Avoid running the fallback filter if we already know the first one passed
|
||||
const cache = andFallbackCache.get(fallbackFilter)
|
||||
const prefilteredAnd = createFilterGenerator(
|
||||
([filter, prefilter], id, exports, initialized) => {
|
||||
if (initialized) {
|
||||
if (filter(id, exports, true)) {
|
||||
// Avoid running the prefilter again if we already know it passed
|
||||
const cache = andPrefilterCache.get(prefilter)
|
||||
return (
|
||||
// biome-ignore lint/complexity/useOptionalChain: Hot path should be optimized
|
||||
(cache && cache.has(id)) || fallbackFilter(id, exports)
|
||||
(cache && cache.has(id)) || prefilter(id, exports, true)
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const result = fallbackFilter(id)
|
||||
const result = prefilter(id, undefined, false)
|
||||
if (result) {
|
||||
// Cache fallback hits to avoid calling the fallback filter again
|
||||
// Fallback filters are usually more expensive
|
||||
let set = andFallbackCache.get(fallbackFilter)
|
||||
if (!set) andFallbackCache.set(fallbackFilter, (set = new Set()))
|
||||
// Cache prefilter hits to avoid calling the prefilter again
|
||||
// Prefilters are usually more expensive
|
||||
let set = andPrefilterCache.get(prefilter)
|
||||
if (!set) andPrefilterCache.set(prefilter, (set = new Set()))
|
||||
set.add(id)
|
||||
}
|
||||
return result
|
||||
},
|
||||
andKeyGenerator,
|
||||
FilterFlag.Dynamic,
|
||||
andScopesGenerator,
|
||||
) as And
|
||||
|
||||
/**
|
||||
* Combines two filters into one, returning true if **every** filter matches.
|
||||
*
|
||||
* If each filter has different flags,
|
||||
* If only one of the filters can run on uninitialized modules ({@link FilterScopes.Uninitialized}),
|
||||
* it is used as the prefilter for uninitialized modules, and the other only runs once a candidate is initialized.
|
||||
*
|
||||
* @param filters The filters to combine.
|
||||
*
|
||||
|
|
@ -111,23 +126,13 @@ const dynamicFlagsAnd = createFilterGenerator(
|
|||
* )
|
||||
* ```
|
||||
*/
|
||||
export const and = Object.assign(
|
||||
compositeHandler(sameFlagsAnd, dynamicFlagsAnd),
|
||||
{
|
||||
keyFor: compositeArrayHandler(
|
||||
sameFlagsAnd.keyFor,
|
||||
dynamicFlagsAnd.keyFor,
|
||||
),
|
||||
flagsFor: compositeArrayHandler(
|
||||
sameFlagsAnd.flagsFor,
|
||||
dynamicFlagsAnd.flagsFor,
|
||||
),
|
||||
export const and = Object.assign(compositeHandler(directAnd, prefilteredAnd), {
|
||||
keyFor: compositeArrayHandler(directAnd.keyFor, prefilteredAnd.keyFor),
|
||||
defaultScopesFor: compositeArrayHandler(
|
||||
sameFlagsAnd.defaultScopesFor,
|
||||
dynamicFlagsAnd.defaultScopesFor,
|
||||
directAnd.defaultScopesFor,
|
||||
prefilteredAnd.defaultScopesFor,
|
||||
),
|
||||
},
|
||||
) satisfies And
|
||||
}) satisfies And
|
||||
|
||||
export type Or = FilterGenerator<
|
||||
<F1 extends FilterBase, F2 extends FilterBase>(
|
||||
|
|
@ -141,21 +146,21 @@ const orKeyGenerator = ([a, b]: Parameters<Or>) =>
|
|||
|
||||
const orScopesGenerator = ([a, b]: Parameters<Or>) => a.scopes | b.scopes
|
||||
|
||||
const sameFlagOr = createFilterGenerator(
|
||||
([a, b], id, exports) => a(id, exports) || b(id, exports),
|
||||
const directOr = createFilterGenerator(
|
||||
([a, b], id, exports, initialized) =>
|
||||
a(id, exports, initialized) || b(id, exports, initialized),
|
||||
orKeyGenerator,
|
||||
([a]) => a.flags,
|
||||
orScopesGenerator,
|
||||
) as Or
|
||||
|
||||
const dynamicFlagOr = createFilterGenerator(
|
||||
([filter, fallbackFilter], id, exports) => {
|
||||
// TODO(PalmDevs): Potential optimization: Add fallback cache here too?
|
||||
if (exports) return filter(id, exports) || fallbackFilter(id, exports)
|
||||
return fallbackFilter(id)
|
||||
const prefilteredOr = createFilterGenerator(
|
||||
([filter, prefilter], id, exports, initialized) => {
|
||||
// TODO(PalmDevs): Potential optimization: Add prefilter cache here too?
|
||||
if (initialized)
|
||||
return filter(id, exports, true) || prefilter(id, exports, true)
|
||||
return prefilter(id, undefined, false)
|
||||
},
|
||||
orKeyGenerator,
|
||||
FilterFlag.Dynamic,
|
||||
orScopesGenerator,
|
||||
) as Or
|
||||
|
||||
|
|
@ -183,17 +188,10 @@ const dynamicFlagOr = createFilterGenerator(
|
|||
* )
|
||||
* ```
|
||||
*/
|
||||
export const or = Object.assign(
|
||||
compositeHandler<Or>(sameFlagOr, dynamicFlagOr),
|
||||
{
|
||||
keyFor: compositeArrayHandler(sameFlagOr.keyFor, dynamicFlagOr.keyFor),
|
||||
flagsFor: compositeArrayHandler(
|
||||
sameFlagOr.flagsFor,
|
||||
dynamicFlagOr.flagsFor,
|
||||
),
|
||||
export const or = Object.assign(compositeHandler<Or>(directOr, prefilteredOr), {
|
||||
keyFor: compositeArrayHandler(directOr.keyFor, prefilteredOr.keyFor),
|
||||
defaultScopesFor: compositeArrayHandler(
|
||||
sameFlagOr.defaultScopesFor,
|
||||
dynamicFlagOr.defaultScopesFor,
|
||||
directOr.defaultScopesFor,
|
||||
prefilteredOr.defaultScopesFor,
|
||||
),
|
||||
},
|
||||
) satisfies Or
|
||||
}) satisfies Or
|
||||
|
|
|
|||
|
|
@ -1,21 +1,3 @@
|
|||
export const FilterFlag = {
|
||||
/**
|
||||
* This filter works with and without module exports.
|
||||
* Allowing for both initialized and uninitialized modules to be matched.
|
||||
*/
|
||||
Dynamic: 0,
|
||||
/**
|
||||
* This filter requires module exports to work.
|
||||
* Only initialized modules will be matched.
|
||||
*/
|
||||
RequiresExports: 1,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* @see {@link FilterFlag}
|
||||
*/
|
||||
export type FilterFlag = number
|
||||
|
||||
/**
|
||||
* Scopes to limit filters to certain module states.
|
||||
*/
|
||||
|
|
@ -23,10 +5,16 @@ export const FilterScopes = {
|
|||
/**
|
||||
* Include all modules (both initialized and uninitialized, including blacklisted).
|
||||
* This overrides {@link FilterScopes.Uninitialized} and {@link FilterScopes.Initialized}.
|
||||
*
|
||||
* **Filter generators generally don't need this scope.**
|
||||
*
|
||||
* When combining multiple filters with composite filters, the {@link FilterScopes.All} scope doesn't set assumptions for the filter predicate.
|
||||
* It only decides which modules to run the predicate against.
|
||||
* **Filter generators must include {@link FilterScopes.Uninitialized} and/or {@link FilterScopes.Initialized} as well.**
|
||||
*/
|
||||
All: 1,
|
||||
/**
|
||||
* Include uninitialized modules in the search.
|
||||
* Include uninitialized modules in the search. Implies the predicate can run without exports.
|
||||
*/
|
||||
Uninitialized: 2,
|
||||
/**
|
||||
|
|
@ -47,10 +35,6 @@ export interface FilterInfo {
|
|||
* The result type of the filter.
|
||||
*/
|
||||
Result: any
|
||||
/**
|
||||
* Whether the filter requires exports to work.
|
||||
*/
|
||||
RequiresExports: boolean
|
||||
/**
|
||||
* Scopes the filter matches modules in.
|
||||
*/
|
||||
|
|
@ -59,6 +43,5 @@ export interface FilterInfo {
|
|||
|
||||
export interface DefaultFilterInfo extends FilterInfo {
|
||||
Result: any
|
||||
RequiresExports: boolean
|
||||
Scopes: FilterScope[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
getModuleDependencies,
|
||||
} from '../../metro/utils'
|
||||
import { runFilter } from '../_internal'
|
||||
import { FilterFlag, FilterScopes } from './constants'
|
||||
import { FilterScopes } from './constants'
|
||||
import { createFilterGenerator } from './utils'
|
||||
import type { Metro } from '../../types'
|
||||
import type { Filter, FilterGenerator } from './utils'
|
||||
|
|
@ -76,7 +76,6 @@ export interface ComparableDependencyMap
|
|||
const withDependencies_ = createFilterGenerator<Parameters<WithDependencies>>(
|
||||
([deps], id) => depCompare(getModuleDependencies(id)!, deps, id, id),
|
||||
deps => `revenge.deps(${depGenFilterKey(deps)})`,
|
||||
FilterFlag.Dynamic,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithDependencies
|
||||
|
||||
|
|
@ -98,7 +97,6 @@ withDependencies.relative = relative
|
|||
type WithDependencies = FilterGenerator<
|
||||
<T>(deps: ComparableDependencyMap) => Filter<{
|
||||
Result: T
|
||||
RequiresExports: false
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { isModuleExportBad } from '@revenge-mod/modules/metro/utils'
|
||||
import { FilterFlag, FilterScopes } from './constants'
|
||||
import { FilterScopes } from './constants'
|
||||
import { createFilterGenerator } from './utils'
|
||||
import type { Filter, FilterGenerator } from './utils'
|
||||
|
||||
|
|
@ -8,7 +8,6 @@ export * from './utils'
|
|||
|
||||
type FilterRequiringExports<T> = Filter<{
|
||||
Result: T
|
||||
RequiresExports: true
|
||||
Scopes: [typeof FilterScopes.Initialized]
|
||||
}>
|
||||
|
||||
|
|
@ -41,7 +40,6 @@ export const withProps = createFilterGenerator<Parameters<WithProps>>(
|
|||
return false
|
||||
},
|
||||
props => `revenge.props(${props.join(',')})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithProps
|
||||
|
||||
|
|
@ -67,7 +65,6 @@ export const withoutProps = createFilterGenerator<Parameters<WithoutProps>>(
|
|||
return true
|
||||
},
|
||||
props => `revenge.withoutProps(${props.join(',')})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithoutProps
|
||||
|
||||
|
|
@ -97,7 +94,6 @@ export const withSingleProp = createFilterGenerator<Parameters<WithSingleProp>>(
|
|||
return false
|
||||
},
|
||||
([prop]) => `revenge.singleProp(${prop})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithSingleProp
|
||||
|
||||
|
|
@ -138,7 +134,6 @@ export type WithSingleProp = FilterGenerator<
|
|||
export const withName = createFilterGenerator<Parameters<WithName>>(
|
||||
([name], _, exports) => exports?.name === name,
|
||||
([name]) => `revenge.name(${name})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithName
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +1,32 @@
|
|||
import { isModuleInitialized } from '@revenge-mod/modules/metro/utils'
|
||||
import { getErrorStack } from '@revenge-mod/utils/error'
|
||||
import { withProps } from '.'
|
||||
import { and, or } from './composite'
|
||||
import { FilterScopes } from './constants'
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { If, LogicalAnd } from '@revenge-mod/utils/types'
|
||||
import type {
|
||||
DefaultFilterInfo,
|
||||
FilterFlag,
|
||||
FilterInfo,
|
||||
FilterScope,
|
||||
FilterScopes as FilterScopesObject,
|
||||
FilterScopeValue,
|
||||
} from './constants'
|
||||
|
||||
export type FilterResult<F> = F extends Filter<infer I> ? I['Result'] : never
|
||||
|
||||
export type FilterRequiresExports<F> =
|
||||
F extends Filter<infer I> ? I['RequiresExports'] : never
|
||||
|
||||
export type FilterInfoOf<F> = F extends Filter<infer I> ? I : FilterInfo
|
||||
|
||||
declare const FilterInfoBrand: unique symbol
|
||||
|
||||
export interface FilterBase<Info extends FilterInfo = DefaultFilterInfo> {
|
||||
(
|
||||
...args: If<
|
||||
Info['RequiresExports'],
|
||||
[id: Metro.ModuleID, exports: Metro.ModuleExports],
|
||||
[id: Metro.ModuleID, exports?: never]
|
||||
>
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
initialized?: boolean,
|
||||
): boolean
|
||||
key: string
|
||||
flags: If<
|
||||
Info['RequiresExports'],
|
||||
(typeof FilterFlag)['RequiresExports'],
|
||||
FilterFlag
|
||||
>
|
||||
scopes: FilterScopeValue
|
||||
/** @internal */
|
||||
readonly [FilterInfoBrand]?: Info
|
||||
}
|
||||
|
||||
export type Filter<Info extends FilterInfo = DefaultFilterInfo> =
|
||||
|
|
@ -42,13 +34,11 @@ export type Filter<Info extends FilterInfo = DefaultFilterInfo> =
|
|||
|
||||
export type MergeFilterInfo<I1 extends FilterInfo, I2 extends FilterInfo> = {
|
||||
Result: I1['Result'] & I2['Result']
|
||||
RequiresExports: LogicalAnd<I1['RequiresExports'], I2['RequiresExports']>
|
||||
Scopes: [...I1['Scopes'], ...I2['Scopes']]
|
||||
}
|
||||
|
||||
export type UnionFilterInfo<I1 extends FilterInfo, I2 extends FilterInfo> = {
|
||||
Result: I1['Result'] | I2['Result']
|
||||
RequiresExports: LogicalAnd<I1['RequiresExports'], I2['RequiresExports']>
|
||||
Scopes: [...I1['Scopes'], ...I2['Scopes']]
|
||||
}
|
||||
|
||||
|
|
@ -91,25 +81,12 @@ export interface FilterHelpers<Info extends FilterInfo = DefaultFilterInfo> {
|
|||
*/
|
||||
scope<T extends Filter<Info>, const S extends FilterScope[]>(
|
||||
this: T,
|
||||
...scopes: If<
|
||||
Info['RequiresExports'],
|
||||
[typeof FilterScopesObject.Initialized],
|
||||
S
|
||||
>
|
||||
): Filter<
|
||||
Info & {
|
||||
Scopes: If<
|
||||
Info['RequiresExports'],
|
||||
[typeof FilterScopesObject.Initialized],
|
||||
S
|
||||
>
|
||||
}
|
||||
>
|
||||
...scopes: S
|
||||
): Filter<Info & { Scopes: S }>
|
||||
}
|
||||
|
||||
export type FilterGenerator<G extends (...args: any[]) => Filter> = G & {
|
||||
keyFor(args: Parameters<G>): string
|
||||
flagsFor(args: Parameters<G>): FilterFlag
|
||||
defaultScopesFor(args: Parameters<G>): FilterScopeValue
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +106,8 @@ const Helpers: FilterHelpers = Object.setPrototypeOf(
|
|||
const newFilter = ((
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
) => this(id, exports)) as typeof this
|
||||
initialized?: boolean,
|
||||
) => this(id, exports, initialized)) as typeof this
|
||||
|
||||
Object.assign(newFilter, this)
|
||||
|
||||
|
|
@ -139,7 +117,9 @@ const Helpers: FilterHelpers = Object.setPrototypeOf(
|
|||
const filter = this.new()
|
||||
filter.scopes = 0
|
||||
for (const scope of scopes) filter.scopes |= scope
|
||||
return filter
|
||||
return filter as Filter<
|
||||
FilterInfoOf<typeof filter> & { Scopes: typeof scopes }
|
||||
>
|
||||
},
|
||||
} satisfies FilterHelpers,
|
||||
Function.prototype,
|
||||
|
|
@ -150,15 +130,14 @@ const Helpers: FilterHelpers = Object.setPrototypeOf(
|
|||
*
|
||||
* @param filter The function that filters the modules.
|
||||
* @param keyFor The function that generates the key for the filter.
|
||||
* @param flagFor The function that generates the flags for the filter, or a static flag.
|
||||
* @param defaultScopesFor The function that generates the default scopes for the filter, or static scopes. Defaults to {@link FilterScopes.Initialized}.
|
||||
* @returns A function that generates a filter with the specified arguments.
|
||||
*
|
||||
* @example
|
||||
* ```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
|
||||
* ([arg1, arg2], id, exports, initialized) => {
|
||||
* // WARNING: exports can be a Proxy, nullish, or a primitive, so be careful when using it
|
||||
* // filter logic
|
||||
* return true
|
||||
* },
|
||||
|
|
@ -173,16 +152,15 @@ export function createFilterGenerator<A extends any[]>(
|
|||
args: A,
|
||||
id: Metro.ModuleID,
|
||||
exports: Metro.ModuleExports,
|
||||
initialized: boolean,
|
||||
) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
flagFor: ((args: A) => FilterFlag) | FilterFlag,
|
||||
defaultScopesFor?: ((args: A) => FilterScopeValue) | FilterScopeValue,
|
||||
): FilterGenerator<(...args: A) => Filter>
|
||||
|
||||
export function createFilterGenerator<A extends any[]>(
|
||||
filter: (args: A, id: Metro.ModuleID) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
flagFor: ((args: A) => FilterFlag) | FilterFlag,
|
||||
defaultScopesFor?: ((args: A) => FilterScopeValue) | FilterScopeValue,
|
||||
): FilterGenerator<(...args: A) => Filter>
|
||||
|
||||
|
|
@ -190,16 +168,14 @@ export function createFilterGenerator<A extends any[]>(
|
|||
filter: (
|
||||
args: A,
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
exports: Metro.ModuleExports,
|
||||
initialized: boolean,
|
||||
) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
flagFor: ((args: A) => FilterFlag) | FilterFlag,
|
||||
defaultScopesFor?: ((args: A) => FilterScopeValue) | FilterScopeValue,
|
||||
): FilterGenerator<(...args: A) => Filter> {
|
||||
type GeneratorType = ReturnType<typeof createFilterGenerator<A>>
|
||||
|
||||
const isFlagsStatic = typeof flagFor === 'number'
|
||||
|
||||
const defaultScopes = defaultScopesFor ?? FilterScopes.Initialized
|
||||
const isDefaultScopesStatic = typeof defaultScopes === 'number'
|
||||
|
||||
|
|
@ -207,9 +183,10 @@ export function createFilterGenerator<A extends any[]>(
|
|||
const filter_ = ((
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
initialized: boolean = Boolean(isModuleInitialized(id)),
|
||||
) => {
|
||||
try {
|
||||
return filter(args, id, exports)
|
||||
return filter(args, id, exports, initialized)
|
||||
} catch (e) {
|
||||
DEBUG_warnFilterThrown(id, keyFor(args), e)
|
||||
return false
|
||||
|
|
@ -217,17 +194,12 @@ export function createFilterGenerator<A extends any[]>(
|
|||
}) as ReturnType<GeneratorType>
|
||||
|
||||
filter_.key = keyFor(args)
|
||||
filter_.flags = isFlagsStatic ? flagFor : flagFor(args)
|
||||
filter_.scopes = isDefaultScopesStatic
|
||||
? defaultScopes
|
||||
: defaultScopes(args)
|
||||
return Object.setPrototypeOf(filter_, Helpers)
|
||||
}
|
||||
|
||||
generator.flagsFor = isFlagsStatic
|
||||
? () => flagFor
|
||||
: (args: A) => flagFor(args)
|
||||
|
||||
generator.defaultScopesFor = isDefaultScopesStatic
|
||||
? () => defaultScopes
|
||||
: defaultScopes
|
||||
|
|
|
|||
|
|
@ -183,11 +183,19 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
|
||||
if (includeUninit)
|
||||
for (const id of mUninitialized) {
|
||||
// Must snapshot because partial matches can cause modules that haven't been looped over to be initialized
|
||||
// which would remove them from the Set
|
||||
for (const id of Array.from(mUninitialized)) {
|
||||
// biome-ignore lint/complexity/useOptionalChain: Hot path should be optimized
|
||||
if (cached && cached.has(id)) continue
|
||||
|
||||
const flag = runFilter(filter, id, undefined, options)
|
||||
const flag = runFilter(
|
||||
filter,
|
||||
id,
|
||||
// Could be initialized by a previous partial match
|
||||
getInitializedModuleExports(id),
|
||||
options,
|
||||
)
|
||||
if (flag) {
|
||||
notFound = false
|
||||
|
||||
|
|
@ -308,8 +316,15 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
|
||||
if (includeUninit)
|
||||
for (const id of mUninitialized) {
|
||||
const flag = runFilter(filter, id, undefined, options)
|
||||
// See equivalent code in `lookupModules` for why this is snapshotted
|
||||
for (const id of [...mUninitialized]) {
|
||||
const flag = runFilter(
|
||||
filter,
|
||||
id,
|
||||
// See equivalent code in `lookupModules` for why this isn't undefined
|
||||
getInitializedModuleExports(id),
|
||||
options,
|
||||
)
|
||||
if (flag) {
|
||||
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__)
|
||||
DEBUG_logLookupMatched(filter.key, flag, id)
|
||||
|
|
|
|||
|
|
@ -12,10 +12,7 @@ import {
|
|||
ReactModuleId,
|
||||
ReactNativeModuleId,
|
||||
} from '@revenge-mod/react'
|
||||
import {
|
||||
FilterFlag,
|
||||
FilterScopes,
|
||||
} from '../../modules/src/finders/filters/constants'
|
||||
import { FilterScopes } from '../../modules/src/finders/filters/constants'
|
||||
import type {
|
||||
Filter,
|
||||
FilterGenerator,
|
||||
|
|
@ -50,7 +47,6 @@ export type WithGeneratedIconComponent = FilterGenerator<
|
|||
...assets: string[]
|
||||
) => Filter<{
|
||||
Result: { [K in N]: FC<any> }
|
||||
RequiresExports: boolean
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
|
|
@ -126,7 +122,6 @@ export const withGeneratedIconComponent = createFilterGenerator<
|
|||
return false
|
||||
},
|
||||
names => `revenge.utils.discord.generatedIconComponent(${names.join(',')})`,
|
||||
FilterFlag.Dynamic,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithGeneratedIconComponent
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue