feat(lib/modules)!: add new options for lookup finders, include prefix from finders options, make utils functions safe again

This commit is contained in:
PalmDevs 2025-07-04 21:38:37 +07:00
parent 22a931de51
commit 2cff8ebf07
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
16 changed files with 226 additions and 156 deletions

View file

@ -40,7 +40,7 @@ export let ActionSheetActionCreators: DiscordModules.Actions.ActionSheetActionCr
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)
@ -66,7 +66,7 @@ export let AlertActionCreators: DiscordModules.Actions.AlertActionCreators =
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)
@ -91,7 +91,7 @@ export let ToastActionCreators: DiscordModules.Actions.ToastActionCreators =
byDependencies([DispatcherModuleId, 2]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -28,7 +28,7 @@ export const [Dispatcher, DispatcherModuleId] = lookupModule(
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
) as [DiscordModules.Flux.Dispatcher, Metro.ModuleID]

View file

@ -25,7 +25,7 @@ export let TypedEventEmitter: typeof DiscordModules.Utils.TypedEventEmitter =
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -34,7 +34,7 @@ export let Design: Design = proxify(
),
),
{
includeUninitialized: true,
uninitialized: true,
},
)
@ -75,7 +75,7 @@ export let FormSwitch: DiscordModules.Components.FormSwitch = proxify(() => {
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -26,7 +26,7 @@ export let RootNavigationRef: RootNavigationRef = proxify(
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -34,7 +34,7 @@ export let SettingListRenderer: SettingListRenderer = proxify(
),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -16,7 +16,7 @@ export const [AppStartPerformance] = lookupModule(
byDependencies([relative(-1), undefined, undefined, 2]),
),
{
includeUninitialized: true,
uninitialized: true,
},
) as [DiscordModules.AppStartPerformance, Metro.ModuleID]

View file

@ -20,7 +20,7 @@ export let nodeUtil: typeof import('node:util') = proxify(
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -38,7 +38,7 @@ let ClipboardModule: typeof import('@react-native-clipboard/clipboard') =
]),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -29,7 +29,7 @@ export let ReactNavigationNative: typeof import('@react-navigation/native') =
),
),
{
includeUninitialized: true,
uninitialized: true,
},
)
@ -62,7 +62,7 @@ export let ReactNavigationStack: typeof import('@react-navigation/stack') =
),
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -30,7 +30,7 @@ export let FlashList: typeof import('@shopify/flash-list') = proxify(
// [React, RN, JSXRuntime, (FlashListExports), (Reanimated), (RNBottomSheet), ImportTracker, (BottomSheetFlashList)]
),
{
includeUninitialized: true,
uninitialized: true,
},
)

View file

@ -12,6 +12,12 @@ export interface RunFilterOptions {
* @default false
*/
skipDefault?: boolean
/**
* Whether to allow initializing modules to check their exports.
*
* @default false
*/
initialize?: boolean
}
export type RunFilterReturnExportsOptions<
@ -79,9 +85,11 @@ export function runFilter(
const flag = runFilter(filter, id, __r(id), options)
if (!flag) warnDeveloperAboutPartialFilterMatch(id, filter.key)
return flag
} else {
} else if (options?.initialize) {
return runFilter(filter, id, __r(id), options)
}
return FilterResultFlags.Found
}
return

View file

