|
| 1 | +// const { withDangerousMod, withMainApplication } = require('@expo/config-plugins'); |
| 2 | +// const fs = require('fs'); |
| 3 | +// const path = require('path'); |
| 4 | + |
| 5 | +// // Inject native uncaught handler + CrashTester module with verbose debug logging |
| 6 | +// module.exports = function withNativeExceptionHandler(config) { |
| 7 | + |
| 8 | +// function resolveAndroidPackage(config) { |
| 9 | +// return ( |
| 10 | +// config?.android?.package || |
| 11 | +// config?.expo?.android?.package || |
| 12 | +// process.env.ANDROID_PACKAGE_NAME || |
| 13 | +// 'com.digitalnomad91.codebuilderadmin' |
| 14 | +// ); |
| 15 | +// } |
| 16 | + |
| 17 | +// // Helper: create crash tester native files if absent |
| 18 | +// function ensureCrashTesterFiles(projectRoot, packageName) { |
| 19 | +// const packagePath = packageName.replace(/\./g, '/'); |
| 20 | +// const javaDir = path.join(projectRoot, 'android', 'app', 'src', 'main', 'java', packagePath); |
| 21 | +// if (!fs.existsSync(javaDir)) fs.mkdirSync(javaDir, { recursive: true }); |
| 22 | +// const moduleFile = path.join(javaDir, 'CrashTesterModule.java'); |
| 23 | +// const packageFile = path.join(javaDir, 'CrashTesterPackage.java'); |
| 24 | + |
| 25 | +// const moduleSource = `package ${packageName};\n\nimport com.facebook.react.bridge.ReactApplicationContext;\nimport com.facebook.react.bridge.ReactContextBaseJavaModule;\nimport com.facebook.react.bridge.ReactMethod;\nimport android.util.Log;\nimport android.os.Handler;\nimport android.os.Looper;\n\npublic class CrashTesterModule extends ReactContextBaseJavaModule {\n public CrashTesterModule(ReactApplicationContext context) { super(context); }\n @Override public String getName() { return \"CrashTester\"; }\n\n @ReactMethod public void dumpDefaultHandler() {\n Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();\n if (h == null) {\n Log.e(\"CrashTester\", \"Default UncaughtExceptionHandler: <null>\");\n } else {\n Log.e(\"CrashTester\", \"Default UncaughtExceptionHandler class: \" + h.getClass().getName());\n Log.e(\"CrashTester\", \"Handler toString(): \" + h.toString());\n }\n }\n\n /** Spawn a new background thread and crash there */\n @ReactMethod public void induceNativeException() {\n Log.d(\"CrashTester\", \"induceNativeException() invoked - spawning crash thread\");\n new Thread(() -> {\n try { Thread.sleep(750); } catch (InterruptedException ignored) {}\n Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();\n Log.e(\"CrashTester\", \"(crash thread) default handler before throw: \" + (h == null ? \"<null>\" : h.getClass().getName()));\n Log.e(\"CrashTester\", \"About to throw uncaught exception on thread: \" + Thread.currentThread().getName());\n throw new RuntimeException(\"CrashTester: induced uncaught thread exception\");\n }, \"CrashTesterThread\").start();\n }\n\n /** Crash on the MAIN (UI) thread */\n @ReactMethod public void induceMainThreadException() {\n Log.d(\"CrashTester\", \"induceMainThreadException() posting to main looper\");\n new Handler(Looper.getMainLooper()).postDelayed(() -> {\n Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();\n Log.e(\"CrashTester\", \"(main thread) default handler before throw: \" + (h == null ? \"<null>\" : h.getClass().getName()));\n Log.e(\"CrashTester\", \"Throwing on MAIN thread: \" + Thread.currentThread().getName());\n throw new RuntimeException(\"CrashTester: induced MAIN thread exception\");\n }, 600);\n }\n}\n`; |
| 26 | +// fs.writeFileSync(moduleFile, moduleSource); |
| 27 | +// console.log('[nativeExceptionHandler] (Re)wrote CrashTesterModule.java'); |
| 28 | + |
| 29 | +// const packageSource = `package ${packageName};\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.NativeModule;\nimport com.facebook.react.bridge.ReactApplicationContext;\nimport com.facebook.react.uimanager.ViewManager;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class CrashTesterPackage implements ReactPackage {\n @Override\n public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); }\n @Override\n public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {\n List<NativeModule> modules = new ArrayList<>();\n modules.add(new CrashTesterModule(reactContext));\n return modules;\n }\n}\n`; |
| 30 | +// if (!fs.existsSync(packageFile) || fs.readFileSync(packageFile, 'utf8') !== packageSource) { |
| 31 | +// fs.writeFileSync(packageFile, packageSource); |
| 32 | +// console.log('[nativeExceptionHandler] (Re)wrote CrashTesterPackage.java'); |
| 33 | +// } |
| 34 | +// } |
| 35 | + |
| 36 | +// // 2. Create crash tester native module files |
| 37 | +// config = withDangerousMod(config, ['android', async (cfg) => { |
| 38 | +// const pkg = resolveAndroidPackage(cfg); |
| 39 | +// console.log('[nativeExceptionHandler] Ensuring CrashTester sources for', pkg); |
| 40 | +// ensureCrashTesterFiles(cfg.modRequest.projectRoot, pkg); |
| 41 | +// return cfg; |
| 42 | +// }]); |
| 43 | + |
| 44 | +// // 3. Register CrashTesterPackage in MainApplication |
| 45 | +// config = withMainApplication(config, (cfg) => { |
| 46 | +// if (!cfg.modResults || !cfg.modResults.contents) return cfg; |
| 47 | +// let contents = cfg.modResults.contents; |
| 48 | +// const pkg = resolveAndroidPackage(cfg); |
| 49 | +// contents = contents.replace(/\nimport .*CrashTesterPackage.*\s*$/,''); |
| 50 | + |
| 51 | +// if (!contents.includes(`import ${pkg}.CrashTesterPackage`)) { |
| 52 | +// const importBlockMatch = contents.match(/(package[\s\S]*?)(import[\s\S]*?)(\nclass\s+MainApplication)/); |
| 53 | +// if (importBlockMatch) { |
| 54 | +// const [full, pkgSeg, importsSeg, classSeg] = importBlockMatch; |
| 55 | +// if (!importsSeg.includes(`import ${pkg}.CrashTesterPackage`)) { |
| 56 | +// const newImports = importsSeg.trimEnd() + `\nimport ${pkg}.CrashTesterPackage\n`; |
| 57 | +// contents = contents.replace(full, pkgSeg + newImports + classSeg); |
| 58 | +// console.log('[nativeExceptionHandler] Added CrashTesterPackage import'); |
| 59 | +// } |
| 60 | +// } else { |
| 61 | +// contents = contents.replace(/(package [^\n]+\n)/, `$1import ${pkg}.CrashTesterPackage\n`); |
| 62 | +// console.log('[nativeExceptionHandler] Fallback import insertion'); |
| 63 | +// } |
| 64 | +// } |
| 65 | + |
| 66 | +// if (!contents.includes('CrashTesterPackage()')) { |
| 67 | +// const javaListRegex = /new PackageList\(this\)\.getPackages\(\);/; |
| 68 | +// if (javaListRegex.test(contents) && !contents.includes('new CrashTesterPackage()')) { |
| 69 | +// contents = contents.replace(javaListRegex, 'new PackageList(this).getPackages();\n packages.add(new CrashTesterPackage());'); |
| 70 | +// console.log('[nativeExceptionHandler] Injected into Java PackageList'); |
| 71 | +// } |
| 72 | +// const kotlinListRegex = /(val\s+packages\s*=\s*PackageList\(this\)\.packages)/; |
| 73 | +// if (kotlinListRegex.test(contents) && !/packages\.add\(CrashTesterPackage\(\)\)/.test(contents)) { |
| 74 | +// contents = contents.replace(kotlinListRegex, '$1\n packages.add(CrashTesterPackage())'); |
| 75 | +// console.log('[nativeExceptionHandler] Injected into Kotlin packages list'); |
| 76 | +// } |
| 77 | +// const manualArrayRegex = /(List<ReactPackage> packages = new ArrayList<ReactPackage>\(\);[\s\S]*?return packages;)/; |
| 78 | +// if (manualArrayRegex.test(contents) && !contents.includes('new CrashTesterPackage()')) { |
| 79 | +// contents = contents.replace(manualArrayRegex, (block) => block.replace('return packages;', 'packages.add(new CrashTesterPackage());\n return packages;')); |
| 80 | +// console.log('[nativeExceptionHandler] Injected into manual array pattern'); |
| 81 | +// } |
| 82 | +// } |
| 83 | + |
| 84 | +// if (contents.includes('CrashTesterPackage()')) { |
| 85 | +// console.log('[nativeExceptionHandler] CrashTesterPackage registration confirmed'); |
| 86 | +// } else { |
| 87 | +// console.warn('[nativeExceptionHandler] Could not confirm CrashTesterPackage registration'); |
| 88 | +// } |
| 89 | + |
| 90 | +// cfg.modResults.contents = contents; |
| 91 | +// return cfg; |
| 92 | +// }); |
| 93 | + |
| 94 | +// return config; |
| 95 | +// }; |
| 96 | + |
0 commit comments