2026-06-18 13:53:36 +08:00
|
|
|
|
const { withDangerousMod } = require('@expo/config-plugins');
|
2026-06-18 00:03:37 +08:00
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
const path = require('path');
|
|
|
|
|
|
|
|
|
|
|
|
// 荣耀厂商推送通道的 Expo config plugin.
|
|
|
|
|
|
//
|
|
|
|
|
|
// 客户端职责(依据极光官方文档 https://docs.jiguang.cn/jpush/client/Android/android_3rd_guide):
|
|
|
|
|
|
// 1) 添加 cn.jiguang.sdk.plugin:honor 依赖
|
|
|
|
|
|
// 2) 注入 manifestPlaceholders: HONOR_APPID
|
2026-06-18 13:22:15 +08:00
|
|
|
|
// 3) 添加荣耀 Maven 仓库 https://developer.hihonor.com/repo 到 settings.gradle 和根 build.gradle
|
2026-06-18 13:53:36 +08:00
|
|
|
|
// 关键: 必须同时加到 settings.gradle 的 dependencyResolutionManagement 块(CI build 走这条路径),
|
|
|
|
|
|
// 和根 build.gradle 的 buildscript+allprojects(本地 build 兜底)。
|
2026-06-18 00:03:37 +08:00
|
|
|
|
// 4) 添加 Proguard 规则 (com.hihonor.push.** 保留 + 5 条 -keepattributes)
|
|
|
|
|
|
//
|
2026-06-18 13:53:36 +08:00
|
|
|
|
// 实现说明:所有文件改动统一用 withDangerousMod 直接读写磁盘。
|
|
|
|
|
|
// 原因:Expo SDK 56+ 的 withSettingsGradle / withProjectBuildGradle / withAppBuildGradle hook 链中,
|
|
|
|
|
|
// 第二个以后的 plugin 写回 modResults.contents 会被静默忽略。withDangerousMod 走文件系统串行执行,
|
|
|
|
|
|
// 多 vendor plugin 共存可靠。
|
2026-06-18 00:03:37 +08:00
|
|
|
|
const withHonorPush = (config, options = {}) => {
|
|
|
|
|
|
const {
|
2026-06-18 12:46:34 +08:00
|
|
|
|
jpushVersion = '6.1.0',
|
2026-06-18 00:03:37 +08:00
|
|
|
|
appId = '104562917',
|
|
|
|
|
|
} = options;
|
|
|
|
|
|
|
|
|
|
|
|
const honorRepoUrl = 'https://developer.hihonor.com/repo';
|
|
|
|
|
|
|
|
|
|
|
|
config = withDangerousMod(config, [
|
|
|
|
|
|
'android',
|
|
|
|
|
|
async (config) => {
|
2026-06-18 13:53:36 +08:00
|
|
|
|
const settingsGradlePath = path.join(
|
|
|
|
|
|
config.modRequest.platformProjectRoot,
|
|
|
|
|
|
'settings.gradle'
|
|
|
|
|
|
);
|
2026-06-18 13:22:15 +08:00
|
|
|
|
const rootBuildGradlePath = path.join(
|
|
|
|
|
|
config.modRequest.platformProjectRoot,
|
|
|
|
|
|
'build.gradle'
|
|
|
|
|
|
);
|
2026-06-18 00:03:37 +08:00
|
|
|
|
const appBuildGradlePath = path.join(
|
|
|
|
|
|
config.modRequest.platformProjectRoot,
|
|
|
|
|
|
'app',
|
|
|
|
|
|
'build.gradle'
|
|
|
|
|
|
);
|
|
|
|
|
|
const proguardFilePath = path.join(
|
|
|
|
|
|
config.modRequest.platformProjectRoot,
|
|
|
|
|
|
'app',
|
|
|
|
|
|
'proguard-rules.pro'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-06-18 13:53:36 +08:00
|
|
|
|
// --- 1. settings.gradle: pluginManagement + dependencyResolutionManagement ---
|
|
|
|
|
|
if (fs.existsSync(settingsGradlePath)) {
|
|
|
|
|
|
let settings = fs.readFileSync(settingsGradlePath, 'utf-8');
|
|
|
|
|
|
let settingsChanged = false;
|
|
|
|
|
|
const repoLine = ` maven { url '${honorRepoUrl}' }`;
|
|
|
|
|
|
|
|
|
|
|
|
// pluginManagement.repositories
|
|
|
|
|
|
if (!settings.match(/pluginManagement\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) {
|
|
|
|
|
|
const hasRepositoriesInPM = /pluginManagement\s*\{[\s\S]*?repositories\s*\{/.test(settings);
|
|
|
|
|
|
if (hasRepositoriesInPM) {
|
|
|
|
|
|
settings = settings.replace(
|
|
|
|
|
|
/(pluginManagement\s*\{[\s\S]*?repositories\s*\{)/,
|
|
|
|
|
|
`$1\n ${repoLine}`
|
|
|
|
|
|
);
|
|
|
|
|
|
settingsChanged = true;
|
|
|
|
|
|
} else if (settings.includes('pluginManagement')) {
|
|
|
|
|
|
settings = settings.replace(
|
|
|
|
|
|
/(pluginManagement\s*\{)/,
|
|
|
|
|
|
`$1\n repositories {\n gradlePluginPortal()\n google()\n mavenCentral()\n ${repoLine}\n }`
|
|
|
|
|
|
);
|
|
|
|
|
|
settingsChanged = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
settings =
|
|
|
|
|
|
`pluginManagement {\n repositories {\n ${repoLine}\n }\n}\n\n` + settings;
|
|
|
|
|
|
settingsChanged = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// dependencyResolutionManagement (Gradle 7+, takes precedence over allprojects in CI)
|
|
|
|
|
|
if (!settings.match(/dependencyResolutionManagement[\s\S]*developer\.hihonor\.com\/repo/)) {
|
|
|
|
|
|
const depMgmtPattern = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/;
|
|
|
|
|
|
if (depMgmtPattern.test(settings)) {
|
|
|
|
|
|
settings = settings.replace(
|
|
|
|
|
|
depMgmtPattern,
|
|
|
|
|
|
`$&\n maven { url '${honorRepoUrl}' }`
|
|
|
|
|
|
);
|
|
|
|
|
|
settingsChanged = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Block doesn't exist — fallback: create with Huawei + Honor + standard repos.
|
|
|
|
|
|
const block = `\ndependencyResolutionManagement {\n repositories {\n maven { url 'https://developer.huawei.com/repo/' }\n maven { url '${honorRepoUrl}' }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n`;
|
|
|
|
|
|
const pluginsBlockEnd = /(\nplugins\s*\{[\s\S]*?\n\})/;
|
|
|
|
|
|
if (pluginsBlockEnd.test(settings)) {
|
|
|
|
|
|
settings = settings.replace(pluginsBlockEnd, `$1\n${block}`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
settings = settings + block;
|
|
|
|
|
|
}
|
|
|
|
|
|
settingsChanged = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (settingsChanged) {
|
|
|
|
|
|
fs.writeFileSync(settingsGradlePath, settings);
|
|
|
|
|
|
console.log('[withHonorPush] updated settings.gradle (pluginManagement + dependencyResolutionManagement)');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 2. 根 build.gradle: buildscript + allprojects repositories ---
|
2026-06-18 13:22:15 +08:00
|
|
|
|
if (fs.existsSync(rootBuildGradlePath)) {
|
|
|
|
|
|
let gradle = fs.readFileSync(rootBuildGradlePath, 'utf-8');
|
|
|
|
|
|
let changed = false;
|
|
|
|
|
|
const repoLine = ` maven { url '${honorRepoUrl}' }`;
|
|
|
|
|
|
|
|
|
|
|
|
if (!gradle.match(/buildscript\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) {
|
|
|
|
|
|
const bsRepoPattern = /(buildscript\s*\{[\s\S]*?repositories\s*\{)/;
|
|
|
|
|
|
if (bsRepoPattern.test(gradle)) {
|
|
|
|
|
|
gradle = gradle.replace(bsRepoPattern, `$1\n${repoLine}`);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!gradle.match(/allprojects\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) {
|
|
|
|
|
|
const allProjectsPattern = /(allprojects\s*\{[\s\S]*?repositories\s*\{)/;
|
|
|
|
|
|
if (allProjectsPattern.test(gradle)) {
|
|
|
|
|
|
gradle = gradle.replace(allProjectsPattern, `$1\n${repoLine}`);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const allProjectsBlock = `\nallprojects {\n repositories {${repoLine}\n }\n}\n`;
|
|
|
|
|
|
gradle = gradle + allProjectsBlock;
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
|
fs.writeFileSync(rootBuildGradlePath, gradle);
|
|
|
|
|
|
console.log('[withHonorPush] updated root build.gradle (buildscript + allprojects repos)');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 13:53:36 +08:00
|
|
|
|
// --- 3. app/build.gradle: AAR 依赖 + manifestPlaceholders ---
|
2026-06-18 00:03:37 +08:00
|
|
|
|
if (fs.existsSync(appBuildGradlePath)) {
|
|
|
|
|
|
let gradle = fs.readFileSync(appBuildGradlePath, 'utf-8');
|
|
|
|
|
|
let changed = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!gradle.includes('cn.jiguang.sdk.plugin:honor')) {
|
|
|
|
|
|
const depsPattern = /dependencies\s*\{/;
|
|
|
|
|
|
if (depsPattern.test(gradle)) {
|
|
|
|
|
|
gradle = gradle.replace(
|
|
|
|
|
|
depsPattern,
|
|
|
|
|
|
`dependencies {\n implementation 'cn.jiguang.sdk.plugin:honor:${jpushVersion}'`
|
|
|
|
|
|
);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const manifestPlaceholderRegex = /manifestPlaceholders\s*=\s*\[([\s\S]*?)\]/;
|
|
|
|
|
|
const match = gradle.match(manifestPlaceholderRegex);
|
|
|
|
|
|
|
|
|
|
|
|
const requiredPlaceholders = {
|
|
|
|
|
|
HONOR_APPID: appId,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (match) {
|
|
|
|
|
|
const existingBlock = match[1];
|
|
|
|
|
|
const existingEntries = {};
|
|
|
|
|
|
const entryRegex = /(\w+)\s*:\s*"([^"]*)"/g;
|
|
|
|
|
|
let entryMatch;
|
|
|
|
|
|
while ((entryMatch = entryRegex.exec(existingBlock)) !== null) {
|
|
|
|
|
|
existingEntries[entryMatch[1]] = entryMatch[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let placeholdersChanged = false;
|
|
|
|
|
|
for (const [key, value] of Object.entries(requiredPlaceholders)) {
|
|
|
|
|
|
if (existingEntries[key] !== value) {
|
|
|
|
|
|
existingEntries[key] = value;
|
|
|
|
|
|
placeholdersChanged = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (placeholdersChanged) {
|
|
|
|
|
|
const entriesStr = Object.entries(existingEntries)
|
|
|
|
|
|
.map(([k, v]) => `${k}: "${v}"`)
|
|
|
|
|
|
.join(',\n ');
|
|
|
|
|
|
const newBlock = `\n manifestPlaceholders = [\n ${entriesStr}\n ]`;
|
|
|
|
|
|
gradle = gradle.replace(manifestPlaceholderRegex, newBlock.trim());
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (gradle.includes('REACT_NATIVE_RELEASE_LEVEL')) {
|
|
|
|
|
|
const entriesStr = Object.entries(requiredPlaceholders)
|
|
|
|
|
|
.map(([k, v]) => `${k}: "${v}"`)
|
|
|
|
|
|
.join(',\n ');
|
|
|
|
|
|
const block = `\n manifestPlaceholders = [\n ${entriesStr}\n ]\n`;
|
|
|
|
|
|
const lines = gradle.split('\n');
|
|
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
|
|
|
if (lines[i].includes('REACT_NATIVE_RELEASE_LEVEL')) {
|
|
|
|
|
|
lines.splice(i + 1, 0, block);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
gradle = lines.join('\n');
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
|
fs.writeFileSync(appBuildGradlePath, gradle);
|
|
|
|
|
|
console.log('[withHonorPush] updated app/build.gradle (AAR + manifestPlaceholders)');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 13:53:36 +08:00
|
|
|
|
// --- 4. proguard-rules.pro ---
|
2026-06-18 00:03:37 +08:00
|
|
|
|
if (fs.existsSync(proguardFilePath)) {
|
|
|
|
|
|
let proguard = fs.readFileSync(proguardFilePath, 'utf-8');
|
|
|
|
|
|
if (!proguard.includes('-keep class com.hihonor.push.**')) {
|
|
|
|
|
|
const honorRules = `
|
|
|
|
|
|
# JPush Honor vendor channel — keep rules (https://docs.jiguang.cn/jpush/client/Android/android_3rd_guide)
|
|
|
|
|
|
-ignorewarnings
|
|
|
|
|
|
-keepattributes *Annotation*
|
|
|
|
|
|
-keepattributes Exceptions
|
|
|
|
|
|
-keepattributes InnerClasses
|
|
|
|
|
|
-keepattributes Signature
|
|
|
|
|
|
-keepattributes SourceFile,LineNumberTable
|
|
|
|
|
|
-keep class com.hihonor.push.**{*;}
|
|
|
|
|
|
`;
|
|
|
|
|
|
fs.writeFileSync(proguardFilePath, proguard + honorRules);
|
|
|
|
|
|
console.log('[withHonorPush] appended Honor Proguard rules to proguard-rules.pro');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = withHonorPush;
|