@ -1,7 +1,7 @@
import { getCurrentStack } from '@revenge-mod/utils/errors'
import { proxify } from '@revenge-mod/utils/proxy'
import { cache } from '../caches'
import { _inits, _paths, _uninits } from '../metro/_internal'
import { _inits, _metas, _paths, _uninits } from '../metro/_internal'
import {
getInitializedModuleExports,
isModuleInitialized,
@ -9,80 +9,86 @@ import {
import { exportsFromFilterResultFlag, runFilter } from './_internal'
import type { If } from '@revenge-mod/utils/types'
import type { MaybeDefaultExportMatched, Metro } from '../types'
import type {
RunFilterOptions,
RunFilterReturnExportsOptions,
} from './_internal'
import type { RunFilterReturnExportsOptions } from './_internal'
import type { Filter, FilterResult } from './filters'
export interface BaseLookupModulesOptions<
IncludeUninitialized extends boolean = boolean,
> extends RunFilterOptions {
interface LookupModulesOptionsWithAll<O extends boolean> {
/**
* Whether to include all modules in the lookup, including blacklisted ones.
*
* **This overrides {@link BaseLookupModulesOptions.initialized} and {@link BaseLookupModulesOptions.uninitialized}.**
*/
all: O
}
interface LookupModulesOptionsWithInitializedUninitialized<U extends boolean> {
/**
* Whether to include initialized modules in the lookup.
*
* @default true
*/
includeInitialized?: boolean
initialized?: boolean
/**
* Whether to include uninitialized modules in the lookup.
*
* **This will initialize any modules that match the exportsless filter and may cause unintended side effects.**
* Set {@link BaseLookupModulesOptions.initialize} `true` to initialize uninitialized modules.
*
* @default false
*/
includeUninitialized?: IncludeUninitialized
/**
* If the lookup is cached, whether to initialize the cached modules.
*/
initializeCached?: boolean
uninitialized?: U
}
export type LookupModulesOptions<
ReturnNamespace extends boolean = boolean,
IncludeUninitialized extends boolean = boolean,
> = RunFilterReturnExportsOptions<ReturnNamespace> &
BaseLookupModulesOptions<IncludeUninitialized>
Uninitialized extends boolean = boolean,
All extends boolean = boolean,
Initialize extends boolean = boolean,
> = RunFilterReturnExportsOptions<ReturnNamespace> & {
/**
* Whether to use cached lookup results.
*/
cached?: boolean
/**
* Whether to initialize matching uninitialized modules.
*
* **This will initialize any modules that match the exportsless filter and may cause unintended side effects.**
*/
initialize?: Initialize
} & If<
All,
LookupModulesOptionsWithAll<All> & {
[K in keyof LookupModulesOptionsWithInitializedUninitialized<Uninitialized>]?: never
},
LookupModulesOptionsWithInitializedUninitialized<Uninitialized> & {
[K in keyof LookupModulesOptionsWithAll<All>]?: never
}
>
export type LookupModulesResult<
F extends Filter,
O extends LookupModulesOptions,
> = [
exports: O extends RunFilterReturnExportsOptions<true>
? MaybeDefaultExportMatched<FilterResult<F>>
: FilterResult<F>,
exports: O extends LookupModulesOptions<boolean, boolean, false, true>
? LookupFilterResult<F, O>
: LookupFilterResult<F, O> | undefined,
id: Metro.ModuleID,
]
export type LookupModuleIdsOptions<
IncludeUninitialized extends boolean = boolean,
IncludeAll extends If<IncludeUninitialized, false, boolean> = If<
IncludeUninitialized,
false,
boolean
>,
> = RunFilterOptions &
(
| (BaseLookupModulesOptions<IncludeUninitialized> & {
includeAll?: false
})
| ({
/**
* Whether to include all modules, including ones with bad exports.
* This option overrides `includeInitialized` and `includeUninitialized`.
*
* This will filter modules in order of how they're defined.
*
* @default false
*/
includeAll: IncludeAll
} & BaseLookupModulesOptions<false>)
)
type LookupFilterResult<
F extends Filter,
O extends LookupModulesOptions,
> = O extends RunFilterReturnExportsOptions<true>
? MaybeDefaultExportMatched<FilterResult<F>>
: FilterResult<F>
const NotFoundResult: [] = []
type LookupNotFoundResult = typeof NotFoundResult
/**
* Lookup modules.
*
* You can lookup uninitialized modules by passing `options.includeUninitialized` when filtering via without-exports filters (eg. `byDependencies`).
* You can lookup uninitialized modules by passing `options.uninitialized` when filtering via exportsless filters (eg. `byDependencies`).
* Use the `moduleStateAware` helper to filter dynamically based on whether the module is initialized or not.
*
* @param filter The filter to use.
@ -101,41 +107,50 @@ export function lookupModules<F extends Filter>(
): Generator<LookupModulesResult<F, object>, undefined>
export function lookupModules<
F extends O extends BaseLookupModulesOptions<true>
F extends O extends LookupModulesOptions<boolean, true>
? Filter<any, false>
: Filter,
O extends LookupModulesOptions,
const O extends LookupModulesOptions,
>(filter: F, options: O): Generator<LookupModulesResult<F, O>, undefined>
export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
let notFound = true
let cached: Set<Metro.ModuleID> | undefined
const reg = cache[filter.key]
// Return early if previous lookup was a full lookup and no modules were found
if (reg === null) return
const init = options?.initialize ?? true
const notInit = !init
const noInitCached = !(options?.initializeCached ?? true)
const cached = new Set<Metro.ModuleID>()
if (options?.cached ?? true) {
const reg = cache[filter.key]
// Return early if previous lookup was a full lookup and no modules were found
if (reg === null) return
if (reg)
for (const sId in reg) {
const flag = reg[sId]
const id = Number(sId)
let exports: Metro.ModuleExports | undefined
if (reg) {
cached = new Set()
if (isModuleInitialized(id))
exports = getInitializedModuleExports(id)
else {
if (noInitCached) continue
exports = __r(id)
for (const sId in reg) {
const flag = reg[sId]
const id = Number(sId)
let exports: Metro.ModuleExports | undefined
if (isModuleInitialized(id))
exports = getInitializedModuleExports(id)
else {
if (notInit) continue
exports = __r(id)
}
cached.add(id)
yield [exportsFromFilterResultFlag(flag, exports, options), id]
}
yield [exportsFromFilterResultFlag(flag, exports, options), id]
}
}
if (options?.includeInitialized ?? true)
for (const id of _inits) {
if (cached.has(id)) continue
// Full lookup
if (options?.all) {
for (const id of _metas.keys()) {
if (cached?.has(id)) continue
const exports = getInitializedModuleExports(id)
const flag = runFilter(filter, id, exports, options)
@ -145,28 +160,49 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
}
}
if (options?.includeUninitialized)
for (const id of _uninits) {
if (cached.has(id)) continue
if (notFound) cache[filter.key] = null // Full lookup, and still not found!
}
// Partial lookup
else {
if (options?.initialized ?? true)
for (const id of _inits) {
if (cached?.has(id)) continue
const flag = runFilter(filter, id)
if (flag) {
notFound = false
yield [
exportsFromFilterResultFlag(
flag,
getInitializedModuleExports(id),
options,
),
id,
]
const exports = getInitializedModuleExports(id)
const flag = runFilter(filter, id, exports, options)
if (flag) {
notFound = false
yield [
exportsFromFilterResultFlag(flag, exports, options),
id,
]
}
}
}
if (options?.uninitialized)
for (const id of _uninits) {
if (cached?.has(id)) continue
const flag = runFilter(filter, id, undefined, options)
if (flag) {
notFound = false
yield [
init
? exportsFromFilterResultFlag(
flag,
getInitializedModuleExports(id),
options,
)
: undefined,
id,
]
}
}
}
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__)
if (notFound) warnDeveloperAboutNoFilterMatch(filter)
if (notFound) cache[filter.key] = null // Full lookup, and still not found!
}
/**
@ -185,64 +221,91 @@ export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
*/
export function lookupModule<F extends Filter>(
filter: F,
): LookupModulesResult<F, object> | []
): LookupModulesResult<F, object> | LookupNotFoundResult
export function lookupModule<F extends Filter, O extends LookupModulesOptions>(
filter: F,
options: O,
): LookupModulesResult<F, O> | []
export function lookupModule<
F extends O extends LookupModulesOptions<boolean, true>
? Filter<any, false>
: Filter,
const O extends LookupModulesOptions,
>(filter: F, options: O): LookupModulesResult<F, O> | LookupNotFoundResult
export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
const reg = cache[filter.key]
// Return early if previous lookup was a full lookup and no modules were found
if (reg === null) return []
const init = options?.initialize ?? true
const notInit = !init
const noInitCached = !(options?.initializeCached ?? true)
if (options?.cached ?? true) {
const reg = cache[filter.key]
// Return early if previous lookup was a full lookup and no modules were found
if (reg === null) return NotFoundResult
if (reg)
for (const sId in reg) {
const flag = reg[sId]
const id = Number(sId)
let exports: Metro.ModuleExports
if (reg)
for (const sId in reg) {
const flag = reg[sId]
const id = Number(sId)
let exports: Metro.ModuleExports
if (isModuleInitialized(id))
exports = getInitializedModuleExports(id)
else {
if (noInitCached) continue
exports = __r(id)
if (isModuleInitialized(id))
exports = getInitializedModuleExports(id)
else {
if (notInit) continue
exports = __r(id)
}
return [exportsFromFilterResultFlag(flag, exports, options), id]
}
}
return [exportsFromFilterResultFlag(flag, exports, options), id]
}
if (options?.includeInitialized ?? true)
for (const id of _inits) {
// Full lookup
if (options?.all) {
for (const id of _metas.keys()) {
const exports = getInitializedModuleExports(id)
const flag = runFilter(filter, id, exports, options)
if (flag)
return [exportsFromFilterResultFlag(flag, exports, options), id]
}
if (options?.includeUninitialized)
for (const id of _uninits) {
const flag = runFilter(filter, id)
if (flag)
return [
exportsFromFilterResultFlag(
flag,
getInitializedModuleExports(id),
options,
),
id,
]
}
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__)
warnDeveloperAboutNoFilterMatch(filter)
cache[filter.key] = null // Full lookup, and still not found!
return NotFoundResult
}
// Partial lookup
else {
if (options?.initialized ?? true)
for (const id of _inits) {
const exports = getInitializedModuleExports(id)
const flag = runFilter(filter, id, exports, options)
if (flag)
return [
exportsFromFilterResultFlag(flag, exports, options),
id,
]
}
if (options?.uninitialized)
for (const id of _uninits) {
const flag = runFilter(filter, id, undefined, options)
if (flag)
return [
init
? exportsFromFilterResultFlag(
flag,
getInitializedModuleExports(id),
options,
)
: undefined,
id,
]
}
}
if (__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__)
warnDeveloperAboutNoFilterMatch(filter)
cache[filter.key] = null // Full lookup, and still not found!
return []
return NotFoundResult
}
/**
@ -262,9 +325,8 @@ export function lookupModuleByImportedPath<T = any>(
path: string,
): [exports: T, id: Metro.ModuleID] | [] {
const id = _paths.get(path)
if (id == null) {
return []
}
if (id == null) return NotFoundResult
return [getInitializedModuleExports(id), id]
}

View file

@ -13,11 +13,11 @@ export interface BaseWaitForModulesOptions<
IncludeAll extends boolean = boolean,
> {
/**
* Whether to include all modules, including ones with bad exports.
* Whether to include all modules, including blacklisted ones.
*
* @default false
*/
includeAll?: IncludeAll
all?: IncludeAll
}
export type WaitForModulesUnsubscribeFunction = () => boolean
@ -79,7 +79,7 @@ export function waitForModules(
options?: WaitForModulesOptions,
) {
return onAnyModuleInitialized(
options?.includeAll
options?.all
? (id, exports) => {
const flag = runFilter(filter, id, exports, options)
if (flag)
@ -133,7 +133,7 @@ export function waitForModuleByImportedPath<T = any>(
options?: BaseWaitForModulesOptions,
) {
const unsub = onModuleFinishedImporting(
options?.includeAll
options?.all
? (id, cmpPath) => {
if (path === cmpPath) {
unsub()

View file

@ -5,21 +5,21 @@ import type { Metro } from '../types'
/**
* Returns the dependencies of a module.
* @param id The module ID.
* @throws {TypeError} If the module with the given ID does not exist.
* @returns The dependency map of the module, or `undefined` if the module does not exist.
*/
export function getModuleDependencies(
id: Metro.ModuleID,
): Metro.DependencyMap | undefined {
return _metas.get(id)![0]
return _metas.get(id)?.[0]
}
/**
* Returns whether a module is initialized.
* @param id The module ID.
* @throws {TypeError} If the module with the given ID does not exist.
* @returns `true` if the module is initialized, `false` if it is not initialized, or `undefined` if the module does not exist.
*/
export function isModuleInitialized(id: Metro.ModuleID): boolean | undefined {
return _metas.get(id)![1]
return _metas.get(id)?.[1]
}
/**
@ -28,12 +28,12 @@ export function isModuleInitialized(id: Metro.ModuleID): boolean | undefined {
* @see {@link isModuleInitialized} to check if the module is initialized.
*
* @param id The module ID.
* @throws {TypeError} If the module with the given ID does not exist, or is not initialized.
* @returns The exports of the module, or `undefined` if the module is not initialized or does not exist.
*/
export function getInitializedModuleExports(
id: Metro.ModuleID,
): Metro.ModuleExports | undefined {
return _metas.get(id)![2]!.exports
return _metas.get(id)?.[2]?.exports
}
/**

View file

@ -60,7 +60,7 @@ export function lookupGeneratedIconComponent<N extends string>(
}
return lookupModule(byDependencies<{ [K in N]: FC }>(filter), {
includeInitialized: false,
includeUninitialized: true,
initialized: false,
uninitialized: true,
})[0]?.[compName]
}