fix(lib): refactor modules, react, utils

This commit is contained in:
PalmDevs 2025-05-09 19:05:31 +07:00
parent c11a74bc35
commit bea3439c5e
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
25 changed files with 206 additions and 205 deletions

View file

@ -5,7 +5,7 @@ import { AssetRegistry } from './preinit'
import type { Asset, AssetId, RegisterableAsset } from './types'
export * from './preinit'
export { AssetRegistry, AssetRegistryModuleId } from './preinit'
// iOS cannot display SVGs
let _preferredType: Asset['type'] = ReactNative.Platform.OS === 'ios' ? 'png' : 'svg'

View file

@ -1,7 +1,7 @@
import { _mInitingId, _mUninited } from '@revenge-mod/modules/_/metro'
import { waitForModules } from '@revenge-mod/modules/finders'
import { _initing, _uninits } from '@revenge-mod/modules/_/metro'
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
import { getModuleDependencies } from '@revenge-mod/modules/metro'
import { waitForModules } from '@revenge-mod/modules/finders/wait'
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
import { _assets, _metas, _overrides } from './_internal'
@ -28,7 +28,7 @@ const unsubAR = waitForModules(byProps<ReactNative.AssetsRegistry>('registerAsse
// More fragile way, but also more performant:
// There is exactly one asset before the reexported asset registry :/
const firstAssetModuleId = id - 1
for (const mId of _mUninited) {
for (const mId of _uninits) {
if (mId < firstAssetModuleId) continue
const deps = getModuleDependencies(mId)!
@ -53,7 +53,7 @@ const unsubAR = waitForModules(byProps<ReactNative.AssetsRegistry>('registerAsse
// In-memory cache
reg[1][type] = asset
_metas.set(asset, [result, _mInitingId])
_metas.set(asset, [result, _initing])
return result
}

View file

@ -1,11 +1,11 @@
import { lookupModule } from '@revenge-mod/modules/finders'
import { byDependencies, byProps, moduleStateAware, relativeDep } from '@revenge-mod/modules/finders/filters'
import { byDependencies, byProps, preferExports, relativeDep } from '@revenge-mod/modules/finders/filters'
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
import { proxify } from '@revenge-mod/utils/proxy'
export let nodeUtil: typeof import('node:util') = proxify(
() => {
const module = lookupModule(
moduleStateAware(byProps<typeof nodeUtil>('inspect'), byDependencies([relativeDep(1), [], []])),
preferExports(byProps<typeof nodeUtil>('inspect'), byDependencies([relativeDep(1), [], []])),
{
includeUninitialized: true,
},

View file

@ -1,6 +1,6 @@
import { byDependencies, byName, byProps, looseDeps, preferExports } from '@revenge-mod/modules/finders/filters'
import { lookupModule, lookupModuleId } from '@revenge-mod/modules/finders/lookup'
import { proxify } from '@revenge-mod/utils/proxy'
import { lookupModule, lookupModuleId } from '@revenge-mod/modules/finders'
import { byDependencies, byName, byProps, looseDeps, moduleStateAware } from '@revenge-mod/modules/finders/filters'
const _createClassModuleId = lookupModuleId(byName('_createClass'))
const _classCallCheckModuleId = lookupModuleId(byName('_classCallCheck'))
@ -8,7 +8,7 @@ const _classCallCheckModuleId = lookupModuleId(byName('_classCallCheck'))
export let ReactNavigationNative: typeof import('@react-navigation/native') = proxify(
() => {
const module = lookupModule(
moduleStateAware(
preferExports(
byProps<typeof ReactNavigationNative>('useLinkTo'),
byDependencies(looseDeps([[], looseDeps([[_createClassModuleId, _classCallCheckModuleId]])])),
),
@ -27,7 +27,7 @@ export let ReactNavigationNative: typeof import('@react-navigation/native') = pr
export let ReactNavigationStack: typeof import('@react-navigation/stack') = proxify(
() => {
const module = lookupModule(
moduleStateAware(
preferExports(
byProps<typeof ReactNavigationStack>('createStackNavigator'),
byDependencies(looseDeps([[], looseDeps([[_createClassModuleId, _classCallCheckModuleId]])])),
),

View file

@ -1,5 +1,5 @@
import { byDependencies, byProps, moduleStateAware } from '@revenge-mod/modules/finders/filters'
import { lookupModule } from '@revenge-mod/modules/finders'
import { byDependencies, byProps, preferExports } from '@revenge-mod/modules/finders/filters'
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
import { proxify } from '@revenge-mod/utils/proxy'
import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react'
@ -7,7 +7,7 @@ import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@re
export let FlashList: typeof import('@shopify/flash-list') = proxify(
() => {
const module = lookupModule(
moduleStateAware(
preferExports(
byProps<typeof FlashList>('FlashList'),
// biome-ignore lint/suspicious/noSparseArray: don't care
byDependencies([ReactModuleId, ReactNativeModuleId, ReactJsxRuntimeModuleId, , , , 2, ,]),

View file

@ -3,21 +3,24 @@
"private": true,
"type": "module",
"exports": {
".": {
"default": "./src/index.ts"
},
"./finders": {
"default": "./src/finders/index.ts"
},
"./finders/filters": {
"default": "./src/finders/filters.ts"
},
"./metro": {
"default": "./src/metro/index.ts"
"./finders/find": {
"default": "./src/finders/find.ts"
},
"./finders/lookup": {
"default": "./src/finders/lookup.ts"
},
"./finders/wait": {
"default": "./src/finders/wait.ts"
},
"./metro/subscriptions": {
"default": "./src/metro/subscriptions/index.ts"
},
"./metro/utils": {
"default": "./src/metro/utils.ts"
},
"./native": {
"default": "./src/native.ts"
},
@ -25,7 +28,7 @@
"default": "./src/preinit.ts"
},
"./types": {
"default": "./types.ts"
"default": "./src/types.ts"
},
"./_/metro": {
"default": "./src/metro/_internal.ts"

View file

@ -1,7 +1,7 @@
import { isModuleExportBad } from '../metro'
import { isModuleExportBad } from '../metro/utils'
import type { If } from '@revenge-mod/utils/types'
import type { Metro } from '../../types'
import type { Metro } from '../types'
import type { Filter } from './filters'
export interface RunFilterOptions {

View file

@ -1,9 +1,9 @@
import { _mInited } from '../metro/_internal'
import { _inits } from '../metro/_internal'
import { getModuleDependencies, initializedModuleHasBadExports, isModuleInitialized } from '../metro'
import { getModuleDependencies, initializedModuleHasBadExports, isModuleInitialized } from '../metro/utils'
import type { If, LogicalOr } from '@revenge-mod/utils/types'
import type { Metro } from '../../types'
import type { Metro } from '../types'
export type FilterResult<F> = F extends Filter<infer R, boolean> ? R : never
@ -79,8 +79,18 @@ export type ByProps = <T extends Record<string, any> = Record<string, any>>(
* ```
*/
export const byProps = createFilterGenerator<Parameters<ByProps>>(
(props, _, exports) =>
(typeof exports === 'object' || typeof exports === 'function') && props.every(prop => prop in exports),
(props, _, exports) => {
if (typeof exports === 'object' || typeof exports === 'function') {
for (const prop of props) {
if (prop in exports) continue
return false
}
return true
}
return false
},
props => `revenge.props(${props.join(',')})`,
) as ByProps
@ -93,8 +103,12 @@ export type WithoutProps = <T extends Record<string, any>>(prop: string, ...prop
* @param props More properties to check for (optional).
*/
export const withoutProps = createFilterGenerator<Parameters<WithoutProps>>(
(props, _, exports) =>
(typeof exports === 'object' || typeof exports === 'function') && !props.some(prop => prop in exports),
(props, _, exports) => {
if (typeof exports === 'object' || typeof exports === 'function')
for (const prop of props) if (prop in exports) return false
return true
},
props => `revenge.withoutProps(${props.join(',')})`,
) as WithoutProps
@ -173,7 +187,7 @@ type ByDependencies = <T>(deps: ComparableDependencyMap) => Filter<T, false>
*/
export const byDependencies = createFilterGenerator<Parameters<ByDependencies>>(
([deps], id) => compareDeps(id, deps, deps.l),
deps => `revenge.deps(${depsToKey(deps)})`,
deps => `revenge.deps(${genDepsKey(deps)})`,
) as ByDependencies
/**
@ -194,21 +208,11 @@ export function looseDeps(deps: ComparableDependencyMap) {
* Marks this dependency to compare relatively to the module ID being compared.
*
* @param id The dependency ID to mark as relative.
* @param root Whether this dependency should be relatively to the root module ID. Useless for single-depth comparisons.
*/
export function relativeDep(id: Metro.ModuleID): RelativeDependency {
export function relativeDep(id: Metro.ModuleID, root?: boolean): RelativeDependency {
const o = Object(id) as RelativeDependency
return o
}
/**
* Marks this dependency to compare relatively to the module ID that the filter is given.
* Useless for single-depth comparisons, but useful for nested comparisons.
*
* @param id The dependency ID to mark as root relative.
*/
export function rootRelativeDep(id: Metro.ModuleID): RelativeDependency {
const o = Object(id) as RelativeDependency
o.r = true
if (root) o.r = true
return o
}
@ -249,14 +253,14 @@ function compareDeps(rootOf: Metro.ModuleID, compare: ComparableDependencyMap, l
return true
}
function depsToKey(deps: ComparableDependencyMap): string {
function genDepsKey(deps: ComparableDependencyMap): string {
let key = ''
for (const dep of deps)
if (dep === undefined) key += ','
else if (Array.isArray(dep)) {
if (dep.l) key += '#'
key += `[${depsToKey(dep)}],`
key += `[${genDepsKey(dep)}],`
} else key += `${(dep as RelativeDependency).r ? `r${dep.valueOf()}` : dep.valueOf()},`
return key.substring(0, key.length - 1)
@ -408,7 +412,7 @@ export type PreferExports = <WEF extends Filter>(
*/
export const preferExports = createFilterGenerator<Parameters<PreferExports>>(
([withExportsFilter, exportslessFilter], id, exports) => {
if (_mInited.has(id)) return withExportsFilter(id, exports)
if (_inits.has(id)) return withExportsFilter(id, exports)
return exportslessFilter(id)
},
([f1, f2]) => `revenge.preferExports(${f1.key},${f2.key})`,

View file

@ -3,10 +3,11 @@ import { type LookupModulesOptions, lookupModule, lookupModuleByImportedPath, lo
import { type WaitForModulesOptions, waitForModuleByImportedPath, waitForModules } from './wait'
import type { If } from '@revenge-mod/utils/types'
import type { LookupModuleIdsOptions, MaybeDefaultExportMatched } from '.'
import type { Metro } from '../../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 {
/**

View file

@ -1,8 +0,0 @@
export * from './lookup'
export * from './wait'
export * from './find'
/**
* Maybe the default export matched instead of the namespace, because you're using `options.returnNamespace`.
*/
export type MaybeDefaultExportMatched<T> = T | { default: T }

View file

@ -1,4 +1,4 @@
import { _mInited, _mMd, _mPaths, _mUninited } from '../metro/_internal'
import { _inits, _metas, _paths, _uninits } from '../metro/_internal'
import {
type RunFilterOptions,
type RunFilterReturnExportsOptions,
@ -6,11 +6,11 @@ import {
runFilter,
} from './_internal'
import { getInitializedModuleExports } from '../metro'
import { getInitializedModuleExports } from '../metro/utils'
import type { If } from '@revenge-mod/utils/types'
import type { MaybeDefaultExportMatched } from '.'
import type { Metro } from '../../types'
import type { MaybeDefaultExportMatched } from '../types'
import type { Metro } from '../types'
import type { Filter, FilterResult } from './filters'
export interface BaseLookupModulesOptions<IncludeUninitialized extends boolean = boolean> extends RunFilterOptions {
@ -53,6 +53,9 @@ export type LookupModuleIdsOptions<
| ({
/**
* 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
*/
@ -92,12 +95,12 @@ export function* lookupModuleIds<O extends LookupModuleIdsOptions>(
options?: O,
): Generator<Metro.ModuleID, undefined> {
if (options?.includeAll) {
for (const id of _mMd.keys()) if (runFilter(filter, id, getInitializedModuleExports(id), options)) yield id
for (const id of _metas.keys()) if (runFilter(filter, id, getInitializedModuleExports(id), options)) yield id
} else {
if (options?.includeInitialized ?? true)
for (const id of _mInited) if (runFilter(filter, id, getInitializedModuleExports(id), options)) yield id
for (const id of _inits) if (runFilter(filter, id, getInitializedModuleExports(id), options)) yield id
if (options?.includeUninitialized) for (const id of _mUninited) if (runFilter(filter, id)) yield id
if (options?.includeUninitialized) for (const id of _uninits) if (runFilter(filter, id)) yield id
}
}
@ -127,14 +130,14 @@ export function lookupModules<
export function* lookupModules(filter: Filter, options?: LookupModulesOptions) {
if (options?.includeInitialized ?? true)
for (const id of _mInited) {
for (const id of _inits) {
const exports = getInitializedModuleExports(id)
const flag = runFilter(filter, id, exports, options)
if (flag) yield exportsFromFilterResultFlag(flag, exports, options)
}
if (options?.includeUninitialized)
for (const id of _mUninited)
for (const id of _uninits)
if (runFilter(filter, id)) {
// Run the filter again to ensure we have the correct exports
const exports = __r(id)
@ -171,12 +174,12 @@ export function lookupModuleId<O extends LookupModuleIdsOptions>(
options?: O,
): Metro.ModuleID | undefined {
if (options?.includeAll) {
for (const id of _mMd.keys()) if (runFilter(filter, id, getInitializedModuleExports(id), options)) return id
for (const id of _metas.keys()) if (runFilter(filter, id, getInitializedModuleExports(id), options)) return id
} else {
if (options?.includeInitialized ?? true)
for (const id of _mInited) if (runFilter(filter, id, getInitializedModuleExports(id), options)) return id
for (const id of _inits) if (runFilter(filter, id, getInitializedModuleExports(id), options)) return id
if (options?.includeUninitialized) for (const id of _mUninited) if (runFilter(filter, id)) return id
if (options?.includeUninitialized) for (const id of _uninits) if (runFilter(filter, id)) return id
}
}
@ -203,20 +206,20 @@ export function lookupModule<F extends Filter, O extends LookupModulesOptions>(
export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
if (options?.includeInitialized ?? true)
for (const id of _mInited) {
for (const id of _inits) {
const exports = getInitializedModuleExports(id)
const flag = runFilter(filter, id, exports, options)
if (flag) return exportsFromFilterResultFlag(flag, exports, options)
}
if (options?.includeUninitialized)
for (const id of _mUninited) {
for (const id of _uninits)
if (runFilter(filter, id)) {
const exports = __r(id)
// Run the filter again to ensure we have the correct exports
const flag = runFilter(filter, id, exports, options)
if (flag) return exportsFromFilterResultFlag(flag, exports, options)
}
}
}
/**
@ -231,7 +234,7 @@ export function lookupModule(filter: Filter, options?: LookupModulesOptions) {
* ```
*/
export function lookupModuleIdByImportedPath(path: string): Metro.ModuleID | undefined {
return _mPaths.get(path)
return _paths.get(path)
}
/**
@ -248,6 +251,6 @@ export function lookupModuleIdByImportedPath(path: string): Metro.ModuleID | und
* ```
*/
export function lookupModuleByImportedPath<T = any>(path: string): T | undefined {
const id = lookupModuleIdByImportedPath(path)
return getInitializedModuleExports(id!)
// undefined will also return undefined either way, so we can just non-nullish assert it
return getInitializedModuleExports(lookupModuleIdByImportedPath(path)!)
}

View file

@ -1,13 +1,9 @@
import {
getInitializedModuleExports,
initializedModuleHasBadExports,
onAnyModuleInitialized,
onModuleFinishedImporting,
} from '../metro'
import { onAnyModuleInitialized, onModuleFinishedImporting } from '../metro/subscriptions'
import { getInitializedModuleExports, initializedModuleHasBadExports } from '../metro/utils'
import { type RunFilterReturnExportsOptions, exportsFromFilterResultFlag, runFilter } from './_internal'
import type { MaybeDefaultExportMatched } from '.'
import type { Metro } from '../../types'
import type { MaybeDefaultExportMatched } from '../types'
import type { Metro } from '../types'
import type { Filter, FilterResult } from './filters'
export interface BaseWaitForModulesOptions<IncludeAll extends boolean = boolean> {
@ -64,16 +60,18 @@ export function waitForModules(
callback: (id: Metro.ModuleID, exports: Metro.ModuleExports) => any,
options?: WaitForModulesOptions,
) {
return options?.includeAll
? onAnyModuleInitialized((id, exports) => {
const flag = runFilter(filter, id, exports, options)
if (flag) callback(id, exportsFromFilterResultFlag(flag, exports, options))
})
: onAnyModuleInitialized((id, exports) => {
if (initializedModuleHasBadExports(id)) return
const flag = runFilter(filter, id, exports, options)
if (flag) callback(id, exportsFromFilterResultFlag(flag, exports, options))
})
return onAnyModuleInitialized(
options?.includeAll
? (id, exports) => {
const flag = runFilter(filter, id, exports, options)
if (flag) callback(id, exportsFromFilterResultFlag(flag, exports, options))
}
: (id, exports) => {
if (initializedModuleHasBadExports(id)) return
const flag = runFilter(filter, id, exports, options)
if (flag) callback(id, exportsFromFilterResultFlag(flag, exports, options))
},
)
}
/**
@ -103,13 +101,22 @@ export function waitForModuleByImportedPath<T = any>(
callback: (id: Metro.ModuleID, exports: T) => any,
options?: BaseWaitForModulesOptions,
) {
const unsub = onModuleFinishedImporting((id, cmpPath) => {
if (path === cmpPath) {
unsub()
if (!options?.includeAll && initializedModuleHasBadExports(id)) return
callback(id, getInitializedModuleExports(id))
}
})
const unsub = onModuleFinishedImporting(
options?.includeAll
? (id, cmpPath) => {
if (path === cmpPath) {
unsub()
callback(id, getInitializedModuleExports(id))
}
}
: (id, cmpPath) => {
if (path === cmpPath) {
unsub()
if (initializedModuleHasBadExports(id)) return
callback(id, getInitializedModuleExports(id))
}
},
)
return unsub
}

View file

@ -1,4 +0,0 @@
export * as finders from './finders'
export * as filters from './finders/filters'
export * as metro from './metro'
export * as native from './native'

View file

@ -1,49 +1,49 @@
import { _executeInitializedSubscription, _executeRequiredSubscription } from './subscriptions/_internal'
import { _execInitSubs, _execReqSubs } from './subscriptions/_internal'
import { isModuleExportsBad } from './utils'
import type { Metro } from '../../types'
import type { Metro } from '../types'
/** Initializing ID */
export let _mInitingId = -1
/** Known uninitialized IDs (not blacklisted) */
export const _mUninited = new Set<Metro.ModuleID>()
export let _initing = -1
/** Known uninitialized IDs */
export const _uninits = new Set<Metro.ModuleID>()
/** Known initialized IDs (not blacklisted) */
export const _mInited = new Set<Metro.ModuleID>()
export const _inits = new Set<Metro.ModuleID>()
export const _mPaths = new Map<string, Metro.ModuleID>()
export const _mMd = new Map<Metro.ModuleID, [deps: Metro.DependencyMap, initialized: boolean, module?: Metro.Module]>()
export const _paths = new Map<string, Metro.ModuleID>()
export const _metas = new Map<
Metro.ModuleID,
[deps: Metro.DependencyMap, initialized: boolean, module?: Metro.Module]
>()
export function patchMetroDefine(metroDefine: Metro.DefineFn) {
return ((origFactory, id, deps) => {
// deps won't be undefined last time I checked
const metadata = [deps!, false] as typeof _mMd extends Map<any, infer V> ? V : never
_mMd.set(id, metadata)
_mUninited.add(id)
const metadata = [deps!, false] as typeof _metas extends Map<any, infer V> ? V : never
_metas.set(id, metadata)
_uninits.add(id)
metroDefine(
((global, req, importDefault, importAll, module, exports, deps) => {
metadata[1] = true
metadata[2] = module
const prevIId = _mInitingId
_mInitingId = id
const prevIId = _initing
_initing = id
_executeRequiredSubscription(id)
_execReqSubs(id)
try {
origFactory(global, req, importDefault, importAll, module, exports, deps)
// Don't use exports here, as modules can set module.exports to a different object
if (isModuleExportsBad(module.exports)) throw undefined
metadata[1] = true
// Add the module to the initialized set only if the factory doesn't error or the exports aren't bad
_mInited.add(id)
} catch {
} finally {
_mInitingId = prevIId
_mUninited.delete(id)
// Don't use exports here, as modules can set module.exports to a different object
_executeInitializedSubscription(id, module.exports)
}
if (!isModuleExportsBad(module.exports)) _inits.add(id)
_execInitSubs(id, module.exports)
} catch {}
_initing = prevIId
_uninits.delete(id)
}) satisfies Metro.FactoryFn,
id,
deps,

View file

@ -1,4 +0,0 @@
export * from './subscriptions'
export * from './utils'
// TODO(modules): caching

View file

@ -1,19 +1,18 @@
import { _mInitingId, _mPaths } from './_internal'
import { _initing, _paths } from './_internal'
import { _executeImportedPathSubscription } from './subscriptions/_internal'
import { _execPathSubs } from './subscriptions/_internal'
import { waitForModules } from '../finders'
import { byProps } from '../finders/filters'
import { waitForModules } from '../finders/wait'
const unsubFFI = waitForModules(byProps('fileFinishedImporting'), (_, exports) => {
unsubFFI()
const unsubForImportTracker = waitForModules(byProps('fileFinishedImporting'), (_, exports) => {
const orig = exports.fileFinishedImporting
if (orig) {
unsubForImportTracker()
exports.fileFinishedImporting = (path: string) => {
orig(path)
const id = _mInitingId
_mPaths.set(path, id)
_executeImportedPathSubscription(id, path)
}
exports.fileFinishedImporting = (path: string) => {
orig(path)
const id = _initing
_paths.set(path, id)
_execPathSubs(id, path)
}
})

View file

@ -1,30 +1,32 @@
import { _mInitingId } from '../_internal'
import type {
ModuleFirstRequiredCallback,
ModuleInitializedCallback,
ModuleWithImportedPathInitializedCallback,
} from '.'
import type { Metro } from '../../types'
import type { ModuleInitializedCallback, ModuleRequiredCallback, ModuleWithImportedPathInitializedCallback } from '.'
import type { Metro } from '../../../types'
export const _reqAll = new Set<ModuleRequiredCallback>()
export const _reqSpecific = new Map<Metro.ModuleID, Set<ModuleRequiredCallback>>()
export const _reqAll = new Set<ModuleFirstRequiredCallback>()
export const _req = new Map<Metro.ModuleID, Set<ModuleFirstRequiredCallback>>()
export const _initAll = new Set<ModuleInitializedCallback>()
export const _initSpecific = new Map<Metro.ModuleID, Set<ModuleInitializedCallback>>()
export const _importedPath = new Set<ModuleWithImportedPathInitializedCallback>()
export const _init = new Map<Metro.ModuleID, Set<ModuleInitializedCallback>>()
export const _path = new Set<ModuleWithImportedPathInitializedCallback>()
export function _executeRequiredSubscription(id: Metro.ModuleID) {
export function _execReqSubs(id: Metro.ModuleID) {
for (const cb of _reqAll) cb(id)
if (_reqSpecific.has(id)) {
for (const cb of _reqSpecific.get(id)!) cb(id)
_reqSpecific.delete(id)
if (_req.has(id)) {
for (const cb of _req.get(id)!) cb(id)
_req.delete(id)
}
}
export function _executeInitializedSubscription(id: Metro.ModuleID, exports: Metro.ModuleExports) {
export function _execInitSubs(id: Metro.ModuleID, exports: Metro.ModuleExports) {
for (const cb of _initAll) cb(id, exports)
if (_initSpecific.has(id)) {
for (const cb of _initSpecific.get(id)!) cb(id, exports)
_initSpecific.delete(id)
if (_init.has(id)) {
for (const cb of _init.get(id)!) cb(id, exports)
_init.delete(id)
}
}
export function _executeImportedPathSubscription(id: Metro.ModuleID, path: string) {
for (const cb of _importedPath) cb(id, path)
export function _execPathSubs(id: Metro.ModuleID, path: string) {
for (const cb of _path) cb(id, path)
}

View file

@ -1,9 +1,9 @@
import { _importedPath, _initAll, _initSpecific, _reqAll, _reqSpecific } from './_internal'
import { _init, _initAll, _path, _req, _reqAll } from './_internal'
import type { Metro } from '../../../types'
import type { Metro } from '../../types'
import type { initializedModuleHasBadExports } from '../utils'
export type ModuleRequiredCallback = (id: Metro.ModuleID) => void
export type ModuleFirstRequiredCallback = (id: Metro.ModuleID) => void
export type ModuleInitializedCallback = (id: Metro.ModuleID, exports: Metro.ModuleExports) => void
export type ModuleWithImportedPathInitializedCallback = (id: Metro.ModuleID, path: string) => void
@ -30,8 +30,12 @@ export function onAnyModuleInitialized(callback: ModuleInitializedCallback) {
* @returns A function that unregisters the callback.
*/
export function onModuleInitialized(id: Metro.ModuleID, callback: ModuleInitializedCallback) {
if (!_initSpecific.has(id)) _initSpecific.set(id, new Set())
const set = _initSpecific.get(id)!
let set = _init.get(id)
if (!set) {
set = new Set()
_init.set(id, set)
}
set.add(callback)
return () => set.delete(callback)
}
@ -45,8 +49,8 @@ export function onModuleInitialized(id: Metro.ModuleID, callback: ModuleInitiali
* @returns A function that unregisters the callback.
*/
export function onModuleFinishedImporting(callback: ModuleWithImportedPathInitializedCallback) {
_importedPath.add(callback)
return () => _importedPath.delete(callback)
_path.add(callback)
return () => _path.delete(callback)
}
/**
@ -57,9 +61,9 @@ export function onModuleFinishedImporting(callback: ModuleWithImportedPathInitia
* @param callback The callback to be called.
* @returns A function that unregisters the callback.
*/
export function onAnyModuleFirstRequired(callback: ModuleRequiredCallback) {
_reqAll.add(id => callback(id))
return () => _reqAll.delete(id => callback(id))
export function onAnyModuleFirstRequired(callback: ModuleFirstRequiredCallback) {
_reqAll.add(callback)
return () => _reqAll.delete(callback)
}
/**
@ -71,9 +75,13 @@ export function onAnyModuleFirstRequired(callback: ModuleRequiredCallback) {
* @param callback The callback to be called.
* @returns A function that unregisters the callback.
*/
export function onModuleFirstRequired(id: Metro.ModuleID, callback: ModuleRequiredCallback) {
if (!_reqSpecific.has(id)) _reqSpecific.set(id, new Set())
const set = _reqSpecific.get(id)!
export function onModuleFirstRequired(id: Metro.ModuleID, callback: ModuleFirstRequiredCallback) {
let set = _req.get(id)
if (!set) {
set = new Set()
_req.set(id, set)
}
set.add(callback)
return () => set.delete(callback)
}

View file

@ -1,7 +1,7 @@
import { isProxy } from '@revenge-mod/utils/proxy'
import { _mInited, _mMd, _mUninited } from './_internal'
import { _inits, _metas, _uninits } from './_internal'
import type { Metro } from '../../types'
import type { Metro } from '../types'
/**
* Returns whether an uninitialized module has bad exports.
@ -11,7 +11,7 @@ import type { Metro } from '../../types'
* @param id The module ID.
*/
export function uninitializedModuleHasBadExports(id: Metro.ModuleID): boolean {
return !_mUninited.has(id)
return !_uninits.has(id)
}
/**
@ -22,7 +22,7 @@ export function uninitializedModuleHasBadExports(id: Metro.ModuleID): boolean {
* @param id The module ID.
*/
export function initializedModuleHasBadExports(id: Metro.ModuleID): boolean {
return !_mInited.has(id)
return !_inits.has(id)
}
/**
@ -30,7 +30,7 @@ export function initializedModuleHasBadExports(id: Metro.ModuleID): boolean {
* @param id The module ID.
*/
export function getModuleDependencies(id: Metro.ModuleID): Metro.DependencyMap | undefined {
return _mMd.get(id)?.[0]
return _metas.get(id)?.[0]
}
/**
@ -38,7 +38,7 @@ export function getModuleDependencies(id: Metro.ModuleID): Metro.DependencyMap |
* @param id The module ID.
*/
export function isModuleInitialized(id: Metro.ModuleID): boolean {
return _mMd.get(id)?.[1]!
return _metas.get(id)?.[1]!
}
/**
@ -46,7 +46,7 @@ export function isModuleInitialized(id: Metro.ModuleID): boolean {
* @param id The module ID.
*/
export function getInitializedModuleExports(id: Metro.ModuleID): Metro.ModuleExports | undefined {
return _mMd.get(id)?.[2]?.exports
return _metas.get(id)?.[2]?.exports
}
/**
@ -80,7 +80,7 @@ export function isModuleExportsBad(exports: Metro.ModuleExports): boolean {
// Checking if the object is empty
(exports.__proto__ === Object.prototype && !Reflect.ownKeys(exports).length) ||
// Can't run isProxy() on this because this isn't your typical proxy:
// https://github.com/discord/react-native/blob/master/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp
// https://github.com/facebook/react-native/blob/master/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp
exports === nativeModuleProxy
)
}

View file

@ -3,7 +3,7 @@ import type { If, Nullish } from '@revenge-mod/utils/types'
/**
* Metro is a bundler for React Native.
*
* @see {@link https://github.com/facebook/metro/blob/1361405/packages/metro-runtime/src/polyfills/require.js}
* @see {@link https://github.com/facebook/metro/blob/main/packages/metro-runtime/src/polyfills/require.js}
*/
export namespace Metro {
export type DependencyMap = Array<ModuleID>
@ -68,3 +68,8 @@ export namespace Metro {
export type ModuleExports = any
}
/**
* Maybe the default export matched instead of the namespace, because you're using `options.returnNamespace`.
*/
export type MaybeDefaultExportMatched<T> = T | { default: T }

View file

@ -13,7 +13,7 @@
"default": "./src/preinit.ts"
},
"./types": {
"default": "./types.ts"
"default": "./src/types.ts"
}
}
}

View file

@ -1,10 +1,7 @@
import { lookupModuleId, lookupModuleIds } from '@revenge-mod/modules/finders'
import { byProps } from '@revenge-mod/modules/finders/filters'
import { lookupModuleId, lookupModuleIds } from '@revenge-mod/modules/finders/lookup'
import { yielded } from '@revenge-mod/utils/generators'
export * as jsxRuntime from './jsx-runtime'
export * from '../types'
export const ReactModuleId = yielded(lookupModuleIds(byProps<typeof import('react')>('useState')), 1)!
export const ReactNativeModuleId = lookupModuleId(byProps<typeof import('react-native')>('AppRegistry'))!
export const ReactJsxRuntimeModuleId = yielded(lookupModuleIds(byProps<typeof import('react/jsx-runtime')>('jsx'))!, 1)

View file

@ -3,9 +3,6 @@
"private": true,
"type": "module",
"exports": {
".": {
"default": "./src/index.ts"
},
"./callbacks": {
"default": "./src/callbacks.ts"
},

View file

@ -1,9 +0,0 @@
export * from './callbacks'
export * from './errors'
export * from './generators'
export * from './logger'
export * from './objects'
export * from './proxy'
export * from './react'
export * from './trees'
export * from './types'