From 273294df823f1d73c4101cd15973d3bae1ce5092 Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Tue, 13 May 2025 10:29:25 +0700 Subject: [PATCH] fix(lib/modules/finders)!: remove `find*` apis - `find` APIs are merely a wrapper for `wait` and `lookup` APIs. I think the developer should already know whether the module they're trying to get is initialized already or not by the time their plugin runs. In the `init` phase, most important packages such as React, React Native, React JSX Runtime, Native modules, etc. are already initialized. So, the `lookup` APIs can be used here instead. - While I am not sure what exactly gets initialized after `init` until `start`, you can simply do trial and error to see if the module is initialized or not. If it is not, you can use `wait` API to wait for it to be initialized. - `lookup` can also be used inside `wait` if you know a certain module would be initialized after another module. With all the above reasons, I think the `find*` APIs are redundant and should be removed. --- lib/modules/package.json | 3 - lib/modules/src/finders/find.ts | 300 -------------------------------- lib/plugins/src/apis/modules.ts | 4 +- 3 files changed, 1 insertion(+), 306 deletions(-) delete mode 100644 lib/modules/src/finders/find.ts diff --git a/lib/modules/package.json b/lib/modules/package.json index 8f5047e..7f28340 100644 --- a/lib/modules/package.json +++ b/lib/modules/package.json @@ -6,9 +6,6 @@ "./finders/filters": { "default": "./src/finders/filters.ts" }, - "./finders/find": { - "default": "./src/finders/find.ts" - }, "./finders/lookup": { "default": "./src/finders/lookup.ts" }, diff --git a/lib/modules/src/finders/find.ts b/lib/modules/src/finders/find.ts deleted file mode 100644 index 8a93753..0000000 --- a/lib/modules/src/finders/find.ts +++ /dev/null @@ -1,300 +0,0 @@ -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 { If } from '@revenge-mod/utils/types' -import type { MaybeDefaultExportMatched } from '../types' -import type { Metro } from '../types' -import type { RunFilterReturnExportsOptions } from './_internal' -import type { Filter, FilterResult } from './filters' -import type { LookupModuleIdsOptions } from './lookup' - -export interface BaseFindModuleOptions { - /** - * Abort signal for the find. - */ - abortSignal?: AbortSignal - // TODO(modules/finders/find::options): Add an option to force initialize when module ID is known (find is cached). -} - -export type FindModuleOptions< - ReturnNamespace extends boolean = boolean, - IncludeUninitialized extends boolean = boolean, -> = WaitForModulesOptions & - LookupModulesOptions & - BaseFindModuleOptions - -export type FindModuleIdOptions< - IncludeUninitialized extends boolean = boolean, - IncludeAll extends If = If, -> = LookupModuleIdsOptions & BaseFindModuleOptions - -export type FindModuleResult< - F extends Filter, - O extends FindModuleOptions, -> = O extends RunFilterReturnExportsOptions ? MaybeDefaultExportMatched> : FilterResult - -/** - * Find a module. - * - * @see {@link lookupModule} - * @see {@link waitForModules} - * - * @param filter The filter to use to find the module. - * @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 a module is found. - * - * @example - * ```ts - * // Resolves after 1 event loop, because the module is already initialized - * const React = await findModule(byProps('createElement')) - * - * // Resolves when the module is initialized - * const FlashList = await findModule(byProps('FlashList')) - * ``` - */ -export function findModule(filter: F): Promise> - -export function findModule< - F extends O extends FindModuleOptions ? Filter : Filter, - O extends FindModuleOptions, ->(filter: F, options: O): Promise> - -export function findModule(filter: Filter, options?: FindModuleOptions) { - return new Promise((ok, err) => { - const exports = lookupModule(filter, options!) - if (exports != null) return ok(exports) - - const unsub = waitForModules( - filter, - (_, exports) => { - unsub() - ok(exports) - }, - options!, - ) - - const onAbort = () => { - unsub() - err(new Error(`${findModule.name} aborted before resolved: ${filter.key}`)) - } - - const signal = options?.abortSignal - if (signal) { - if (signal.aborted) { - onAbort() - return - } - - signal.addEventListener('abort', onAbort, { once: true }) - } - }) -} - -/** - * Find a module ID. - * - * @see {@link lookupModuleId} - * @see {@link waitForModules} - * - * @param filter The filter to use to find the module. - * @param options The options to use for the find. - * @returns A promise that resolves to the module ID or rejects if the find is aborted before a module ID is found. - * - * @example - * ```ts - * // Resolves after 1 event loop, because the module is already initialized - * const ReactId = await findModuleId(byProps('createElement')) - * const React = __r(ReactId) - * - * // Resolves when the module is initialized - * const FlashListId = await findModuleId(byProps('FlashList')) - * const FlashListModule = __r(FlashListId) - * ``` - */ -export function findModuleId( - filter: O extends FindModuleIdOptions ? Filter : Filter, - options?: O, -): Promise { - return new Promise((ok, err) => { - const id = lookupModuleId(filter, options) - if (id != null) { - ok(id) - return - } - - const unsub = waitForModules( - filter, - id => { - unsub() - ok(id) - }, - options!, - ) - - const onAbort = () => { - unsub() - err(new Error(`${findModuleId.name} aborted before resolved: ${filter.key}`)) - } - - const signal = options?.abortSignal - if (signal) { - if (signal.aborted) { - onAbort() - return - } - - signal.addEventListener('abort', onAbort, { once: true }) - } - }) -} - -/** - * Find a module by its imported path. - * - * @param path The path to find the module by. - * @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. - * - * @example - * ```ts - * const SettingsConstants = await findModuleByImportedPath('modules/main_tabs_v2/native/settings/SettingsConstants.tsx') - * console.log('Settings page opened') // Logs once the module is initialized - * ``` - */ -export function findModuleByImportedPath(path: string, options?: BaseFindModuleOptions): Promise { - return new Promise((ok, err) => { - const exports = lookupModuleByImportedPath(path) - if (exports != null) { - ok(exports) - return - } - - const unsub = waitForModuleByImportedPath(path, (_, exports) => { - unsub() - ok(exports) - }) - - const onAbort = () => { - unsub() - err(new Error(`${findModuleByImportedPath.name} aborted before resolved: ${path}`)) - } - - const signal = options?.abortSignal - if (signal) { - if (signal.aborted) { - onAbort() - return - } - - signal.addEventListener('abort', onAbort, { once: true }) - } - }) -} - -export interface BaseFindModuleSyncOptions extends BaseFindModuleOptions, ProxifyOptions {} -export type FindModuleSyncOptions< - ReturnNamespace extends boolean = boolean, - IncludeUninitialized extends boolean = boolean, -> = FindModuleOptions & BaseFindModuleSyncOptions - -/** - * Find a module synchronously. - * - * @see {@link lookupModule} - * @see {@link waitForModules} - * - * @see {@link proxify} for how proxification works. - * - * @param filter The filter to use to find the module. - * @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 - * ```ts - * const React = findModuleSync(byProps('createElement'), { hint: 'object' }) - * const App = findModuleSync(byName('App')) - * - * console.log(React) - * // No proxification happens because React is loaded before index module is required - * // Logs: { createElement: ..., ... } - * - * console.log(App) - * // Logs: [Function], but is actually undefined, calling it will throw an error - * console.log(unproxify(App)) - * // Logs: undefined - * - * onAppInitialized(() => { - * console.log(App) - * // When the app is initialized, the App component must also be initialized - * // Logs: [Function], but is actually function App() { ... } - * console.log(unproxify(App)) - * // Logs: function App() { ... } - * }) - * ``` - */ -export function findModuleSync(filter: F): FilterResult | undefined - -export function findModuleSync< - F extends O extends FindModuleSyncOptions ? Filter : Filter, - O extends FindModuleSyncOptions, ->(filter: F, options: O): FindModuleResult | undefined - -export function findModuleSync(filter: Filter, options?: FindModuleSyncOptions) { - const exports = lookupModule(filter, options!) - if (exports != null) return exports - - let value: unknown | undefined - const unsub = waitForModules( - filter, - (_, exports) => { - unsub() - value = exports - }, - options!, - ) - - const signal = options?.abortSignal - if (signal) { - if (signal.aborted) unsub() - else signal.addEventListener('abort', unsub, { once: true }) - } - - return proxify(() => value, options) -} - -/** - * Find a module by its imported path synchronously. - * - * @see {@link findModuleSync} for more details on proxification. - * - * @param path The path to find the module by. - * @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 - * ```ts - * const Logger = findModuleByImportedPathSync('modules/debug/Logger.tsx') - * ``` - */ -export function findModuleByImportedPathSync( - path: string, - options?: BaseFindModuleSyncOptions, -): T | undefined { - const exports = lookupModuleByImportedPath(path) - if (exports != null) return exports - - let value: unknown | undefined - const unsub = waitForModuleByImportedPath(path, (_, exports) => { - unsub() - value = exports - }) - - const signal = options?.abortSignal - if (signal) { - if (signal.aborted) unsub() - else signal.addEventListener('abort', unsub, { once: true }) - } - - return proxify(() => value as T | undefined, options) -} diff --git a/lib/plugins/src/apis/modules.ts b/lib/plugins/src/apis/modules.ts index 90aa15b..df4fa53 100644 --- a/lib/plugins/src/apis/modules.ts +++ b/lib/plugins/src/apis/modules.ts @@ -14,8 +14,7 @@ export type PluginApiModulesMetro = export type PluginApiModulesFinders = // biome-ignore format: Don't typeof import('@revenge-mod/modules/finders/lookup') & - typeof import('@revenge-mod/modules/finders/wait') & - typeof import('@revenge-mod/modules/finders/find') & { + typeof import('@revenge-mod/modules/finders/wait') & { filters: typeof import('@revenge-mod/modules/finders/filters') } @@ -23,7 +22,6 @@ export const modules: PluginApiModules = { finders: { ...require('@revenge-mod/modules/finders/lookup'), ...require('@revenge-mod/modules/finders/wait'), - ...require('@revenge-mod/modules/finders/find'), filters: require('@revenge-mod/modules/finders/filters'), }, metro: {