fix(lib): migrate to new withDependencies APIs

This commit is contained in:
PalmDevs 2026-08-12 15:47:36 +07:00
parent d3fb84b76d
commit ecb2cf810b
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
3 changed files with 71 additions and 88 deletions

View file

@ -1,6 +1,5 @@
import { lookupModule, lookupModules } from '@revenge-mod/modules/finders'
import { withDependencies } from '@revenge-mod/modules/finders/filters'
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
import {
ReactJSXRuntimeModuleId,
ReactModuleId,
@ -10,7 +9,7 @@ import { proxify } from '@revenge-mod/utils/proxy'
import { ImportTrackerModuleId } from './common/import-tracker'
import type { DiscordModules } from './types'
const { loose } = withDependencies
const { atLeast, last, loose, relative } = withDependencies
/**
* The lowest amount of dependencies `design/native.tsx` is expected to have.
@ -29,13 +28,12 @@ export let Design: Design = proxify(
// [3909, 4608, 5182, 13172, 2, ...] (141 dependencies)
const [, id] = lookupModule(
withDependencies<Design>(
loose([
atLeast(DesignMinimumDependencies, [
[ImportTrackerModuleId],
[ImportTrackerModuleId],
[ReactNativeModuleId, ImportTrackerModuleId],
[ImportTrackerModuleId],
ImportTrackerModuleId,
...Array<null>(DesignMinimumDependencies - 5).fill(null),
]),
).keyAs('revenge.discord.design.Design'),
{
@ -55,9 +53,6 @@ export let Design: Design = proxify(
// design/components/Forms/native/FormSwitch.native.tsx
export let FormSwitch: DiscordModules.Components.FormSwitch = proxify(() => {
// TODO: Possibly come up with a better dependency fingerprinting API
// to not have to deal with this bullshit
for (const [, id] of lookupModules(
withDependencies(
loose([
@ -66,17 +61,25 @@ export let FormSwitch: DiscordModules.Components.FormSwitch = proxify(() => {
ReactNativeModuleId,
ReactJSXRuntimeModuleId,
]),
).keyAs('revenge.discord.design.FormSwitch'),
)
.and(
withDependencies(
last([
relative(1),
relative(2),
null,
null,
ImportTrackerModuleId,
]),
),
)
.keyAs('revenge.discord.design.FormSwitch'),
{
initialize: false,
},
)) {
const deps = getModuleDependencies(id)!
if (deps.at(-1) !== ImportTrackerModuleId) continue
if (deps.at(-4) === id + 2 && deps.at(-5) === id + 1) {
const FormSwitch_ = __r(id)!.FormSwitch
if (FormSwitch_) return (FormSwitch = FormSwitch_)
}
const FormSwitch_ = __r(id)!.FormSwitch
if (FormSwitch_) return (FormSwitch = FormSwitch_)
}
})!

View file

@ -9,7 +9,6 @@ import {
withDependencies,
withName,
} from '@revenge-mod/modules/finders/filters'
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
import { asap, noop } from '@revenge-mod/utils/callback'
import {
cache,
@ -75,14 +74,19 @@ export function getStore<T>(
]
*/
const withLeadingFluxStoreDeps = withDependencies(
withDependencies.loose([
withName('_classCallCheck'),
withName('_createClass'),
withName('_possibleConstructorReturn'),
withName('bound getPrototypeOf'),
withName('_inherits'),
]),
const { last, loose } = withDependencies
// The import tracker is checked first, as it is far cheaper than resolving the leading dependencies' exports
const withFluxStoreDeps = withDependencies(last([ImportTrackerModuleId])).and(
withDependencies(
loose([
withName('_classCallCheck'),
withName('_createClass'),
withName('_possibleConstructorReturn'),
withName('bound getPrototypeOf'),
withName('_inherits'),
]),
),
)
export type WithStore = FilterGenerator<
@ -102,9 +106,7 @@ export const withStore = createFilterGenerator(
(_, id, exports, initialized) => {
if (initialized) return Boolean(exports?._dispatchToken)
if (!withLeadingFluxStoreDeps(id)) return false
const deps = getModuleDependencies(id)!
return deps[deps.length - 1] === ImportTrackerModuleId
return withFluxStoreDeps(id, undefined, false)
},
() => 'revenge.discord.store',
FilterScopes.Uninitialized | FilterScopes.Initialized,

View file

@ -7,39 +7,50 @@ import {
withDependencies,
} from '@revenge-mod/modules/finders/filters'
import { isModuleExportBad } from '@revenge-mod/modules/metro/utils'
import {
ReactJSXRuntimeModuleId,
ReactModuleId,
ReactNativeModuleId,
} from '@revenge-mod/react'
import { FilterScopes } from '../../modules/src/finders/filters/constants'
import type {
Filter,
FilterGenerator,
} from '@revenge-mod/modules/finders/filters'
import type { Metro } from '@revenge-mod/modules/types'
import type { FC } from 'react'
// [React, ReactJsxRuntime, (Tokens), (BaseIconImage), (Asset), ImportTracker]
// Asset = relativeDep(1)
const IconComponentFilter = [
ReactModuleId,
ReactJSXRuntimeModuleId,
TokensModuleId,
null,
null,
ImportTrackerModuleId,
]
const { last } = withDependencies
// Generated IconComponents that have multiple assets, there is no specific length, so the filter has to be generated on-the-fly.
// [React, ReactNative, ReactJsxRuntime, (Tokens), (BaseIconImage), (...Assets), ImportTracker]
// ...Assets = relativeDep(1..n)
const MultiIconComponentFilterBase = [
ReactModuleId,
ReactNativeModuleId,
ReactJSXRuntimeModuleId,
TokensModuleId,
null,
]
const depsFilters: Record<string, Filter> = {}
/**
* Builds a dependency filter for an icon component.
*
* `[React, (ReactNative), ReactJsxRuntime, Tokens, (BaseIconImage), (...Assets), ImportTracker]`
*
* `ReactNative` is only present on components with multiple assets, and it sits right after `React`,
* so everything from `ReactJsxRuntime` onwards is at a fixed distance from the end on both shapes.
*
* @param names The component name, then the asset names if the component has multiple assets.
* @returns The filter, or `undefined` if any of the assets aren't registered yet.
*/
function depsFilterFor(names: string[]) {
const key = names.join(',')
const cached = depsFilters[key]
if (cached) return cached
// Components with multiple assets are named separately from their assets
const assets = names.length > 1 ? names.slice(1) : names
const mids: Metro.ModuleID[] = []
for (const name of assets) {
const mid = getAssetByName(name)?.moduleId
// Module ID can never be zero, so if it's falsy, it means the asset was not found.
if (!mid) return
mids.push(mid)
}
return (depsFilters[key] = withDependencies(
last([TokensModuleId, null, ...mids, ImportTrackerModuleId]),
))
}
export type WithGeneratedIconComponent = FilterGenerator<
<N extends string>(
@ -82,44 +93,11 @@ export type WithGeneratedIconComponent = FilterGenerator<
export const withGeneratedIconComponent = createFilterGenerator<
Parameters<WithGeneratedIconComponent>
>(
(names, id, exports) => {
if (typeof exports === 'object' && isModuleExportBad(exports)) {
if (typeof exports[names[0]] === 'function')
return Object.keys(exports).length === 1
} else {
let filter = IconComponentFilter
(names, id, exports, initialized) => {
// Icon components always export their component, so unusable exports can never match
if (initialized && isModuleExportBad(exports)) return false
if (names.length > 1) {
const mids = []
for (let i = 1; i < names.length; i++) {
const name = names[i]
const mid = getAssetByName(name)?.moduleId
// Module ID can never be zero, so if it's falsy, it means the asset was not found.
if (!mid) return false
mids.push(mid)
}
filter = [
...MultiIconComponentFilterBase,
...mids,
ImportTrackerModuleId,
]
} else {
const [name] = names
const mid = getAssetByName(name)?.moduleId
// Module ID can never be zero, so if it's falsy, it means the asset was not found.
if (!mid) return false
IconComponentFilter[4] = mid
}
return withDependencies(filter)(id, exports)
}
return false
return depsFilterFor(names)?.(id, exports, initialized) ?? false
},
names => `revenge.utils.discord.generatedIconComponent(${names.join(',')})`,
FilterScopes.Uninitialized | FilterScopes.Initialized,