const { withDangerousMod, withGradleProperties } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); /** * 在 gradle.properties 的 modResults 数组中设置某个 key(存在则更新,不存在则追加)。 * * 必须用 withGradleProperties(走 expo 内存 mod 流程)而非 withDangerousMod(直接改文件), * 否则会被 expo 的 gradleProperties base mod 在 write 阶段用内存快照全量覆盖。 * 典型症状:reactNativeArchitectures 改了不生效(被覆盖回全架构), * 而 minify/shrink 等新 key 反而生效(不在 expo 内存快照里,未被覆盖)。 */ function setGradleProperty(modResults, key, value) { let found = false; const updated = modResults.map((prop) => { if (prop.type === 'property' && prop.key === key) { found = true; return { ...prop, value }; } return prop; }); if (!found) { updated.push({ type: 'property', key, value }); } return updated; } function withSizeOptimization(config) { // 1. 修改 gradle.properties(用 withGradleProperties 避免 base mod write 覆盖) // - 限制打包架构为 arm64-v8a(加速构建、减小包体) // - 开启 R8 混淆与资源裁剪 // - 开启 .so 高倍率压缩 config = withGradleProperties(config, (config) => { let props = config.modResults; props = setGradleProperty(props, 'reactNativeArchitectures', 'arm64-v8a'); props = setGradleProperty(props, 'android.enableMinifyInReleaseBuilds', 'true'); props = setGradleProperty(props, 'android.enableShrinkResourcesInReleaseBuilds', 'true'); props = setGradleProperty(props, 'expo.useLegacyPackaging', 'true'); config.modResults = props; return config; }); // 2. 在 prebuild 时修改 app/build.gradle 启用 ABI Splits 分包 + 字体裁剪 config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const buildGradlePath = path.join(projectRoot, 'app/build.gradle'); if (fs.existsSync(buildGradlePath)) { let content = fs.readFileSync(buildGradlePath, 'utf8'); // ABI Splits 分包配置 if (!content.includes('splits {')) { content = content.replace( /android\s*\{/, `android {\n splits {\n abi {\n enable true\n reset()\n include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"\n universalApk true\n }\n }` ); } // 3. 裁剪未使用的 @expo/vector-icons 矢量字体 if (!content.includes('variant.mergeAssetsProvider.configure')) { content += ` android.applicationVariants.all { variant -> if (variant.buildType.name == "release") { variant.mergeAssetsProvider.configure { doLast { delete fileTree(dir: outputDir, includes: [ "**/fonts/AntDesign.ttf", "**/fonts/Entypo.ttf", "**/fonts/EvilIcons.ttf", "**/fonts/Feather.ttf", "**/fonts/FontAwesome*.ttf", "**/fonts/Fontisto.ttf", "**/fonts/Foundation.ttf", "**/fonts/MaterialCommunityIcons.ttf", "**/fonts/MaterialIcons.ttf", "**/fonts/Octicons.ttf", "**/fonts/SimpleLineIcons.ttf", "**/fonts/Zocial.ttf" ]) } } } } `; } fs.writeFileSync(buildGradlePath, content, 'utf8'); } return modConfig; } ]); // 3. 在 prebuild 时修改 proguard-rules.pro 保护原生插件与 ONNX 模块 config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const proguardPath = path.join(projectRoot, 'app/proguard-rules.pro'); if (fs.existsSync(proguardPath)) { let content = fs.readFileSync(proguardPath, 'utf8'); if (!content.includes('com.beancount.mobile')) { content += ` # Size optimization: keep custom native packages & ONNX -keep class com.beancount.mobile.** { *; } -keep class com.example.driftledger.** { *; } -keep class ai.onnxruntime.** { *; } `; fs.writeFileSync(proguardPath, content, 'utf8'); } } return modConfig; } ]); // 4. 在 prebuild 时修改 root build.gradle 强制指定所有模块的 NDK 版本以匹配 27.1.12297006 config = withDangerousMod(config, [ 'android', async (modConfig) => { const projectRoot = modConfig.modRequest.platformProjectRoot; const rootBuildGradlePath = path.join(projectRoot, 'build.gradle'); if (fs.existsSync(rootBuildGradlePath)) { let content = fs.readFileSync(rootBuildGradlePath, 'utf8'); if (!content.includes('ndkVersion = "27.1.12297006"')) { content += ` subprojects { project -> def configureProject = { proj -> if (proj.hasProperty("android")) { proj.android { ndkVersion = "27.1.12297006" } } } if (project.state.executed) { configureProject(project) } else { project.afterEvaluate { configureProject(project) } } } `; fs.writeFileSync(rootBuildGradlePath, content, 'utf8'); } } return modConfig; } ]); return config; } module.exports = withSizeOptimization;