fix(lib/modules/metro): move all error handling to metroRequire

This commit is contained in:
PalmDevs 2026-08-01 00:18:32 +07:00
parent ffce0e2cf2
commit 6d7e017c0b
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
2 changed files with 38 additions and 28 deletions

View file

@ -1,6 +1,3 @@
import { callNativeMethodSync } from '@revenge-mod/modules/native'
import { getErrorStack } from '@revenge-mod/utils/error'
import { FullVersion } from '~constants'
import { cache, cacheBlacklistedModule, Uncached } from '../caches'
import {
global,
@ -87,6 +84,7 @@ const metroDefine = (
flags: 0,
module: undefined,
factory: () => {
// def.module set in metroRequire before factory is called
handleFactoryCall(factory, def.module!)
},
importedDefault: undefined,
@ -187,19 +185,6 @@ function handleFactoryCall(
cacheBlacklistedModule(mInitializingId)
}
}
} catch (e) {
const msg = `Module ${mInitializingId} failed to initialize:\n\n${getErrorStack(e)}`
if (__DEV__) {
callNativeMethodSync('revenge.alertError', [msg, FullVersion])
} else {
// So... it wasn't a great idea to throw, Discord has pushed a broken build that has some failing modules
// Vanilla Metro would swallow the error and just return an empty object as the exports..., insanity
// throw e
moduleObject.exports = {}
cacheBlacklistedModule(mInitializingId)
nativeLoggingHook(msg, 2)
}
} finally {
mUninitialized.delete(mInitializingId)
mInitializingId = prevId

View file

@ -5,10 +5,15 @@
* Also avoids cloning exports, allowing for patches to be applied directly without checking for clones.
*/
import { loadModuleFromSegment, mList } from './patches'
import { callNativeMethodSync } from '@revenge-mod/modules/native'
import { getErrorStack } from '@revenge-mod/utils/error'
import { FullVersion } from '~constants'
import { loadModuleFromSegment, mInitializingId, mList } from './patches'
import { executeInitializeSubscriptions } from './subscriptions/_internal'
import type { Metro } from '../types'
export const mErrorChain: Metro.ModuleID[] = []
export const Initialized = 1 << 0
const HasError = 1 << 1
const HasImportedDefault = 1 << 2
@ -30,7 +35,11 @@ export const metroRequire = (moduleId => {
let { flags, module: moduleObject } = mod
if (flags & InitializedOrInitializing) return moduleObject!.exports
if (flags & HasError) throw mod.error
if (flags & HasError) {
// Swallow the error and return an empty object, like what Metro does.
// throw mod.error
return moduleObject!.exports
}
mod.flags |= Initializing
@ -44,21 +53,37 @@ export const metroRequire = (moduleId => {
mod.factory = undefined
factory!()
mod.flags = (flags & NotInitializedOrInitializingMask) | Initialized
executeInitializeSubscriptions(moduleId, moduleObject.exports)
return moduleObject.exports
} catch (e) {
mod.flags = (flags & NotInitializedOrInitializingMask) | HasError
mod.error = e
mod.module = undefined
// @ts-expect-error: Not documented, but used by React Native
if (global.ErrorUtils) global.ErrorUtils.reportFatalError(e)
else throw e
const msg = `Module ${mInitializingId} failed to initialize:\n\n${getErrorStack(e)}`
if (__DEV__) {
callNativeMethodSync('revenge.alertError', [msg, FullVersion])
} else {
nativeLoggingHook(msg, 2)
}
// Some modifications can cause modules to fail by initializing them in the wrong order, we can't just blacklist them
// cacheBlacklistedModule(mInitializingId)
// So... it wasn't a great idea to throw, Discord has pushed a broken build that has some failing modules
// Vanilla Metro would swallow the error and just return an empty object as the exports..., insanity
// // @ts-expect-error: Not documented, but used by React Native
// if (global.ErrorUtils) global.ErrorUtils.reportFatalError(e)
// else throw e
mErrorChain.push(moduleId)
return (moduleObject.exports = {})
}
mod.flags = (flags & NotInitializedOrInitializingMask) | Initialized
executeInitializeSubscriptions(moduleId, moduleObject.exports)
return moduleObject.exports
}) as Metro.Require
global.__r = metroRequire