feat(lib/modules)!: filter scopes + fixes and improvements
# Features - Filters now control what types of modules they see. - Filters can be reinstantiated via `.new()` - Added the `Not` type in @revenge-mod/utils/types # Fixes - Pitfall with `lookupModules` and `withDependencies` without `uninitialize: true` fixed. - Filter type-metadata is now stored in a single object instead of multiple parameters. - Incorrect caching behavior for `lookupModules` where cache always reports not found if you finish the iterator. - Also attempt to report not found when using `lookupModule`.
This commit is contained in:
parent
dde81af9f6
commit
b6d1cff9f5
24 changed files with 287 additions and 298 deletions
|
|
@ -102,7 +102,6 @@ export let AssetsRegistryModuleId: Metro.ModuleID | undefined
|
|||
export let AssetsRegistry: ReactNative.AssetsRegistry = proxify(() => {
|
||||
for (const [, id] of lookupModules(withDependencies([[]]), {
|
||||
initialize: false,
|
||||
uninitialized: true,
|
||||
})) {
|
||||
const deps = getModuleDependencies(id)!
|
||||
if (deps.length !== 1) continue
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@ export let ActionSheetActionCreators: DiscordModules.Actions.ActionSheetActionCr
|
|||
2,
|
||||
]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ActionSheetActionCreators = module)
|
||||
|
|
@ -67,9 +64,6 @@ export let AlertActionCreators: DiscordModules.Actions.AlertActionCreators =
|
|||
]),
|
||||
),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (AlertActionCreators = module)
|
||||
|
|
@ -89,9 +83,6 @@ export let ToastActionCreators: DiscordModules.Actions.ToastActionCreators =
|
|||
withProps<DiscordModules.Actions.ToastActionCreators>('open')
|
||||
.and(withoutProps('init'))
|
||||
.and(withDependencies([DispatcherModuleId, 2])),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
for (const [module] of generator)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,4 @@ export const [Dispatcher, DispatcherModuleId] = lookupModule(
|
|||
withProps<DiscordModules.Flux.Dispatcher>('_interceptors').and(
|
||||
withDependencies([relative(1), null, null, null, null, 2]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
) as [DiscordModules.Flux.Dispatcher, Metro.ModuleID]
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@ export let TypedEventEmitter: typeof DiscordModules.Utils.TypedEventEmitter =
|
|||
2,
|
||||
]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (TypedEventEmitter = module)
|
||||
|
|
|
|||
|
|
@ -52,10 +52,6 @@ export let Design: Design = proxify(
|
|||
),
|
||||
)
|
||||
.keyAs('revenge.discord.design.Design'),
|
||||
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (Design = module)
|
||||
|
|
@ -93,9 +89,6 @@ export let FormSwitch: DiscordModules.Components.FormSwitch = proxify(() => {
|
|||
2,
|
||||
]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (FormSwitch = module.FormSwitch)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ import {
|
|||
Uncached,
|
||||
} from '../../../modules/src/caches'
|
||||
import { FilterResultFlags } from '../../../modules/src/finders/_internal'
|
||||
import { FilterFlag } from '../../../modules/src/finders/filters/constants'
|
||||
import {
|
||||
FilterFlag,
|
||||
FilterScopes,
|
||||
} from '../../../modules/src/finders/filters/constants'
|
||||
import type {
|
||||
Filter,
|
||||
FilterGenerator,
|
||||
|
|
@ -36,9 +39,7 @@ const _stores: Record<string, DiscordModules.Flux.Store> = {}
|
|||
export const Stores = new Proxy(_stores, {
|
||||
ownKeys: target => Reflect.ownKeys(target),
|
||||
get: (target, prop: string) =>
|
||||
target[prop] ??
|
||||
// @ts-expect-error: This always uses cache
|
||||
lookupModule(withStoreName(prop), { uninitialized: true })[0],
|
||||
target[prop] ?? lookupModule(withStoreName(prop))[0],
|
||||
})
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +95,14 @@ const FluxStoreLeadingDeps = [
|
|||
]
|
||||
|
||||
export type WithStore = FilterGenerator<
|
||||
<T>() => Filter<DiscordModules.Flux.Store<T>, boolean>
|
||||
<T>() => Filter<{
|
||||
Result: DiscordModules.Flux.Store<T>
|
||||
RequiresExports: boolean
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
]
|
||||
}>
|
||||
>
|
||||
|
||||
/**
|
||||
|
|
@ -115,10 +123,18 @@ export const withStore = createFilterGenerator(
|
|||
},
|
||||
() => 'revenge.discord.store',
|
||||
FilterFlag.Dynamic,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithStore
|
||||
|
||||
export type WithStoreName = FilterGenerator<
|
||||
<T>(name: string) => Filter<DiscordModules.Flux.Store<T>, true>
|
||||
<T>(name: string) => Filter<{
|
||||
Result: DiscordModules.Flux.Store<T>
|
||||
RequiresExports: true
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
]
|
||||
}>
|
||||
>
|
||||
|
||||
/**
|
||||
|
|
@ -129,6 +145,7 @@ export const withStoreName = createFilterGenerator(
|
|||
exports.getName?.length === 0 && exports.getName() === name,
|
||||
([name]) => `revenge.discord.storeName(${name})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithStoreName
|
||||
|
||||
/// STORE CACHING
|
||||
|
|
@ -146,9 +163,7 @@ waitForModules(withStore(), (store, id) => {
|
|||
|
||||
if (cache === Uncached)
|
||||
asap(() => {
|
||||
const lookup = lookupModules(withStore(), {
|
||||
uninitialized: true,
|
||||
})
|
||||
const lookup = lookupModules(withStore())
|
||||
|
||||
while (lookup.next().done);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ export let RootNavigationRef: RootNavigationRef = proxify(
|
|||
]),
|
||||
)
|
||||
.keyAs('revenge.discord.modules.mainTabsV2.RootNavigationRef'),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (RootNavigationRef = module)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ export let SettingListRenderer: SettingListRenderer = proxify(
|
|||
]),
|
||||
),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (SettingListRenderer = module)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@ export const [AppStartPerformance] = lookupModule(
|
|||
withProps<DiscordModules.AppStartPerformance>('markAndLog').and(
|
||||
withDependencies([relative(-1), null, null, 2]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
) as [DiscordModules.AppStartPerformance, Metro.ModuleID]
|
||||
|
||||
AppStartPerformance.mark('👊', 'Pre-init')
|
||||
|
|
|
|||
3
lib/externals/src/browserify.ts
vendored
3
lib/externals/src/browserify.ts
vendored
|
|
@ -17,9 +17,6 @@ export let nodeUtil: typeof import('node:util') = proxify(
|
|||
[],
|
||||
]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (nodeUtil = module)
|
||||
|
|
|
|||
3
lib/externals/src/react-native-clipboard.ts
vendored
3
lib/externals/src/react-native-clipboard.ts
vendored
|
|
@ -35,9 +35,6 @@ let ClipboardModule: typeof import('@react-native-clipboard/clipboard') =
|
|||
),
|
||||
]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) {
|
||||
|
|
|
|||
6
lib/externals/src/react-navigation.ts
vendored
6
lib/externals/src/react-navigation.ts
vendored
|
|
@ -26,9 +26,6 @@ export let ReactNavigationNative: typeof import('@react-navigation/native') =
|
|||
]),
|
||||
),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ReactNavigationNative = module)
|
||||
|
|
@ -52,9 +49,6 @@ export let ReactNavigationStack: typeof import('@react-navigation/stack') =
|
|||
.keyAs(
|
||||
'revenge.externals.ReactNavigation.ReactNavigationStack',
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (ReactNavigationStack = module)
|
||||
|
|
|
|||
3
lib/externals/src/shopify.ts
vendored
3
lib/externals/src/shopify.ts
vendored
|
|
@ -27,9 +27,6 @@ export let FlashList: typeof import('@shopify/flash-list') = proxify(
|
|||
null,
|
||||
]),
|
||||
),
|
||||
{
|
||||
uninitialized: true,
|
||||
},
|
||||
)
|
||||
|
||||
if (module) return (FlashList = module)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export interface RunFilterOptions {
|
|||
*/
|
||||
skipDefault?: boolean
|
||||
/**
|
||||
* Whether to allow initializing modules to check their exports.
|
||||
* Whether to allow initializing modules to confirm their exports.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
|
|
@ -72,7 +72,7 @@ export const FilterResultFlagToHumanReadable: Record<FilterResultFlag, string> =
|
|||
// 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<any, false>,
|
||||
filter: Filter<{ Result: any; RequiresExports: false; Scopes: any[] }>,
|
||||
id: Metro.ModuleID,
|
||||
): FilterResultFlag | undefined
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { FilterFlag } from './constants'
|
||||
import { createFilterGenerator } from './utils'
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { LogicalAnd } from '@revenge-mod/utils/types'
|
||||
import type {
|
||||
Filter,
|
||||
FilterBase,
|
||||
FilterGenerator,
|
||||
FilterRequiresExports,
|
||||
FilterResult,
|
||||
FilterInfoOf,
|
||||
MergeFilterInfo,
|
||||
UnionFilterInfo,
|
||||
} from '.'
|
||||
|
||||
const compositeHandler = <G extends (a: FilterBase, b: FilterBase) => any>(
|
||||
|
|
@ -38,19 +38,19 @@ export type And = FilterGenerator<
|
|||
<F1 extends FilterBase, F2 extends FilterBase>(
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
) => Filter<
|
||||
FilterResult<F1> & FilterResult<F2>,
|
||||
LogicalAnd<FilterRequiresExports<F1>, FilterRequiresExports<F2>>
|
||||
>
|
||||
) => Filter<MergeFilterInfo<FilterInfoOf<F1>, FilterInfoOf<F2>>>
|
||||
>
|
||||
|
||||
const andKeyGenerator = ([a, b]: Parameters<And>) =>
|
||||
`revenge.and(${a.key},${b.key})`
|
||||
|
||||
const andScopesGenerator = ([a, b]: Parameters<And>) => a.scopes | b.scopes
|
||||
|
||||
const sameFlagsAnd = createFilterGenerator(
|
||||
([a, b], id, exports) => a(id, exports) && b(id, exports),
|
||||
andKeyGenerator,
|
||||
([a]) => a.flags,
|
||||
andScopesGenerator,
|
||||
) as And
|
||||
|
||||
const andFallbackCache = new WeakMap<FilterBase, Set<Metro.ModuleID>>()
|
||||
|
|
@ -82,6 +82,7 @@ const dynamicFlagsAnd = createFilterGenerator(
|
|||
},
|
||||
andKeyGenerator,
|
||||
FilterFlag.Dynamic,
|
||||
andScopesGenerator,
|
||||
) as And
|
||||
|
||||
/**
|
||||
|
|
@ -121,6 +122,10 @@ export const and = Object.assign(
|
|||
sameFlagsAnd.flagsFor,
|
||||
dynamicFlagsAnd.flagsFor,
|
||||
),
|
||||
defaultScopesFor: compositeArrayHandler(
|
||||
sameFlagsAnd.defaultScopesFor,
|
||||
dynamicFlagsAnd.defaultScopesFor,
|
||||
),
|
||||
},
|
||||
) satisfies And
|
||||
|
||||
|
|
@ -128,19 +133,19 @@ export type Or = FilterGenerator<
|
|||
<F1 extends FilterBase, F2 extends FilterBase>(
|
||||
f1: F1,
|
||||
f2: F2,
|
||||
) => Filter<
|
||||
FilterResult<F1> | FilterResult<F2>,
|
||||
LogicalAnd<FilterRequiresExports<F1>, FilterRequiresExports<F2>>
|
||||
>
|
||||
) => Filter<UnionFilterInfo<FilterInfoOf<F1>, FilterInfoOf<F2>>>
|
||||
>
|
||||
|
||||
const orKeyGenerator = ([a, b]: Parameters<Or>) =>
|
||||
`revenge.or(${a.key},${b.key})`
|
||||
|
||||
const orScopesGenerator = ([a, b]: Parameters<Or>) => a.scopes | b.scopes
|
||||
|
||||
const sameFlagOr = createFilterGenerator(
|
||||
([a, b], id, exports) => a(id, exports) || b(id, exports),
|
||||
orKeyGenerator,
|
||||
([a]) => a.flags,
|
||||
orScopesGenerator,
|
||||
) as Or
|
||||
|
||||
const dynamicFlagOr = createFilterGenerator(
|
||||
|
|
@ -151,6 +156,7 @@ const dynamicFlagOr = createFilterGenerator(
|
|||
},
|
||||
orKeyGenerator,
|
||||
FilterFlag.Dynamic,
|
||||
orScopesGenerator,
|
||||
) as Or
|
||||
|
||||
/**
|
||||
|
|
@ -185,5 +191,9 @@ export const or = Object.assign(
|
|||
sameFlagOr.flagsFor,
|
||||
dynamicFlagOr.flagsFor,
|
||||
),
|
||||
defaultScopesFor: compositeArrayHandler(
|
||||
sameFlagOr.defaultScopesFor,
|
||||
dynamicFlagOr.defaultScopesFor,
|
||||
),
|
||||
},
|
||||
) satisfies Or
|
||||
|
|
|
|||
|
|
@ -15,3 +15,50 @@ export const FilterFlag = {
|
|||
* @see {@link FilterFlag}
|
||||
*/
|
||||
export type FilterFlag = number
|
||||
|
||||
/**
|
||||
* Scopes to limit filters to certain module states.
|
||||
*/
|
||||
export const FilterScopes = {
|
||||
/**
|
||||
* Include all modules (both initialized and uninitialized, including blacklisted).
|
||||
* This overrides {@link FilterScopes.Uninitialized} and {@link FilterScopes.Initialized}.
|
||||
*/
|
||||
All: 1,
|
||||
/**
|
||||
* Include uninitialized modules in the search.
|
||||
*/
|
||||
Uninitialized: 2,
|
||||
/**
|
||||
* Include initialized modules from the search.
|
||||
*/
|
||||
Initialized: 4,
|
||||
} as const
|
||||
|
||||
export type FilterScope = (typeof FilterScopes)[keyof typeof FilterScopes]
|
||||
|
||||
/**
|
||||
* @see {@link FilterScopes}
|
||||
*/
|
||||
export type FilterScopeValue = number
|
||||
|
||||
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.
|
||||
*/
|
||||
Scopes: FilterScope[]
|
||||
}
|
||||
|
||||
export interface DefaultFilterInfo extends FilterInfo {
|
||||
Result: any
|
||||
RequiresExports: boolean
|
||||
Scopes: FilterScope[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { getCurrentStack } from '@revenge-mod/utils/error'
|
||||
import { getModuleDependencies } from '../../metro/utils'
|
||||
import { FilterFlag } from './constants'
|
||||
import { FilterFlag, FilterScopes } from './constants'
|
||||
import { createFilterGenerator } from './utils'
|
||||
import type { Metro } from '../../types'
|
||||
import type { Filter, FilterGenerator } from './utils'
|
||||
|
|
@ -62,13 +62,21 @@ export const withDependencies = createFilterGenerator<
|
|||
([deps], id) => depCompare(getModuleDependencies(id)!, deps, id, id),
|
||||
deps => `revenge.deps(${depGenFilterKey(deps)})`,
|
||||
FilterFlag.Dynamic,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithDependencies
|
||||
|
||||
withDependencies.loose = loose
|
||||
withDependencies.relative = relative
|
||||
|
||||
type WithDependencies = FilterGenerator<
|
||||
<T>(deps: ComparableDependencyMap) => Filter<T, false>
|
||||
<T>(deps: ComparableDependencyMap) => Filter<{
|
||||
Result: T
|
||||
RequiresExports: false
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
]
|
||||
}>
|
||||
> & {
|
||||
loose: typeof loose
|
||||
relative: typeof relative
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import { FilterFlag } from './constants'
|
||||
import { FilterFlag, FilterScopes } from './constants'
|
||||
import { createFilterGenerator } from './utils'
|
||||
import type { Filter, FilterGenerator } from './utils'
|
||||
|
||||
export * from './constants'
|
||||
export * from './utils'
|
||||
|
||||
type FilterRequiringExports<T> = Filter<{
|
||||
Result: T
|
||||
RequiresExports: true
|
||||
Scopes: [typeof FilterScopes.Initialized]
|
||||
}>
|
||||
|
||||
/**
|
||||
* Filter modules by their exports having all of the specified properties.
|
||||
*
|
||||
|
|
@ -33,13 +39,14 @@ export const withProps = createFilterGenerator<Parameters<WithProps>>(
|
|||
},
|
||||
props => `revenge.props(${props.join(',')})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithProps
|
||||
|
||||
export type WithProps = FilterGenerator<
|
||||
<T extends Record<string, any> = Record<string, any>>(
|
||||
prop: keyof T,
|
||||
...props: Array<keyof T>
|
||||
) => Filter<T, true>
|
||||
) => FilterRequiringExports<T>
|
||||
>
|
||||
|
||||
/**
|
||||
|
|
@ -58,13 +65,14 @@ export const withoutProps = createFilterGenerator<Parameters<WithoutProps>>(
|
|||
},
|
||||
props => `revenge.withoutProps(${props.join(',')})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithoutProps
|
||||
|
||||
export type WithoutProps = FilterGenerator<
|
||||
<T extends Record<string, any>>(
|
||||
prop: string,
|
||||
...props: string[]
|
||||
) => Filter<T, true>
|
||||
) => FilterRequiringExports<T>
|
||||
>
|
||||
|
||||
/**
|
||||
|
|
@ -87,10 +95,11 @@ export const withSingleProp = createFilterGenerator<Parameters<WithSingleProp>>(
|
|||
},
|
||||
([prop]) => `revenge.singleProp(${prop})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithSingleProp
|
||||
|
||||
export type WithSingleProp = FilterGenerator<
|
||||
<T extends Record<string, any>>(prop: keyof T) => Filter<T, true>
|
||||
<T extends Record<string, any>>(prop: keyof T) => FilterRequiringExports<T>
|
||||
>
|
||||
|
||||
/**
|
||||
|
|
@ -127,10 +136,11 @@ export const withName = createFilterGenerator<Parameters<WithName>>(
|
|||
([name], _, exports) => exports.name === name,
|
||||
([name]) => `revenge.name(${name})`,
|
||||
FilterFlag.RequiresExports,
|
||||
FilterScopes.Initialized,
|
||||
) as WithName
|
||||
|
||||
export type WithName = FilterGenerator<
|
||||
<T extends object = object>(name: string) => Filter<T, true>
|
||||
<T extends object = object>(name: string) => FilterRequiringExports<T>
|
||||
>
|
||||
|
||||
export * from './composite'
|
||||
|
|
|
|||
|
|
@ -1,84 +1,116 @@
|
|||
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 { FilterFlag } from './constants'
|
||||
import type {
|
||||
DefaultFilterInfo,
|
||||
FilterFlag,
|
||||
FilterInfo,
|
||||
FilterScope,
|
||||
FilterScopes as FilterScopesObject,
|
||||
FilterScopeValue,
|
||||
} from './constants'
|
||||
|
||||
export type FilterResult<F> = F extends Filter<infer R, boolean>
|
||||
? R
|
||||
: F extends FilterBase<infer R>
|
||||
? R
|
||||
: never
|
||||
export type FilterResult<F> = F extends Filter<infer I> ? I['Result'] : never
|
||||
|
||||
export type FilterRequiresExports<F> = F extends Filter<any, infer RE>
|
||||
? RE
|
||||
: F extends FilterBase<any, infer RE>
|
||||
? RE
|
||||
: never
|
||||
export type FilterRequiresExports<F> = F extends Filter<infer I>
|
||||
? I['RequiresExports']
|
||||
: never
|
||||
|
||||
export interface FilterBase<
|
||||
_Result = any,
|
||||
RequiresExports extends boolean = boolean,
|
||||
> {
|
||||
export type FilterInfoOf<F> = F extends Filter<infer I> ? I : FilterInfo
|
||||
|
||||
export interface FilterBase<Info extends FilterInfo = DefaultFilterInfo> {
|
||||
(
|
||||
...args: If<
|
||||
RequiresExports,
|
||||
Info['RequiresExports'],
|
||||
[id: Metro.ModuleID, exports: Metro.ModuleExports],
|
||||
[id: Metro.ModuleID, exports?: never]
|
||||
>
|
||||
): boolean
|
||||
key: string
|
||||
flags: If<
|
||||
RequiresExports,
|
||||
Info['RequiresExports'],
|
||||
(typeof FilterFlag)['RequiresExports'],
|
||||
FilterFlag
|
||||
>
|
||||
scopes: FilterScopeValue
|
||||
}
|
||||
|
||||
export type Filter<
|
||||
Result = any,
|
||||
RequiresExports extends boolean = boolean,
|
||||
> = FilterHelpers & FilterBase<Result, RequiresExports>
|
||||
export type Filter<Info extends FilterInfo = DefaultFilterInfo> =
|
||||
FilterHelpers<Info> & FilterBase<Info>
|
||||
|
||||
export interface FilterHelpers {
|
||||
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']]
|
||||
}
|
||||
|
||||
export interface FilterHelpers<Info extends FilterInfo = DefaultFilterInfo> {
|
||||
/**
|
||||
* Manually the key for this filter.
|
||||
*
|
||||
* **Don't use this unless you know what you're doing.** Only API exports should be using
|
||||
* **Don't use this unless you know what you're doing.** Only API exports should be using this.
|
||||
*
|
||||
* @param key The key to set for this filter.
|
||||
*/
|
||||
keyAs<T extends FilterBase>(this: T, key: string): T
|
||||
keyAs<T extends FilterBase<any>>(this: T, key: string): T
|
||||
/**
|
||||
* Combines this filter with another filter, returning a new filter that matches if **both** filters match.
|
||||
*
|
||||
* @param filter The filter to combine with.
|
||||
*/
|
||||
and<T extends FilterBase, F extends FilterBase>(
|
||||
and<T extends FilterBase<any>, F extends FilterBase<any>>(
|
||||
this: T,
|
||||
filter: F,
|
||||
): Filter<
|
||||
FilterResult<T> & FilterResult<F>,
|
||||
LogicalAnd<FilterRequiresExports<T>, FilterRequiresExports<F>>
|
||||
>
|
||||
): Filter<MergeFilterInfo<Info, FilterInfoOf<F>>>
|
||||
/**
|
||||
* Combines this filter with another filter, returning a new filter that matches if **either** filter matches.
|
||||
*
|
||||
* Note that exportsless filters must come first to avoid gotchas with uninitialized modules.
|
||||
*
|
||||
* @param filter The filter to combine with.
|
||||
*/
|
||||
or<T extends FilterBase, F extends FilterBase>(
|
||||
or<T extends Filter<Info>, F extends FilterBase<any>>(
|
||||
this: T,
|
||||
filter: F,
|
||||
): Filter<UnionFilterInfo<Info, FilterInfoOf<F>>>
|
||||
/**
|
||||
* Creates a new instance of this filter.
|
||||
*/
|
||||
// biome-ignore format: Preserve special property name syntax for 'new' keyword
|
||||
'new'(this: Filter<Info>): Filter<Info>
|
||||
/**
|
||||
* Scopes this filter to match specific modules.
|
||||
*
|
||||
* @param scopes The scopes of modules to match.
|
||||
*/
|
||||
scope<T extends Filter<Info>, const S extends FilterScope[]>(
|
||||
this: T,
|
||||
...scopes: If<
|
||||
Info['RequiresExports'],
|
||||
[typeof FilterScopesObject.Initialized],
|
||||
S
|
||||
>
|
||||
): Filter<
|
||||
FilterResult<T> | FilterResult<F>,
|
||||
LogicalAnd<FilterRequiresExports<T>, FilterRequiresExports<F>>
|
||||
Info & {
|
||||
Scopes: If<
|
||||
Info['RequiresExports'],
|
||||
[typeof FilterScopesObject.Initialized],
|
||||
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
|
||||
}
|
||||
|
||||
const Helpers: FilterHelpers = Object.setPrototypeOf(
|
||||
|
|
@ -93,6 +125,22 @@ const Helpers: FilterHelpers = Object.setPrototypeOf(
|
|||
or(filter) {
|
||||
return or(this, filter)
|
||||
},
|
||||
new() {
|
||||
const newFilter = ((
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
) => this(id, exports)) as typeof this
|
||||
|
||||
Object.assign(newFilter, this)
|
||||
|
||||
return Object.setPrototypeOf(newFilter, Helpers)
|
||||
},
|
||||
scope(...scopes) {
|
||||
const filter = this.new()
|
||||
filter.scopes = 0
|
||||
for (const scope of scopes) filter.scopes |= scope
|
||||
return filter
|
||||
},
|
||||
} satisfies FilterHelpers,
|
||||
Function.prototype,
|
||||
)
|
||||
|
|
@ -103,6 +151,7 @@ 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
|
||||
|
|
@ -126,13 +175,15 @@ export function createFilterGenerator<A extends any[]>(
|
|||
) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
flagFor: ((args: A) => FilterFlag) | FilterFlag,
|
||||
): FilterGenerator<(...args: A) => Filter<any, true>>
|
||||
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,
|
||||
): FilterGenerator<(...args: A) => Filter<any, false>>
|
||||
defaultScopesFor?: ((args: A) => FilterScopeValue) | FilterScopeValue,
|
||||
): FilterGenerator<(...args: A) => Filter>
|
||||
|
||||
export function createFilterGenerator<A extends any[]>(
|
||||
filter: (
|
||||
|
|
@ -142,17 +193,24 @@ export function createFilterGenerator<A extends any[]>(
|
|||
) => 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'
|
||||
|
||||
const generator: GeneratorType = (...args: A) => {
|
||||
const filter_ = ((id: Metro.ModuleID, exports?: Metro.ModuleExports) =>
|
||||
filter(args, id, exports)) as ReturnType<GeneratorType>
|
||||
|
||||
filter_.key = keyFor(args)
|
||||
filter_.flags = isFlagsStatic ? flagFor : flagFor(args)
|
||||
filter_.scopes = isDefaultScopesStatic
|
||||
? defaultScopes
|
||||
: defaultScopes(args)
|
||||
return Object.setPrototypeOf(filter_, Helpers)
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +218,10 @@ export function createFilterGenerator<A extends any[]>(
|
|||
? () => flagFor
|
||||
: (args: A) => flagFor(args)
|
||||
|
||||
generator.defaultScopesFor = isDefaultScopesStatic
|
||||
? () => defaultScopes
|
||||
: defaultScopes
|
||||
|
||||
generator.keyFor = keyFor
|
||||
|
||||
return generator
|
||||
|
|
|
|||
|
|
@ -5,25 +5,21 @@ import {
|
|||
lookupModuleWithImportedPath,
|
||||
} from './lookup'
|
||||
import { waitForModules, waitForModuleWithImportedPath } from './wait'
|
||||
import type { If } from '@revenge-mod/utils/types'
|
||||
import type { Metro } from '../types'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
import type { LookupModulesOptions } from './lookup'
|
||||
import type { WaitForModulesOptions, WaitForModulesResult } from './wait'
|
||||
|
||||
export type GetModulesOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
Uninitialized extends boolean = boolean,
|
||||
All extends boolean = boolean,
|
||||
> = WaitForModulesOptions<ReturnNamespace> &
|
||||
LookupModulesOptions<ReturnNamespace, Uninitialized, All, true> & {
|
||||
/**
|
||||
* The maximum number of modules to get.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
max?: number
|
||||
}
|
||||
export type GetModulesOptions<ReturnNamespace extends boolean = boolean> =
|
||||
WaitForModulesOptions<ReturnNamespace> &
|
||||
LookupModulesOptions<ReturnNamespace, true> & {
|
||||
/**
|
||||
* The maximum number of modules to get.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
max?: number
|
||||
}
|
||||
|
||||
export type GetModulesResult<
|
||||
F extends Filter,
|
||||
|
|
@ -64,12 +60,7 @@ export function getModules<F extends Filter>(
|
|||
callback: GetModulesCallback<FilterResult<F>>,
|
||||
): GetModulesUnsubscribeFunction
|
||||
|
||||
export function getModules<
|
||||
F extends Filter,
|
||||
const O extends F extends Filter<any, infer RE>
|
||||
? If<RE, GetModulesOptions<boolean, boolean, false>, GetModulesOptions>
|
||||
: never,
|
||||
>(
|
||||
export function getModules<F extends Filter, const O extends GetModulesOptions>(
|
||||
filter: F,
|
||||
callback: GetModulesCallback<GetModulesResult<F, O>>,
|
||||
options: O,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import {
|
|||
FilterResultFlagToHumanReadable,
|
||||
runFilter,
|
||||
} from './_internal'
|
||||
import type { If, Nullish } from '@revenge-mod/utils/types'
|
||||
import { FilterScopes } from './filters'
|
||||
import type { If, Not } from '@revenge-mod/utils/types'
|
||||
import type { MaybeDefaultExportMatched, Metro } from '../types'
|
||||
import type {
|
||||
FilterResultFlag,
|
||||
|
|
@ -25,55 +26,8 @@ import type {
|
|||
} from './_internal'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
|
||||
type LookupModulesOptionsWithAll<A extends boolean> = If<
|
||||
A,
|
||||
{
|
||||
/**
|
||||
* Whether to include all modules in the lookup, including blacklisted ones.
|
||||
*
|
||||
* **This overrides {@link BaseLookupModulesOptions.initialized} and {@link BaseLookupModulesOptions.uninitialized}.**
|
||||
*/
|
||||
all: A
|
||||
},
|
||||
{
|
||||
/**
|
||||
* You can only use `all` with exportsless filters!
|
||||
*/
|
||||
all?: false
|
||||
}
|
||||
>
|
||||
|
||||
type LookupModulesOptionsWithInitializedUninitialized<U extends boolean> = {
|
||||
/**
|
||||
* Whether to include initialized modules in the lookup.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
initialized?: boolean
|
||||
} & If<
|
||||
U,
|
||||
{
|
||||
/**
|
||||
* Whether to include uninitialized modules in the lookup.
|
||||
*
|
||||
* Set {@link BaseLookupModulesOptions.initialize} `true` to initialize uninitialized modules.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
uninitialized: U
|
||||
},
|
||||
{
|
||||
/**
|
||||
* You can only use `uninitialized` with exportsless filters!
|
||||
*/
|
||||
uninitialized?: false
|
||||
}
|
||||
>
|
||||
|
||||
export type LookupModulesOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
Uninitialized extends boolean = boolean,
|
||||
All extends boolean = boolean,
|
||||
Initialize extends boolean = boolean,
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> & {
|
||||
/**
|
||||
|
|
@ -81,42 +35,33 @@ export type LookupModulesOptions<
|
|||
*/
|
||||
cached?: boolean
|
||||
} & If<
|
||||
Initialize,
|
||||
Not<Initialize>,
|
||||
{
|
||||
/**
|
||||
* Whether to initialize matching uninitialized modules.
|
||||
*
|
||||
* **This will initialize any modules that match the exportsless filter and may cause unintended side effects.**
|
||||
*/
|
||||
initialize?: Initialize
|
||||
},
|
||||
{
|
||||
initialize: false
|
||||
}
|
||||
> &
|
||||
If<
|
||||
All,
|
||||
LookupModulesOptionsWithAll<All> & {
|
||||
[K in keyof LookupModulesOptionsWithInitializedUninitialized<Uninitialized>]?: never
|
||||
},
|
||||
LookupModulesOptionsWithInitializedUninitialized<Uninitialized> & {
|
||||
[K in keyof LookupModulesOptionsWithAll<All>]?: never
|
||||
}
|
||||
{ initialize?: true }
|
||||
>
|
||||
|
||||
export type LookupModulesResult<
|
||||
F extends Filter,
|
||||
O extends LookupModulesOptions,
|
||||
> = [
|
||||
exports: O extends LookupModulesOptions<boolean, boolean, boolean, false>
|
||||
? LookupFilterResult<F, O> | Nullish
|
||||
: LookupFilterResult<F, O>,
|
||||
id: Metro.ModuleID,
|
||||
]
|
||||
> = [exports: LookupFilterResult<F, O>, id: Metro.ModuleID]
|
||||
|
||||
type LookupFilterResult<
|
||||
F extends Filter,
|
||||
O extends LookupModulesOptions,
|
||||
> = O extends LookupModulesOptions<any, false>
|
||||
? InitializedLookupFilterResult<F, O> | undefined
|
||||
: InitializedLookupFilterResult<F, O>
|
||||
|
||||
type InitializedLookupFilterResult<
|
||||
F extends Filter,
|
||||
O extends LookupModulesOptions,
|
||||
> = O extends RunFilterReturnExportsOptions<true>
|
||||
? MaybeDefaultExportMatched<FilterResult<F>>
|
||||
: FilterResult<F>
|
||||
|
|
@ -128,8 +73,6 @@ type LookupNotFoundResult = typeof NotFoundResult
|
|||
/**
|
||||
* Lookup modules.
|
||||
*
|
||||
* You can lookup uninitialized modules by passing `options.uninitialized` when filtering via exportsless filters (eg. `withDependencies`).
|
||||
*
|
||||
* @param filter The filter to use.
|
||||
* @param options The options to use for the lookup.
|
||||
* @returns A generator that yields the module exports that match the filter.
|
||||
|
|
@ -147,23 +90,10 @@ export function lookupModules<F extends Filter>(
|
|||
|
||||
export function lookupModules<
|
||||
F extends Filter,
|
||||
const O extends F extends Filter<any, infer RE>
|
||||
? If<
|
||||
RE,
|
||||
LookupModulesOptions<boolean, false, false>,
|
||||
LookupModulesOptions
|
||||
>
|
||||
: never,
|
||||
const O extends LookupModulesOptions,
|
||||
>(filter: F, options: O): Generator<LookupModulesResult<F, O>, undefined>
|
||||
|
||||
export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
||||
if (
|
||||
__DEV__ &&
|
||||
filter.key.includes('revenge.deps') &&
|
||||
!(options?.uninitialized ?? false)
|
||||
)
|
||||
DEBUG_warnFilterWithoutUninitialized(filter.key)
|
||||
|
||||
let notFound = true
|
||||
let cached: Set<Metro.ModuleID> | undefined
|
||||
|
||||
|
|
@ -199,8 +129,10 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
// Full lookup
|
||||
if (options?.all) {
|
||||
const scopes = filter.scopes
|
||||
const includeAll = scopes & FilterScopes.All
|
||||
|
||||
if (includeAll) {
|
||||
for (const id of mList.keys()) {
|
||||
// biome-ignore lint/complexity/useOptionalChain: Hot path should be optimized
|
||||
if (cached && cached.has(id)) continue
|
||||
|
|
@ -228,12 +160,11 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const includeInit = scopes & FilterScopes.Initialized
|
||||
const includeUninit = scopes & FilterScopes.Uninitialized
|
||||
|
||||
if (notFound) cacheFilterNotFound(filter.key) // Full lookup, and still not found!
|
||||
}
|
||||
// Partial lookup
|
||||
else {
|
||||
if (options?.initialized ?? true)
|
||||
if (includeInit)
|
||||
for (const id of mInitialized) {
|
||||
// biome-ignore lint/complexity/useOptionalChain: Hot path should be optimized
|
||||
if (cached && cached.has(id)) continue
|
||||
|
|
@ -253,7 +184,7 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
if (options?.uninitialized)
|
||||
if (includeUninit)
|
||||
for (const id of mUninitialized) {
|
||||
// biome-ignore lint/complexity/useOptionalChain: Hot path should be optimized
|
||||
if (cached && cached.has(id)) continue
|
||||
|
|
@ -277,8 +208,10 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__)
|
||||
if (notFound) DEBUG_warnLookupNoMatch(filter.key)
|
||||
// Cache if nothing was found in a full lookup
|
||||
if (notFound && includeAll) cacheFilterNotFound(filter.key)
|
||||
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__ && notFound)
|
||||
DEBUG_warnLookupNoMatch(filter.key)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -301,23 +234,10 @@ export function lookupModule<F extends Filter>(
|
|||
|
||||
export function lookupModule<
|
||||
F extends Filter,
|
||||
const O extends F extends Filter<any, infer RE>
|
||||
? If<
|
||||
RE,
|
||||
LookupModulesOptions<boolean, false, false>,
|
||||
LookupModulesOptions
|
||||
>
|
||||
: never,
|
||||
const O extends LookupModulesOptions,
|
||||
>(filter: F, options: O): LookupModulesResult<F, O> | LookupNotFoundResult
|
||||
|
||||
export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
||||
if (
|
||||
__DEV__ &&
|
||||
filter.key.includes('revenge.deps') &&
|
||||
!(options?.uninitialized ?? false)
|
||||
)
|
||||
DEBUG_warnFilterWithoutUninitialized(filter.key)
|
||||
|
||||
if (options?.cached ?? true) {
|
||||
const notInit = !(options?.initialize ?? true)
|
||||
|
||||
|
|
@ -345,8 +265,10 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
// Full lookup
|
||||
if (options?.all) {
|
||||
const scopes = filter.scopes
|
||||
const includeAll = scopes & FilterScopes.All
|
||||
|
||||
if (includeAll) {
|
||||
for (const id of mList.keys()) {
|
||||
const flag = runFilter(
|
||||
filter,
|
||||
|
|
@ -369,17 +291,11 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const includeInit = scopes & FilterScopes.Initialized
|
||||
const includeUninit = scopes & FilterScopes.Uninitialized
|
||||
|
||||
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__)
|
||||
DEBUG_warnLookupNoMatch(filter.key)
|
||||
|
||||
cacheFilterNotFound(filter.key) // Full lookup, and still not found!
|
||||
|
||||
return NotFoundResult
|
||||
}
|
||||
// Partial lookup
|
||||
else {
|
||||
if (options?.initialized ?? true)
|
||||
if (includeInit)
|
||||
for (const id of mInitialized) {
|
||||
const exports = getInitializedModuleExports(id)
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
|
|
@ -394,7 +310,7 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
if (options?.uninitialized)
|
||||
if (includeUninit)
|
||||
for (const id of mUninitialized) {
|
||||
const flag = runFilter(filter, id, undefined, options)
|
||||
if (flag) {
|
||||
|
|
@ -413,6 +329,8 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
// Cache if nothing was found in a full lookup
|
||||
if (includeAll) cacheFilterNotFound(filter.key)
|
||||
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__) DEBUG_warnLookupNoMatch(filter.key)
|
||||
|
||||
return NotFoundResult
|
||||
|
|
@ -478,15 +396,3 @@ function DEBUG_warnLookupNoMatch(key: string) {
|
|||
|
||||
nativeLoggingHook(`\u001b[31mFailed lookup: ${key}\n${stack}\u001b[0m`, 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Warns the developer about a potentially bad filter for lookups that do not have uninitialized modules.
|
||||
*/
|
||||
function DEBUG_warnFilterWithoutUninitialized(key: string) {
|
||||
const stack = getCurrentStack()
|
||||
|
||||
nativeLoggingHook(
|
||||
`\u001b[33mPotentially bad filter without uninitialized modules lookup: ${key}\n${stack}\u001b[0m`,
|
||||
2,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
FilterResultFlagToHumanReadable,
|
||||
runFilter,
|
||||
} from './_internal'
|
||||
import { FilterScopes } from './filters'
|
||||
import type { MaybeDefaultExportMatched, Metro } from '../types'
|
||||
import type {
|
||||
FilterResultFlag,
|
||||
|
|
@ -18,24 +19,12 @@ import type {
|
|||
} from './_internal'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
|
||||
export interface BaseWaitForModulesOptions<All extends boolean = boolean> {
|
||||
/**
|
||||
* Whether to include all modules, including blacklisted ones.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
all?: All
|
||||
}
|
||||
|
||||
export type WaitForModulesUnsubscribeFunction = () => void
|
||||
|
||||
export type WaitForModulesCallback<T> = (exports: T, id: Metro.ModuleID) => any
|
||||
|
||||
export type WaitForModulesOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
All extends boolean = boolean,
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> &
|
||||
BaseWaitForModulesOptions<All> & {
|
||||
export type WaitForModulesOptions<ReturnNamespace extends boolean = boolean> =
|
||||
RunFilterReturnExportsOptions<ReturnNamespace> & {
|
||||
/**
|
||||
* Use cached results **only** (if possible).
|
||||
* If there is no cache result, this works as if you did not pass this option at all.
|
||||
|
|
@ -81,9 +70,7 @@ export function waitForModules<F extends Filter>(
|
|||
): WaitForModulesUnsubscribeFunction
|
||||
|
||||
export function waitForModules<
|
||||
F extends O extends WaitForModulesOptions<boolean, true>
|
||||
? Filter<any, false>
|
||||
: Filter,
|
||||
F extends Filter,
|
||||
O extends WaitForModulesOptions,
|
||||
>(
|
||||
filter: F,
|
||||
|
|
@ -144,7 +131,7 @@ export function waitForModules(
|
|||
)
|
||||
|
||||
return onAnyModuleInitialized(
|
||||
options?.all
|
||||
filter.scopes & FilterScopes.All
|
||||
? (id, exports) => {
|
||||
const flag = runFilter(filter, id, exports, options)
|
||||
if (flag) {
|
||||
|
|
@ -187,7 +174,6 @@ export function waitForModules(
|
|||
*
|
||||
* @param path The path to wait for.
|
||||
* @param callback The callback to call once the module is initialized.
|
||||
* @param options The options to use for the wait.
|
||||
* @returns A function to unsubscribe.
|
||||
*
|
||||
* @example
|
||||
|
|
@ -203,14 +189,13 @@ export function waitForModules(
|
|||
export function waitForModuleWithImportedPath<T = any>(
|
||||
path: string,
|
||||
callback: WaitForModulesCallback<T>,
|
||||
options?: BaseWaitForModulesOptions,
|
||||
): WaitForModulesUnsubscribeFunction {
|
||||
const unsub = onModuleFinishedImporting((id, cmpPath) => {
|
||||
if (path === cmpPath) {
|
||||
unsub()
|
||||
// Module is not fully initialized yet, so we need to wait for it
|
||||
onModuleInitialized(id, (id, exports) => {
|
||||
if (!options?.all || mInitialized.has(id)) callback(exports, id)
|
||||
callback(exports, id)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ import {
|
|||
ReactModuleId,
|
||||
ReactNativeModuleId,
|
||||
} from '@revenge-mod/react'
|
||||
import { FilterFlag } from '../../modules/src/finders/filters/constants'
|
||||
import {
|
||||
FilterFlag,
|
||||
FilterScopes,
|
||||
} from '../../modules/src/finders/filters/constants'
|
||||
import type {
|
||||
Filter,
|
||||
FilterGenerator,
|
||||
|
|
@ -43,22 +46,24 @@ export type WithGeneratedIconComponent = FilterGenerator<
|
|||
<N extends string>(
|
||||
name: N,
|
||||
...assets: string[]
|
||||
) => Filter<{ [K in N]: FC<any> }>
|
||||
) => Filter<{
|
||||
Result: { [K in N]: FC<any> }
|
||||
RequiresExports: boolean
|
||||
Scopes: [
|
||||
typeof FilterScopes.Uninitialized,
|
||||
typeof FilterScopes.Initialized,
|
||||
]
|
||||
}>
|
||||
>
|
||||
|
||||
/**
|
||||
* Filter by icon component name and asset names.
|
||||
*
|
||||
* **Make sure to set `uninitialized: true` when using this filter in `lookupModule`!**
|
||||
*
|
||||
* @param names The component name, then the asset names if the component has multiple assets. *
|
||||
* @example
|
||||
* ```ts
|
||||
* const [CopyIconModule] = lookupModule(
|
||||
* withGeneratedIconComponent('CopyIcon'),
|
||||
* {
|
||||
* uninitialized: true,
|
||||
* }
|
||||
* )
|
||||
* if (CopyIconModule) {
|
||||
* const { CopyIcon } = CopyIconModule
|
||||
|
|
@ -73,9 +78,6 @@ export type WithGeneratedIconComponent = FilterGenerator<
|
|||
* 'CircleXIcon-secondary',
|
||||
* 'CircleXIcon-primary',
|
||||
* ),
|
||||
* {
|
||||
* uninitialized: true,
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
|
|
@ -119,6 +121,7 @@ export const withGeneratedIconComponent = createFilterGenerator<
|
|||
},
|
||||
names => `revenge.utils.discord.generatedIconComponent(${names.join(',')})`,
|
||||
FilterFlag.Dynamic,
|
||||
FilterScopes.Uninitialized | FilterScopes.Initialized,
|
||||
) as WithGeneratedIconComponent
|
||||
|
||||
/**
|
||||
|
|
@ -141,9 +144,7 @@ export function lookupGeneratedIconComponent<N extends string>(
|
|||
if (__DEV__ && badFind) return
|
||||
}
|
||||
|
||||
const [module] = lookupModule(withGeneratedIconComponent(...names), {
|
||||
uninitialized: true,
|
||||
})
|
||||
const [module] = lookupModule(withGeneratedIconComponent(...names))
|
||||
|
||||
return module?.[names[0]] as FC<any> | undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export type Nullish = null | undefined
|
||||
export type If<T, Then, Else> = T extends true ? Then : Else
|
||||
export type Not<T extends boolean> = T extends true ? false : true
|
||||
export type AnyObject = Record<any, any>
|
||||
export type LogicalOr<T1, T2> = T1 extends true
|
||||
? true
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue