revenge-bundle-next/lib/assets/src/preinit.ts

144 lines
4.8 KiB
TypeScript
Raw Permalink Normal View History

import { lookupModules, waitForModules } from '@revenge-mod/modules/finders'
import {
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
withDependencies,
withName,
withProps,
} from '@revenge-mod/modules/finders/filters'
import {
mInitializingId,
mUninitialized,
} from '@revenge-mod/modules/metro/patches'
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
import { proxify } from '@revenge-mod/utils/proxy'
2025-07-13 19:52:32 +07:00
import { aOverrides } from './_internal'
import { cache, cacheAsset, Uncached } from './caches'
import type { Metro } from '@revenge-mod/modules/types'
2025-05-08 23:41:55 +07:00
import type { ReactNative } from '@revenge-mod/react/types'
import type { Asset, PackagerAsset } from './types'
2025-05-08 23:41:55 +07:00
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
const { relative } = withDependencies
// https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Image/AssetSourceResolver.js
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
const withAssetSourceResolver = withDependencies([
withName('_classCallCheck'),
withName('_createClass'),
relative(1),
relative(2),
null,
null,
])
const cachedOnly = { cached: true }
2025-05-08 23:41:55 +07:00
// Tracking/caching assets
2025-06-03 16:24:58 +07:00
const unsubAR = waitForModules(
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
withProps<ReactNative.AssetsRegistry>('registerAsset'),
2025-06-03 16:24:58 +07:00
(exports, id) => {
AssetsRegistryModuleId = id
AssetsRegistry = exports as ReactNative.AssetsRegistry
2025-06-03 16:24:58 +07:00
// There are two matching exports. One is the original, and one is a re-export.
// The original asset-registry is simply required by the re-exported one with no changes.
2025-05-08 23:41:55 +07:00
2025-06-03 16:24:58 +07:00
// This is the re-exported registry, because it has dependencies.
// We can begin the caching process here, asset registrar modules have the reexported registry as a dependency.
if (getModuleDependencies(id)!.length) {
unsubAR()
2025-05-08 23:41:55 +07:00
if (cache === Uncached) {
// Resolve reference once and keep in closure
const metroRequire = __r
2025-08-22 02:37:18 +07:00
// 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 mUninitialized) {
if (mId < firstAssetModuleId) continue
const deps = getModuleDependencies(mId)!
if (deps.length === 1 && deps[0] === id) metroRequire(mId)
}
}
2025-05-08 23:41:55 +07:00
2025-06-03 16:24:58 +07:00
// We already patched the original asset registry, so we don't need to patch again.
return
2025-05-08 23:41:55 +07:00
}
2025-06-03 16:24:58 +07:00
const orig = exports.registerAsset
exports.registerAsset = (asset: Asset) => {
const result = orig(asset as PackagerAsset)
2025-05-08 23:41:55 +07:00
// Cache and set moduleId for packager assets only
if ((asset as PackagerAsset).__packager_asset) {
asset.moduleId = mInitializingId
2025-07-05 13:12:57 +07:00
cacheAsset(asset, mInitializingId!)
2025-06-03 16:24:58 +07:00
}
2025-05-08 23:41:55 +07:00
return (asset.id = result)
2025-05-08 23:41:55 +07:00
}
2025-06-03 16:24:58 +07:00
},
cachedOnly,
2025-06-03 16:24:58 +07:00
)
2025-05-08 23:41:55 +07:00
/**
2026-08-01 14:49:54 +07:00
* If you need to use this ID during `preInit`, unproxify {@link AssetsRegistry} first.
*
* ```js
2026-08-01 14:49:54 +07:00
* preInit() {
* unproxify(AssetsRegistry)
* // Module ID will now be set!
* AssetsRegistryModuleId // ...
* }
* ```
*/
export let AssetsRegistryModuleId: Metro.ModuleID | undefined
export let AssetsRegistry: ReactNative.AssetsRegistry = proxify(() => {
if (AssetsRegistryModuleId !== undefined) return __r(AssetsRegistryModuleId)
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
for (const [, id] of lookupModules(withDependencies([[]]), {
initialize: false,
})) {
const deps = getModuleDependencies(id)!
// The module next to assets-registry is AssetSourceResolver
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
if (withAssetSourceResolver(deps[0] + 1)) {
const module = __r(id)
// ID will be set by the wait below
if (module?.registerAsset) return (AssetsRegistry = module)
}
}
throw new Error('assets-registry not found')
})
// TODO: Native overrides?
2025-05-08 23:41:55 +07:00
// Asset overrides
const unsubRAS = waitForModules(
feat!: module finders refactor (#6) - Renamed `byX` filters to `withX`. It is more correct. For example: You don't lookup, wait for, or get modules **BY** props. You lookup, wait for, and get modules **WITH** props. - Renamed `*byImportedPath` to `*withImportedPath` For consistency with the above change. - Renamed `getModule` to `getModules`. The API allows getting multiple modules and not just a single one. Types are renamed as well. `getModuleByImportedPath` remains the same as it only gets one single module with a unique imported path. - `withX` filter **helpers** removed. They can now be replaced with the new capability-based `and` filter. - `preferExports` and `moduleStateAware` filters removed. They can now be replaced with the new capability-based `and` filter. - Composite finders renamed. `some` -> `or`, `every` -> `and` - Composite finders takes a capability-based approach. - Filter generators now need to attach runtime flag to let the system know whether the generated filter can be used to filter modules without proper exports or not. ## Misc. - Removed useless unsub call in Profile Badges - Use `queueMicrotask` with `asap` # Features ## Capability-based approach to composite filters ```js // Old byProps(...).withDependencies(...) // Old, same as: preferExports(byProps(...), byDependencies(...)) every(byDependencies(...), byProps(...)) // Old, ordering matters, this would fail unexpectedly! every(byProps(...), byDependencies(...)) // New withProps(...).and(withDependencies(...)) // New (as well), ordering doesn't matter, this has the same result as above withDependencies(...).and(withProps(...)) ```
2025-08-30 09:19:57 +07:00
withName<{
2025-06-03 16:24:58 +07:00
addCustomSourceTransformer: (
transformer: (arg: { asset: Asset }) => PackagerAsset,
2025-06-03 16:24:58 +07:00
) => void
2025-05-08 23:41:55 +07:00
}>('resolveAssetSource'),
rAS => {
2025-05-08 23:41:55 +07:00
unsubRAS()
// Custom assets
// Why do we need to do this? Because RN/Discord (unsure which) will attempt to resolve the asset via its path, which we don't provide via custom assets.
2025-05-08 23:41:55 +07:00
// This will result in a crash, because it tries to read the path and run checks on it, but the path is undefined.
// @ts-expect-error
rAS.addCustomSourceTransformer(({ asset }) => {
2025-07-01 17:30:53 +07:00
if (!(asset as PackagerAsset).__packager_asset) return asset
2025-05-08 23:41:55 +07:00
})
// Asset overrides
// @ts-expect-error
2025-07-05 13:12:57 +07:00
rAS.addCustomSourceTransformer(({ asset }) => aOverrides.get(asset))
2025-05-08 23:41:55 +07:00
},
cachedOnly,
2025-05-08 23:41:55 +07:00
)
if (cache === Uncached) {
// Initialize the module immediately, so we can cache assets ASAP
AssetsRegistry.getAssetByID(0)
}