feat(lib/modules/finders)!: refactor, fix find bugs, add more lookup shorthands, add moduleStateAware filter
This commit is contained in:
parent
e992183879
commit
e5178632ba
5 changed files with 261 additions and 103 deletions
|
|
@ -49,10 +49,23 @@ const FilterResultFlags = {
|
|||
export type FilterResultFlag = (typeof FilterResultFlags)[keyof typeof FilterResultFlags]
|
||||
|
||||
// The reason this returns a flag is because flags are never falsy, while exports may be falsy when using ID-only filters (eg. `byDependencies`).
|
||||
export function runFilter<F extends Filter>(
|
||||
filter: F,
|
||||
|
||||
// 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<any, false>, id: Metro.ModuleID): FilterResultFlag | false
|
||||
|
||||
export function runFilter(
|
||||
filter: Filter,
|
||||
id: Metro.ModuleID,
|
||||
exports: F extends Filter<any, true> ? Metro.ModuleExports : undefined,
|
||||
exports: Metro.ModuleExports,
|
||||
options?: RunFilterOptions,
|
||||
): FilterResultFlag | false
|
||||
|
||||
export function runFilter(
|
||||
filter: Filter,
|
||||
id: Metro.ModuleID,
|
||||
exports?: Metro.ModuleExports,
|
||||
options?: RunFilterOptions,
|
||||
): FilterResultFlag | false {
|
||||
if (exports?.__esModule) {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { _mMetadatas } from '../metro/_internal'
|
||||
import { _mInited, _mMetadatas } from '../metro/_internal'
|
||||
|
||||
import type { If, LogicalOr } from '@revenge-mod/utils/types'
|
||||
import type { Metro } from '../../types/metro'
|
||||
|
||||
export type FilterResult<F> = F extends Filter<infer R, boolean> ? R : never
|
||||
|
||||
export type IsFilterWithExports<F> = F extends Filter<any, infer W> ? W : never
|
||||
export type IsFilterWithExports<F> = F extends Filter<any, infer WE> ? WE : never
|
||||
|
||||
export type IsFilterGeneratorWithExports<F> = F extends (...args: any[]) => infer F ? IsFilterWithExports<F> : never
|
||||
|
||||
export interface Filter<_Inferable = any, WithExports extends boolean = boolean> {
|
||||
(id: Metro.ModuleID, exports: If<WithExports, Metro.ModuleExports, never>): boolean
|
||||
export type Filter<_Inferable = any, WithExports extends boolean = boolean> = If<
|
||||
WithExports,
|
||||
(id: Metro.ModuleID, exports: Metro.ModuleExports) => boolean,
|
||||
(id: Metro.ModuleID, exports?: undefined) => boolean
|
||||
> & {
|
||||
key: string
|
||||
}
|
||||
|
||||
|
|
@ -38,15 +39,24 @@ export interface Filter<_Inferable = any, WithExports extends boolean = boolean>
|
|||
*
|
||||
* @see {@link byProps} for an example on custom-typed filters.
|
||||
*/
|
||||
export function createFilterGenerator<A extends any[], WithExports extends boolean>(
|
||||
filter: (args: A, id: Metro.ModuleID, exps: If<WithExports, Metro.ModuleExports, never>) => boolean,
|
||||
export function createFilterGenerator<A extends any[]>(
|
||||
filter: (args: A, id: Metro.ModuleID, exports: Metro.ModuleExports) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
) {
|
||||
): (...args: A) => Filter<object, true>
|
||||
|
||||
export function createFilterGenerator<A extends any[]>(
|
||||
filter: (args: A, id: Metro.ModuleID) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
): (...args: A) => Filter<object, false>
|
||||
|
||||
export function createFilterGenerator<A extends any[]>(
|
||||
filter: (args: A, id: Metro.ModuleID, exports?: Metro.ModuleExports) => boolean,
|
||||
keyFor: (args: A) => string,
|
||||
): (...args: any[]) => Filter {
|
||||
return (...args: A) => {
|
||||
const f = (id: Metro.ModuleID, exps: If<WithExports, Metro.ModuleExports, never>) => filter(args, id, exps)
|
||||
const f = (id: Metro.ModuleID, exports?: Metro.ModuleExports) => filter(args, id, exports)
|
||||
f.key = keyFor(args)
|
||||
// biome-ignore lint/complexity/noBannedTypes: So it doesn't turn into `any` when you combine it with other typed filters
|
||||
return f as Filter<{}, WithExports>
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +77,7 @@ export type ByProps = <T extends Record<string, any> = Record<string, any>>(
|
|||
* // const React: typeof import('react')
|
||||
* ```
|
||||
*/
|
||||
export const byProps = createFilterGenerator<Parameters<ByProps>, IsFilterGeneratorWithExports<ByProps>>(
|
||||
export const byProps = createFilterGenerator<Parameters<ByProps>>(
|
||||
(props, _, exports) => props.every(prop => exports[prop]),
|
||||
props => `revenge.props(${props.join(',')})`,
|
||||
) as ByProps
|
||||
|
|
@ -106,7 +116,7 @@ export type ByName = <T extends string | object>(
|
|||
* const SomeClass = await findModule(byName<{ new(param: string): SomeClass }>('SomeClass'))
|
||||
* // const SomeClass: { new(): SomeClass, name: 'SomeClass' }
|
||||
*/
|
||||
export const byName = createFilterGenerator<Parameters<ByName>, IsFilterGeneratorWithExports<ByName>>(
|
||||
export const byName = createFilterGenerator<Parameters<ByName>>(
|
||||
([name], _, exports) => exports.name === name,
|
||||
([name]) => `revenge.name(${name})`,
|
||||
) as ByName
|
||||
|
|
@ -123,7 +133,7 @@ export const byName = createFilterGenerator<Parameters<ByName>, IsFilterGenerato
|
|||
* const Logger = await findModule(byDependencies([4, , 2]))
|
||||
* ```
|
||||
*/
|
||||
export const byDependencies = createFilterGenerator<[Array<Metro.ModuleID | undefined>], false>(
|
||||
export const byDependencies = createFilterGenerator<[Array<Metro.ModuleID | undefined>]>(
|
||||
([deps], id) => {
|
||||
const actDeps = _mMetadatas.get(id)![0]
|
||||
if (actDeps.length === deps.length)
|
||||
|
|
@ -167,14 +177,14 @@ export function every<F1 extends Filter, F2 extends Filter, F3 extends Filter>(
|
|||
>
|
||||
|
||||
export function every(...filters: Filter[]): Filter {
|
||||
const cf: Filter = (id, exports) => {
|
||||
const f = (id: Metro.ModuleID, exports?: Metro.ModuleExports) => {
|
||||
for (const filter of filters) if (!filter(id, exports)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
cf.key = `revenge.every(${filters.map(filter => filter.key).join(',')})`
|
||||
f.key = `revenge.every(${filters.map(filter => filter.key).join(',')})`
|
||||
|
||||
return cf
|
||||
return f
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -206,12 +216,32 @@ export function some<F1 extends Filter, F2 extends Filter, F3 extends Filter>(
|
|||
>
|
||||
|
||||
export function some(...filters: Filter[]): Filter {
|
||||
const cf: Filter = (id, exports) => {
|
||||
const f = (id: Metro.ModuleID, exports?: Metro.ModuleExports) => {
|
||||
for (const filter of filters) if (filter(id, exports)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
cf.key = `revenge.some(${filters.map(filter => filter.key).join(',')})`
|
||||
f.key = `revenge.some(${filters.map(filter => filter.key).join(',')})`
|
||||
|
||||
return cf
|
||||
return f
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter modules depending on their initialized state.
|
||||
*
|
||||
* @param initializedFilter The filter to use for initialized modules.
|
||||
* @param uninitializedFilter The filter to use for uninitialized modules.
|
||||
*/
|
||||
export function moduleStateAware<IF extends Filter>(
|
||||
initializedFilter: IF,
|
||||
uninitializedFilter: Filter<any, false>,
|
||||
): Filter<FilterResult<IF>, false> {
|
||||
const f = (id: Metro.ModuleID, exports?: Metro.ModuleExports) => {
|
||||
if (_mInited.has(id)) return initializedFilter(id, exports)
|
||||
return uninitializedFilter(id)
|
||||
}
|
||||
|
||||
f.key = `revenge.moduleStateAware(${initializedFilter.key},${uninitializedFilter.key})`
|
||||
|
||||
return f
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { type LookupModulesOptions, lookupModuleByImportedPath, lookupModuleIds, lookupModules } from './lookup'
|
||||
import { type ProxifyOptions, proxify } from '@revenge-mod/utils/proxy'
|
||||
import { type LookupModulesOptions, lookupModule, lookupModuleByImportedPath, lookupModuleId } from './lookup'
|
||||
import { type WaitForModulesOptions, waitForModuleByImportedPath, waitForModules } from './wait'
|
||||
|
||||
import { type ProxifyOptions, proxify } from '@revenge-mod/utils/proxy'
|
||||
import type { LookupModuleIdsOptions, MaybeDefaultExportMatched } from '.'
|
||||
import type { Metro } from '../../types/metro'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
import type { RunFilterReturnExportsOptions } from './_internal'
|
||||
|
||||
export type BaseFindModuleOptions = {
|
||||
/**
|
||||
|
|
@ -14,7 +15,7 @@ export type BaseFindModuleOptions = {
|
|||
// TODO(modules/finders/find::options): Add an option to force initialize when module ID is known (find is cached).
|
||||
}
|
||||
|
||||
export type FindModuleOptions = WaitForModulesOptions & BaseFindModuleOptions
|
||||
export type FindModuleOptions = WaitForModulesOptions & LookupModulesOptions & BaseFindModuleOptions
|
||||
|
||||
export type FindModuleIdOptions = LookupModuleIdsOptions & FindModuleOptions
|
||||
|
||||
|
|
@ -34,29 +35,45 @@ export type FindModuleIdOptions = LookupModuleIdsOptions & FindModuleOptions
|
|||
* ```
|
||||
*/
|
||||
export function findModule<F extends Filter>(filter: F): Promise<FilterResult<F>>
|
||||
|
||||
export function findModule<F extends Filter, O extends FindModuleOptions>(
|
||||
filter: F,
|
||||
options: O,
|
||||
): Promise<O extends LookupModulesOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>>
|
||||
): Promise<O extends RunFilterReturnExportsOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>>
|
||||
|
||||
export function findModule<F extends Filter, O extends FindModuleOptions>(filter: F, options?: O) {
|
||||
type Result = FilterResult<F>
|
||||
|
||||
return new Promise<O extends LookupModulesOptions<true> ? MaybeDefaultExportMatched<Result> : Result>((ok, err) => {
|
||||
for (const exports of lookupModules(filter, options!)) return ok(exports)
|
||||
return new Promise<O extends RunFilterReturnExportsOptions<true> ? MaybeDefaultExportMatched<Result> : Result>(
|
||||
(ok, err) => {
|
||||
const exports = lookupModule<F, O>(filter, options!)
|
||||
if (exports !== undefined) return ok(exports)
|
||||
|
||||
const unsub = waitForModules(filter, (_, exports) => ok(exports), options)
|
||||
const unsub = waitForModules<F, O>(
|
||||
filter,
|
||||
(_, exports) => {
|
||||
unsub()
|
||||
ok(exports)
|
||||
},
|
||||
options!,
|
||||
)
|
||||
|
||||
const onAbort = () => {
|
||||
unsub()
|
||||
err(new Error(`${findModule.name} aborted before resolved: ${filter.key}`))
|
||||
}
|
||||
const onAbort = () => {
|
||||
unsub()
|
||||
err(new Error(`${findModule.name} aborted before resolved: ${filter.key}`))
|
||||
}
|
||||
|
||||
const signal = options?.abortSignal
|
||||
if (signal) {
|
||||
if (signal.aborted) return onAbort()
|
||||
signal.addEventListener('abort', onAbort, { once: true })
|
||||
}
|
||||
})
|
||||
const signal = options?.abortSignal
|
||||
if (signal) {
|
||||
if (signal.aborted) {
|
||||
onAbort()
|
||||
return
|
||||
}
|
||||
|
||||
signal.addEventListener('abort', onAbort, { once: true })
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,15 +92,28 @@ export function findModule<F extends Filter, O extends FindModuleOptions>(filter
|
|||
* const ReactNative = __r(ReactNativeId)
|
||||
*/
|
||||
export function findModuleId<F extends Filter>(filter: F): Promise<Metro.ModuleID>
|
||||
|
||||
export function findModuleId<F extends Filter, O extends FindModuleIdOptions>(
|
||||
filter: F,
|
||||
options: O,
|
||||
): Promise<O extends LookupModulesOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>>
|
||||
|
||||
export function findModuleId(filter: Filter, options?: FindModuleIdOptions) {
|
||||
return new Promise<Metro.ModuleID>((ok, err) => {
|
||||
for (const id of lookupModuleIds(filter, options)) return ok(id)
|
||||
const id = lookupModuleId(filter, options)
|
||||
if (id !== undefined) {
|
||||
ok(id)
|
||||
return
|
||||
}
|
||||
|
||||
const unsub = waitForModules(filter, (_, id) => ok(id), options)
|
||||
const unsub = waitForModules(
|
||||
filter,
|
||||
id => {
|
||||
unsub()
|
||||
ok(id)
|
||||
},
|
||||
options!,
|
||||
)
|
||||
|
||||
const onAbort = () => {
|
||||
unsub()
|
||||
|
|
@ -92,7 +122,11 @@ export function findModuleId(filter: Filter, options?: FindModuleIdOptions) {
|
|||
|
||||
const signal = options?.abortSignal
|
||||
if (signal) {
|
||||
if (signal.aborted) return onAbort()
|
||||
if (signal.aborted) {
|
||||
onAbort()
|
||||
return
|
||||
}
|
||||
|
||||
signal.addEventListener('abort', onAbort, { once: true })
|
||||
}
|
||||
})
|
||||
|
|
@ -104,12 +138,18 @@ export function findModuleId(filter: Filter, options?: FindModuleIdOptions) {
|
|||
* @param options The options to use for the find.
|
||||
* @returns A promise that resolves to the module's exports or rejects if the find is aborted before the module is found.
|
||||
*/
|
||||
export function findModuleByImportedPath(path: string, options?: BaseFindModuleOptions) {
|
||||
return new Promise<any>((ok, err) => {
|
||||
const m = lookupModuleByImportedPath(path)
|
||||
if (m) return ok(m)
|
||||
export function findModuleByImportedPath<T>(path: string, options?: BaseFindModuleOptions): Promise<T> {
|
||||
return new Promise((ok, err) => {
|
||||
const exports = lookupModuleByImportedPath(path)
|
||||
if (exports !== undefined) {
|
||||
ok(exports)
|
||||
return
|
||||
}
|
||||
|
||||
const unsub = waitForModuleByImportedPath(path, (_, exports) => ok(exports))
|
||||
const unsub = waitForModuleByImportedPath(path, (_, exports) => {
|
||||
unsub()
|
||||
ok(exports)
|
||||
})
|
||||
|
||||
const onAbort = () => {
|
||||
unsub()
|
||||
|
|
@ -118,7 +158,11 @@ export function findModuleByImportedPath(path: string, options?: BaseFindModuleO
|
|||
|
||||
const signal = options?.abortSignal
|
||||
if (signal) {
|
||||
if (signal.aborted) return onAbort()
|
||||
if (signal.aborted) {
|
||||
onAbort()
|
||||
return
|
||||
}
|
||||
|
||||
signal.addEventListener('abort', onAbort, { once: true })
|
||||
}
|
||||
})
|
||||
|
|
@ -134,32 +178,35 @@ export type FindModuleSyncOptions = FindModuleOptions & BaseFindModuleSyncOption
|
|||
* @param options The options to use for the find.
|
||||
* @returns The module exports object if the module is already initialized, or proxified object of the module exports once the module is initialized, or undefined if otherwise.
|
||||
*
|
||||
* @example A little bit after index module is required:
|
||||
* @example
|
||||
* ```ts
|
||||
* const React = findModuleSync(byProps<typeof import('react')>('createElement'), { hint: 'object' })
|
||||
* const App = findModuleSync(byName<React.FC>('App'))
|
||||
*
|
||||
* // React is defined here because React is loaded before index module is required
|
||||
* // No proxification happens because React is loaded before index module is required
|
||||
* console.log(React)
|
||||
* // { createElement: ..., ... }
|
||||
*
|
||||
* console.log(App)
|
||||
* // undefined
|
||||
* // Logs: [Function], but is actually undefined, calling it will throw an error
|
||||
*
|
||||
* onAppInitialized(() => {
|
||||
* // When the app is initialized, the App component must also be initialized
|
||||
* console.log(App)
|
||||
* // function App() { ... }
|
||||
* // Logs: [Function], but is actually function App() { ... }
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function findModuleSync<F extends Filter>(filter: F): FilterResult<F>
|
||||
|
||||
export function findModuleSync<F extends Filter, O extends FindModuleSyncOptions>(
|
||||
filter: F,
|
||||
options: O,
|
||||
): O extends LookupModulesOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>
|
||||
|
||||
export function findModuleSync<F extends Filter, O extends FindModuleSyncOptions>(filter: F, options?: O) {
|
||||
for (const exports of lookupModules(filter, options!)) return exports
|
||||
const exports = lookupModule(filter, options!)
|
||||
if (exports !== undefined) return exports
|
||||
|
||||
let value: unknown | undefined
|
||||
const unsub = waitForModules(
|
||||
|
|
@ -168,7 +215,7 @@ export function findModuleSync<F extends Filter, O extends FindModuleSyncOptions
|
|||
unsub()
|
||||
value = exports
|
||||
},
|
||||
options,
|
||||
options!,
|
||||
)
|
||||
|
||||
const signal = options?.abortSignal
|
||||
|
|
@ -191,8 +238,8 @@ export function findModuleByImportedPathSync<T = any>(
|
|||
path: string,
|
||||
options?: BaseFindModuleSyncOptions,
|
||||
): T | undefined {
|
||||
const m = lookupModuleByImportedPath<T>(path)
|
||||
if (m) return m
|
||||
const exports = lookupModuleByImportedPath<T>(path)
|
||||
if (exports !== undefined) return exports
|
||||
|
||||
let value: unknown | undefined
|
||||
const unsub = waitForModuleByImportedPath(path, (_, exports) => {
|
||||
|
|
@ -206,5 +253,5 @@ export function findModuleByImportedPathSync<T = any>(
|
|||
else signal.addEventListener('abort', unsub, { once: true })
|
||||
}
|
||||
|
||||
return proxify(() => value, options)
|
||||
return proxify(() => value as T | undefined, options)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,7 @@ import type { MaybeDefaultExportMatched } from '.'
|
|||
import type { Metro } from '../../types/metro'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
|
||||
export type BaseLookupModulesOptions = RunFilterOptions
|
||||
|
||||
export type LookupModulesOptions<ReturnNamespace extends boolean = boolean> =
|
||||
RunFilterReturnExportsOptions<ReturnNamespace>
|
||||
|
||||
export type LookupModulesResult<O extends LookupModulesOptions, F extends Filter> = O extends LookupModulesOptions<true>
|
||||
? MaybeDefaultExportMatched<FilterResult<F>>
|
||||
: FilterResult<F>
|
||||
|
||||
export type LookupModuleIdsOptions<IncludeUninitialized extends boolean = boolean> = BaseLookupModulesOptions & {
|
||||
export type BaseLookupModulesOptions<IncludeUninitialized extends boolean = boolean> = RunFilterOptions & {
|
||||
/**
|
||||
* Whether to include initialized modules in the lookup.
|
||||
*
|
||||
|
|
@ -31,13 +22,29 @@ export type LookupModuleIdsOptions<IncludeUninitialized extends boolean = boolea
|
|||
*
|
||||
* Options that require modules to be initialized (eg. `esmReturnNamespace`, `esmSkipDefault`) will be ignored during uninitialized module ID lookup.
|
||||
*
|
||||
* **This will initialize any modules that match the filter and may cause unintended side effects.**
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
includeUninitialized?: IncludeUninitialized
|
||||
}
|
||||
|
||||
export type LookupModulesOptions<
|
||||
ReturnNamespace extends boolean = boolean,
|
||||
IncludeUninitialized extends boolean = boolean,
|
||||
> = RunFilterReturnExportsOptions<ReturnNamespace> & BaseLookupModulesOptions<IncludeUninitialized>
|
||||
|
||||
export type LookupModulesResult<
|
||||
O extends LookupModulesOptions,
|
||||
F extends Filter,
|
||||
> = O extends RunFilterReturnExportsOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>
|
||||
|
||||
export type LookupModuleIdsOptions<IncludeUninitialized extends boolean = boolean> =
|
||||
BaseLookupModulesOptions<IncludeUninitialized>
|
||||
|
||||
/**
|
||||
* Lookup module IDs by its exports. You may lookup both uninitialized module IDs when filtering via ID-only filters (eg. `byDependencies`).
|
||||
* Lookup module IDs by their exports.
|
||||
* You may lookup both uninitialized module IDs by passing `options.includeUninitialized` when filtering via ID-only filters (eg. `byDependencies`).
|
||||
*
|
||||
* @param filter The filter to use.
|
||||
* @param options The options to use for the lookup.
|
||||
|
|
@ -67,38 +74,99 @@ export function* lookupModuleIds<O extends LookupModuleIdsOptions>(
|
|||
if (runFilter(filter, id, exports, options)) yield id
|
||||
}
|
||||
|
||||
if (options?.includeUninitialized)
|
||||
for (const id of _mUninited) if (runFilter<Filter<any, false>>(filter, id, undefined, options)) yield id
|
||||
if (options?.includeUninitialized) for (const id of _mUninited) if (runFilter(filter, id)) yield id
|
||||
}
|
||||
|
||||
export function lookupModules<F extends Filter>(filter: F): Generator<FilterResult<F>, undefined>
|
||||
|
||||
export function lookupModules<F extends Filter, O extends LookupModulesOptions>(
|
||||
filter: F,
|
||||
options: O,
|
||||
): Generator<LookupModulesResult<O, F>, undefined>
|
||||
|
||||
/**
|
||||
* Lookup modules by its exports.
|
||||
*
|
||||
* `lookupModules` only filters initialized modules. You will need to use `waitForModules` to filter initializing modules.
|
||||
* Lookup initialized modules by their exports.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
export function* lookupModules<F extends Filter, O extends LookupModulesOptions>(filter: F, options?: O) {
|
||||
for (const id of _mInited) {
|
||||
const { exports } = _mMetadatas.get(id)![1]!
|
||||
const result = runFilterReturnExports(filter, id, exports, options)
|
||||
if (result) yield result
|
||||
}
|
||||
export function lookupModules<F extends Filter>(filter: F): Generator<FilterResult<F>, undefined>
|
||||
|
||||
export function lookupModules<
|
||||
F extends O extends BaseLookupModulesOptions<true> ? Filter<any, false> : Filter,
|
||||
O extends LookupModulesOptions,
|
||||
>(filter: F, options: O): Generator<LookupModulesResult<O, F>, undefined>
|
||||
|
||||
export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
|
||||
if (options?.includeInitialized ?? true)
|
||||
for (const id of _mInited) {
|
||||
const { exports } = _mMetadatas.get(id)![1]!
|
||||
const result = runFilterReturnExports(filter, id, exports, options)
|
||||
if (result) yield result
|
||||
}
|
||||
|
||||
if (options?.includeUninitialized) for (const id of _mUninited) if (runFilter(filter, id)) yield __r(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup module exports by its imported path.
|
||||
* Lookup a module ID by its exports. Skipping creating a `Generator`.
|
||||
*
|
||||
* `lookupModuleByImportedPath` only finds initialized modules. You will need to use `waitForModuleByImportedPath` to filter initializing modules.
|
||||
* @see {@link lookupModuleIds} for more documentation.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const id = lookupModuleIds(byProps('createElement'))
|
||||
* // Log the first module ID that exports React
|
||||
* console.log(id)
|
||||
* ```
|
||||
*
|
||||
* @example Lookup uninitialized modules
|
||||
* ```ts
|
||||
* const id = lookupModuleIds(byDependencies([...]), { includeUninitialized: true })
|
||||
* // Log the first module ID that has those dependencies
|
||||
* console.log(id)
|
||||
* ```
|
||||
*/
|
||||
export function lookupModuleId<O extends LookupModuleIdsOptions>(
|
||||
filter: O extends BaseLookupModulesOptions<true> ? Filter<any, false> : Filter,
|
||||
options?: O,
|
||||
): Metro.ModuleID | undefined {
|
||||
if (options?.includeInitialized ?? true)
|
||||
for (const id of _mInited) {
|
||||
const { exports } = _mMetadatas.get(id)![1]!
|
||||
if (runFilter(filter, id, exports, options)) return id
|
||||
}
|
||||
|
||||
if (options?.includeUninitialized) for (const id of _mUninited) if (runFilter(filter, id)) return id
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup an initialized module by its exports. Skipping creating a `Generator`.
|
||||
*
|
||||
* @see {@link lookupModules} for more documentation.
|
||||
*
|
||||
* @param filter The filter to use.
|
||||
* @param options The options to use for the lookup.
|
||||
* @returns The first module exports that match the filter.
|
||||
*/
|
||||
export function lookupModule<F extends Filter>(filter: F): FilterResult<F> | undefined
|
||||
|
||||
export function lookupModule<F extends Filter, O extends LookupModulesOptions>(
|
||||
filter: F,
|
||||
options: O,
|
||||
): LookupModulesResult<O, F> | undefined
|
||||
|
||||
export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
|
||||
if (options?.includeInitialized ?? true)
|
||||
for (const id of _mInited) {
|
||||
const { exports } = _mMetadatas.get(id)![1]!
|
||||
const result = runFilterReturnExports(filter, id, exports, options)
|
||||
if (result) return result
|
||||
}
|
||||
|
||||
if (options?.includeUninitialized) for (const id of _mUninited) if (runFilter(filter, id)) return __r(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup an initialized module by its imported path.
|
||||
*
|
||||
* @param path The path to lookup the module by.
|
||||
* @returns The module exports if the module is initialized, or undefined if the module is not found or not initialized.
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
import { onAnyModuleInitialized, onModuleFinishedImporting } from '../metro'
|
||||
import { _bl, _mMetadatas } from '../metro/_internal'
|
||||
import { runFilterReturnExports } from './_internal'
|
||||
import { runFilterReturnExports, type RunFilterReturnExportsOptions } from './_internal'
|
||||
|
||||
import type { MaybeDefaultExportMatched } from '.'
|
||||
import type { Metro } from '../../types/metro'
|
||||
import type { Filter, FilterResult } from './filters'
|
||||
import type { LookupModulesOptions } from './lookup'
|
||||
|
||||
export type WaitForModulesOptions = LookupModulesOptions
|
||||
export type WaitForModulesOptions<ReturnNamespace extends boolean = boolean> =
|
||||
RunFilterReturnExportsOptions<ReturnNamespace>
|
||||
|
||||
export type WaitForModulesResult<
|
||||
F extends Filter,
|
||||
O extends WaitForModulesOptions,
|
||||
> = O extends RunFilterReturnExportsOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>
|
||||
|
||||
/**
|
||||
* Wait for modules to initialize by its exports.
|
||||
* This is the way to find **uninitialized** module exports that match a filter.
|
||||
*
|
||||
* **Callback won't be called if the module is already initialized! Use `lookupModule` for already initialized modules.**
|
||||
* Wait for modules to initialize by their exports. **Callback won't be called if the module is already initialized!**
|
||||
*
|
||||
* @param filter The filter to use.
|
||||
* @param callback The callback to call when matching modules are initialized.
|
||||
|
|
@ -22,7 +24,7 @@ export type WaitForModulesOptions = LookupModulesOptions
|
|||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* waitForModule(
|
||||
* waitForModules(
|
||||
* byName<typeof import('@shopify/flash-list')>('FlashList'),
|
||||
* // (exports: typeof import('@shopify/flash-list'), id: Metro.ModuleID) => any
|
||||
* (id, exports) => {...}
|
||||
|
|
@ -34,12 +36,9 @@ export function waitForModules<F extends Filter>(
|
|||
callback: (id: Metro.ModuleID, exports: FilterResult<F>) => any,
|
||||
): () => void
|
||||
|
||||
export function waitForModules<F extends Filter, O extends WaitForModulesOptions | undefined>(
|
||||
export function waitForModules<F extends Filter, O extends WaitForModulesOptions>(
|
||||
filter: F,
|
||||
callback: (
|
||||
id: Metro.ModuleID,
|
||||
exports: O extends LookupModulesOptions<true> ? MaybeDefaultExportMatched<FilterResult<F>> : FilterResult<F>,
|
||||
) => any,
|
||||
callback: (id: Metro.ModuleID, exports: WaitForModulesResult<F, O>) => any,
|
||||
options: O,
|
||||
): () => void
|
||||
|
||||
|
|
@ -59,6 +58,7 @@ export function waitForModules(
|
|||
|
||||
/**
|
||||
* Wait for a module to initialize by its imported path.
|
||||
*
|
||||
* @param path The path to wait for.
|
||||
* @param callback The callback to call once the module is initialized.
|
||||
* @returns A function to unsubscribe.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue