feat(lib/assets): add api for registering custom and overriding assets

This commit is contained in:
PalmDevs 2025-05-07 00:17:12 +07:00
parent 7450447c1b
commit 72bd9c00fb
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
2 changed files with 57 additions and 4 deletions

View file

@ -5,9 +5,16 @@ import { AssetRegistry } from './patches'
import type { ReactNative as RN } from '@revenge-mod/types'
export type Asset = RN.AssetsRegistry.PackagerAsset
export type Asset = ReactNativeAsset | RevengeAsset
export type AssetId = number
export type ReactNativeAsset = RN.AssetsRegistry.PackagerAsset
export interface RevengeAsset extends Pick<ReactNativeAsset, 'name' | 'width' | 'height' | 'type'> {
uri: string
}
export type RegisterableAsset = Omit<RevengeAsset, 'id'>
// iOS cannot display SVGs
let _preferredType: Asset['type'] = ReactNative.Platform.OS === 'ios' ? 'png' : 'svg'
@ -60,3 +67,33 @@ export function getAssetsByName(name: string): Record<Asset['type'], Asset> | un
export function setPreferredAssetType(type: Asset['type']) {
_preferredType = type
}
/**
* Register an asset with the given name.
* If an asset with the same name already exists, it will be overridden if `override` is set to true.
*
* @param asset The asset to register.
* @param override Whether to override the existing asset if one exists. `false` by default.
* @throws An error if an asset with the same name and type already exists and `override` is not set to true.
*/
export function registerAsset(asset: RegisterableAsset, override?: boolean) {
const { name } = asset
if (override) _overrides.set(name, asset)
// Make sure to not override the asset if it already exists, unless explicitly set to override
else if (_assets.get(name)?.[1]?.[asset.type])
throw new Error(`Asset "${name}" with type "${asset.type}" already exists`)
// @ts-expect-error
return AssetRegistry.registerAsset(asset)
}
/**
* Remove an asset override.
*
* @param name The name of the asset to remove the override for.
* @returns Whether the override was removed.
*/
export function removeAssetOverride(name: string) {
return _overrides.delete(name)
}

View file

@ -1,15 +1,17 @@
import { _mUninited } from '@revenge-mod/modules/_/metro'
import { waitForModules } from '@revenge-mod/modules/finders'
import { byProps } from '@revenge-mod/modules/finders/filters'
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
import { getModuleDependencies } from '@revenge-mod/modules/metro'
import { _assets, _ids } from './_internal'
import { _assets, _ids, _overrides } from './_internal'
import type { ReactNative } from '@revenge-mod/types'
import type { Asset } from '.'
import type { Asset, ReactNativeAsset } from '.'
export let AssetRegistry: ReactNative.AssetsRegistry
export let AssetRegistryModuleId: number
// Tracking/caching assets
const unsubForAssetRegistry = waitForModules(byProps('registerAsset'), (arId, exports) => {
// We want to do caching only for the re-exported asset-registry
if (!getModuleDependencies(arId)!.length) {
@ -33,6 +35,7 @@ const unsubForAssetRegistry = waitForModules(byProps('registerAsset'), (arId, ex
}
unsubForAssetRegistry()
AssetRegistryModuleId = arId
// TODO(assets/patches): conditionally run this if cache does not exist
// More fragile way, but more performant:
@ -45,3 +48,16 @@ const unsubForAssetRegistry = waitForModules(byProps('registerAsset'), (arId, ex
if (deps.length === 1 && deps[0] === arId) __r(id)
}
})
// Asset overrides
const unsubForResolveAssetSource = waitForModules(
byName<{
addCustomSourceTransformer: (transformer: (arg: { asset: Asset }) => ReactNativeAsset) => void
}>('resolveAssetSource'),
(_, resolveAssetSource) => {
unsubForResolveAssetSource()
// @ts-expect-error
resolveAssetSource.addCustomSourceTransformer(({ asset }) => _overrides.get(asset.name))
},
)