diff --git a/.changeset/late-crabs-deliver.md b/.changeset/late-crabs-deliver.md new file mode 100644 index 000000000..b3daa3b58 --- /dev/null +++ b/.changeset/late-crabs-deliver.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack": patch +--- + +Fix early JS errors not being displayed in LogBox diff --git a/apps/tester-app/android/gradle.properties b/apps/tester-app/android/gradle.properties index 0c541f3a9..b729139a5 100644 --- a/apps/tester-app/android/gradle.properties +++ b/apps/tester-app/android/gradle.properties @@ -38,6 +38,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # Note that this is incompatible with web debugging. newArchEnabled=true bridgelessEnabled=true +hermesEnabled=true # Uncomment the line below to build React Native from source. #react.buildFromSource=true diff --git a/apps/tester-app/ios/Podfile.lock b/apps/tester-app/ios/Podfile.lock index 6aa37aae3..794d6a02f 100644 --- a/apps/tester-app/ios/Podfile.lock +++ b/apps/tester-app/ios/Podfile.lock @@ -1,6 +1,6 @@ PODS: - boost (1.84.0) - - callstack-repack (5.0.0-rc.2): + - callstack-repack (5.0.0-rc.3): - DoubleConversion - glog - hermes-engine @@ -1942,7 +1942,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - callstack-repack: 3106db24c24f7a76a380230ff7794d225cecb760 + callstack-repack: 5219eedfb8cb06b905edecffaecf71af4a4ecdd6 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 FBLazyVector: be7314029d6ec6b90f0f75ce1195b8130ed9ac4f fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be diff --git a/packages/repack/src/modules/ScriptManager/__tests__/ScriptManager.test.ts b/packages/repack/src/modules/ScriptManager/__tests__/ScriptManager.test.ts index 49e9458cb..b20003189 100644 --- a/packages/repack/src/modules/ScriptManager/__tests__/ScriptManager.test.ts +++ b/packages/repack/src/modules/ScriptManager/__tests__/ScriptManager.test.ts @@ -20,6 +20,7 @@ jest.mock('../NativeScriptManager', () => ({ })); globalThis.__webpack_require__ = { + i: [], u: (id: string) => `${id}.chunk.bundle`, p: () => '', repack: { diff --git a/packages/repack/src/modules/ScriptManager/types.ts b/packages/repack/src/modules/ScriptManager/types.ts index 6293355cb..9933620eb 100644 --- a/packages/repack/src/modules/ScriptManager/types.ts +++ b/packages/repack/src/modules/ScriptManager/types.ts @@ -1,4 +1,23 @@ +type ModuleExports = Record; + +type ModuleObject = { + id: number; + loaded: boolean; + error?: any; + exports: ModuleExports; +}; + export interface WebpackContext { + i: ((options: { + id: number; + factory: ( + moduleObject: ModuleObject, + moduleExports: ModuleExports, + webpackRequire: WebpackContext + ) => void; + module: ModuleObject; + require: WebpackContext; + }) => void)[]; p: () => string; u: (id: string) => string; } diff --git a/packages/repack/src/plugins/RepackTargetPlugin/RepackTargetPlugin.ts b/packages/repack/src/plugins/RepackTargetPlugin/RepackTargetPlugin.ts index 148ee395b..7758ec414 100644 --- a/packages/repack/src/plugins/RepackTargetPlugin/RepackTargetPlugin.ts +++ b/packages/repack/src/plugins/RepackTargetPlugin/RepackTargetPlugin.ts @@ -61,6 +61,11 @@ export class RepackTargetPlugin implements RspackPluginInstance { compiler.options.output.chunkFormat = 'array-push'; compiler.options.output.globalObject = globalObject; + // Disable built-in strict module error handling + // this is handled through an interceptor in the + // init module added to __webpack_require__.i array + compiler.options.output.strictModuleErrorHandling = false; + // Normalize global object. new compiler.webpack.BannerPlugin({ raw: true, diff --git a/packages/repack/src/plugins/RepackTargetPlugin/implementation/init.ts b/packages/repack/src/plugins/RepackTargetPlugin/implementation/init.ts index 01014fa89..dc93ab84f 100644 --- a/packages/repack/src/plugins/RepackTargetPlugin/implementation/init.ts +++ b/packages/repack/src/plugins/RepackTargetPlugin/implementation/init.ts @@ -13,6 +13,28 @@ module.exports = function () { __webpack_require__.repack = $globalObject$.__repack__ = repackRuntime; + // intercept module factory calls to forward errors to global.ErrorUtils + // aligned with `guardedLoadModule` behaviour in Metro + // https://github.com/facebook/metro/blob/a4cb0b0e483748ef9f1c760cb60c57e3a84c1afd/packages/metro-runtime/src/polyfills/require.js#L329 + __webpack_require__.i.push(function (options) { + var originalFactory = options.factory; + options.factory = function (moduleObject, moduleExports, webpackRequire) { + try { + originalFactory.call(this, moduleObject, moduleExports, webpackRequire); + } catch (e) { + if ($globalObject$.ErrorUtils) { + // exposed as global early on, part of `@react-native/js-polyfills` error-guard + // https://github.com/facebook/react-native/blob/4dac99cf6d308e804efc098b37f5c24c1eb611cf/packages/polyfills/error-guard.js#L121 + $globalObject$.ErrorUtils.reportFatalError(e); + } else { + // error happened before ErrorUtils was initialized + // at this point in runtime we can only rethrow the error + throw e; + } + } + }; + }); + function loadScript( name: string, caller: string | undefined,