diff --git a/bun.lock b/bun.lock index ea1e969..20d8c2c 100644 --- a/bun.lock +++ b/bun.lock @@ -31,6 +31,9 @@ "lib/discord": { "name": "@revenge-mod/discord", }, + "lib/externals": { + "name": "@revenge-mod/externals", + }, "lib/init": { "name": "@revenge-mod/init", }, @@ -431,6 +434,8 @@ "@revenge-mod/discord": ["@revenge-mod/discord@workspace:lib/discord"], + "@revenge-mod/externals": ["@revenge-mod/externals@workspace:lib/externals"], + "@revenge-mod/init": ["@revenge-mod/init@workspace:lib/init"], "@revenge-mod/modules": ["@revenge-mod/modules@workspace:lib/modules"], diff --git a/lib/externals/README.md b/lib/externals/README.md new file mode 100644 index 0000000..176b3ce --- /dev/null +++ b/lib/externals/README.md @@ -0,0 +1,18 @@ +# @revenge-mod/externals + +This package provides common external libraries bundled in React Native apps, but this does not guarantee that they will be available. + +This is done to ensure that Revenge's core APIs won't change if the external libraries are removed from the app. + +## Libraries + +For obvious reasons, this package only maintains lookups for specific versions of libraries that Discord uses. +However, it can be adapted to work with any other React Native apps. + +- `@react-navigation/native` +- `@react-navigation/stack` + +- `@shopify/flash-list` + +- `util` (`browserify/node-util`) + \ No newline at end of file diff --git a/lib/externals/package.json b/lib/externals/package.json new file mode 100644 index 0000000..731193e --- /dev/null +++ b/lib/externals/package.json @@ -0,0 +1,19 @@ +{ + "name": "@revenge-mod/externals", + "private": true, + "type": "module", + "exports": { + ".": { + "default": "./src/index.ts" + }, + "./browserify": { + "default": "./src/browserify.ts" + }, + "./react-navigation": { + "default": "./src/react-navigation.ts" + }, + "./shopify": { + "default": "./src/shopify.ts" + } + } +} diff --git a/lib/externals/src/browserify.ts b/lib/externals/src/browserify.ts new file mode 100644 index 0000000..5b33bf9 --- /dev/null +++ b/lib/externals/src/browserify.ts @@ -0,0 +1,25 @@ +import { lookupModule } from '@revenge-mod/modules/finders' +import { byDependencies, byProps, moduleStateAware, relativeDep } from '@revenge-mod/modules/finders/filters' +import { proxify } from '@revenge-mod/utils/proxy' + +export let nodeUtil: typeof import('node:util') = proxify( + () => { + const module = lookupModule( + moduleStateAware(byProps('inspect'), byDependencies([relativeDep(1), [], []])), + { + includeUninitialized: true, + }, + ) + + if (module) { + // This allows the Proxy instance to be garbage collected + // after the module is initialized. + nodeUtil = module + gc() + return module + } + }, + { + hint: 'object', + }, +)! diff --git a/lib/externals/src/index.ts b/lib/externals/src/index.ts new file mode 100644 index 0000000..61adbcf --- /dev/null +++ b/lib/externals/src/index.ts @@ -0,0 +1,3 @@ +export * as browserify from './browserify' +export * as reactNavigation from './react-navigation' +export * as shopify from './shopify' diff --git a/lib/externals/src/react-navigation.ts b/lib/externals/src/react-navigation.ts new file mode 100644 index 0000000..bda83bf --- /dev/null +++ b/lib/externals/src/react-navigation.ts @@ -0,0 +1,66 @@ +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')) + +export let ReactNavigationNative: typeof import('@react-navigation/native') = proxify( + () => { + const module = lookupModule( + moduleStateAware( + byProps('useLinkTo'), + byDependencies(looseDeps([[], looseDeps([[_createClassModuleId, _classCallCheckModuleId]])])), + ), + { + includeUninitialized: true, + }, + ) + + if (module) { + // This allows the Proxy instance to be garbage collected + // after the module is initialized. + ReactNavigationNative = module + gc() + return module + } + }, + { + hint: 'object', + }, +)! + +export let ReactNavigationStack: typeof import('@react-navigation/stack') = proxify( + () => { + const module = lookupModule( + moduleStateAware( + byProps('createStackNavigator'), + byDependencies(looseDeps([[], looseDeps([[_createClassModuleId, _classCallCheckModuleId]])])), + ), + { + includeUninitialized: true, + }, + ) + + if (module) { + // This allows the Proxy instance to be garbage collected + // after the module is initialized. + ReactNavigationStack = module + gc() + return module + } + }, + { + hint: 'object', + }, +)! + +export interface ReactNavigationParamList { + [Page: string]: any +} + +declare global { + namespace ReactNavigation { + interface RootParamList extends ReactNavigationParamList {} + } +} diff --git a/lib/externals/src/shopify.ts b/lib/externals/src/shopify.ts new file mode 100644 index 0000000..8ba7f34 --- /dev/null +++ b/lib/externals/src/shopify.ts @@ -0,0 +1,33 @@ +import { byDependencies, byProps, moduleStateAware } from '@revenge-mod/modules/finders/filters' +import { lookupModule } from '@revenge-mod/modules/finders' +import { proxify } from '@revenge-mod/utils/proxy' + +import { ReactJsxRuntimeModuleId, ReactModuleId, ReactNativeModuleId } from '@revenge-mod/react' + +export let FlashList: typeof import('@shopify/flash-list') = proxify( + () => { + const module = lookupModule( + moduleStateAware( + byProps('FlashList'), + // biome-ignore lint/suspicious/noSparseArray: don't care + byDependencies([ReactModuleId, ReactNativeModuleId, ReactJsxRuntimeModuleId, , , , 2, ,]), + // Dependencies. One in brackets are dynamic or late initialized. + // [React, RN, JSXRuntime, (FlashListExports), (Reanimated), (RNBottomSheet), ImportTracker, (BottomSheetFlashList)] + ), + { + includeUninitialized: true, + }, + ) + + if (module) { + // This allows the Proxy instance to be garbage collected + // after the module is initialized. + FlashList = module + gc() + return module + } + }, + { + hint: 'object', + }, +)!