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

92 lines
3.5 KiB
TypeScript
Raw Normal View History

2025-07-05 13:12:57 +07:00
import { mInitializingId, mUninitialized } from '@revenge-mod/modules/_/metro'
2025-05-08 23:41:55 +07:00
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
import { waitForModules } from '@revenge-mod/modules/finders/wait'
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
import { aCallbacks, aOverrides } from './_internal'
import { cacheAsset, cached } from './caches'
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
export let AssetsRegistry: ReactNative.AssetsRegistry
export let AssetsRegistryModuleId: number
2025-05-08 23:41:55 +07:00
// Tracking/caching assets
2025-06-03 16:24:58 +07:00
const unsubAR = waitForModules(
byProps<ReactNative.AssetsRegistry>('registerAsset'),
(exports, id) => {
AssetsRegistryModuleId = id
AssetsRegistry = exports as ReactNative.AssetsRegistry
2025-06-03 16:24:58 +07:00
for (const cb of aCallbacks)
try {
cb()
} catch {}
aCallbacks.clear()
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
// TODO(lib/assets/caches): Ideally we should not wait for the cache promise here, see the promise impl for more info.
cached.then(cached => {
if (!cached) {
// More fragile way, but also more performant:
// There is exactly one asset before the reexported asset registry :/
const firstAssetModuleId = id - 1
2025-07-05 13:12:57 +07:00
for (const mId of mUninitialized) {
if (mId < firstAssetModuleId) continue
const deps = getModuleDependencies(mId)!
if (deps.length === 1 && deps[0] === id) __r(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) {
2025-07-05 13:12:57 +07:00
asset.moduleId = mInitializingId!
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
},
)
2025-05-08 23:41:55 +07:00
// Asset overrides
const unsubRAS = waitForModules(
byName<{
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
},
)