const { withDangerousMod } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); const MAX_JOBS = '8'; // React Native's hermes-engine build hardcodes // Runtime.getRuntime().availableProcessors() // for cmake --build -j, ignoring all env vars. // On high-core CI runners this exhausts memory. // This plugin patches hermes-engine/build.gradle.kts // during prebuild to cap parallel jobs. const withCmakeJobLimit = (config) => withDangerousMod(config, [ 'android', async (config) => { const root = config.modRequest.projectRoot; // 1. Patch hermes-engine: availableProcessors() -> "8" const hermesPath = path.join( root, 'node_modules', 'react-native', 'ReactAndroid', 'hermes-engine', 'build.gradle.kts', ); if (fs.existsSync(hermesPath)) { let content = fs.readFileSync(hermesPath, 'utf-8'); content = content.replace( /Runtime\.getRuntime\(\)\.availableProcessors\(\)\.toString\(\)/, `"${MAX_JOBS}"`, ); fs.writeFileSync(hermesPath, content); } // 2. Patch app/build.gradle: add cmake -j argument to externalNativeBuild const appGradlePath = path.join( config.modRequest.platformProjectRoot, 'app', 'build.gradle', ); if (fs.existsSync(appGradlePath)) { let content = fs.readFileSync(appGradlePath, 'utf-8'); // Insert externalNativeBuild cmake arguments block inside the android { } block // We add it right after "ndkVersion rootProject.ext.ndkVersion" if (!content.includes('CMAKE_BUILD_PARALLEL_LEVEL')) { content = content.replace( /(\s*ndkVersion rootProject\.ext\.ndkVersion)/, `$1\n externalNativeBuild {\n cmake {\n arguments "-j${MAX_JOBS}"\n }\n }`, ); } fs.writeFileSync(appGradlePath, content); } return config; }, ]); module.exports = withCmakeJobLimit;