const { withAndroidManifest, withDangerousMod, withAppDelegate, withInfoPlist, withAppBuildGradle, } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); const withJPush = (config, options = {}) => { const { appKey, channel = 'developer-default' } = options; if (!appKey) { throw new Error('JPush appKey is required in plugin options'); } // 1. Android: Inject manifestPlaceholders into app/build.gradle config = withAppBuildGradle(config, (config) => { const contents = config.modResults.contents; const requiredPlaceholders = { JPUSH_APPKEY: appKey, JPUSH_CHANNEL: channel, }; const manifestPlaceholderRegex = /manifestPlaceholders\s*=\s*\[([\s\S]*?)\]/; const match = contents.match(manifestPlaceholderRegex); 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]; } for (const [key, value] of Object.entries(requiredPlaceholders)) { existingEntries[key] = value; } const entriesStr = Object.entries(existingEntries) .map(([k, v]) => `${k}: "${v}"`) .join(',\n '); const newBlock = `\n manifestPlaceholders = [\n ${entriesStr}\n ]`; config.modResults.contents = contents.replace(manifestPlaceholderRegex, newBlock.trim()); } else if (contents.includes('REACT_NATIVE_RELEASE_LEVEL')) { const entriesStr = Object.entries(requiredPlaceholders) .map(([k, v]) => `${k}: "${v}"`) .join(',\n '); const block = '\n manifestPlaceholders = [\n ' + entriesStr + '\n ]'; const lines = contents.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; } } config.modResults.contents = lines.join('\n'); } return config; }); // 2. Android: Add JPush metadata and permissions to AndroidManifest config = withAndroidManifest(config, (config) => { const manifest = config.modResults; if (!manifest.manifest['uses-permission']) { manifest.manifest['uses-permission'] = []; } const requiredPermissions = [ 'android.permission.RECEIVE_USER_PRESENT', 'android.permission.POST_NOTIFICATIONS', ]; requiredPermissions.forEach((permission) => { const exists = manifest.manifest['uses-permission'].find( (p) => p.$['android:name'] === permission ); if (!exists) { manifest.manifest['uses-permission'].push({ $: { 'android:name': permission }, }); } }); return config; }); // 3. iOS: Add JPush initialization to AppDelegate config = withAppDelegate(config, (config) => { const { contents } = config.modResults; if (!contents.includes('JPush')) { let newContents = contents; if (!newContents.includes('#import ')) { newContents = newContents.replace( /(#import "AppDelegate.h")/, `$1\n#import ` ); } if (!newContents.includes('#import ')) { newContents = newContents.replace( /(#import "AppDelegate.h")/, `$1\n#import ` ); } const didFinishLaunchPattern = /(- \(BOOL\)application:\(UIApplication \*\)application didFinishLaunchingWithOptions:\(NSDictionary \*\)launchOptions\s*\{)/; if (didFinishLaunchPattern.test(newContents)) { newContents = newContents.replace( didFinishLaunchPattern, `$1\n // JPush initialization\n [JCoreModule setupWithOption:launchOptions appKey:@"${appKey}" channel:@"${channel}" apsForProduction:NO];\n` ); } const jpushMethods = ` // JPush: Register for remote notifications - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [JCoreModule registerDeviceToken:deviceToken]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"did fail to register for remote notifications with error: %@", error); } // JPush: Handle remote notification - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [JCoreModule didReceiveRemoteNotification:userInfo]; completionHandler(UIBackgroundFetchResultNewData); } // UNUserNotificationCenter delegate - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound); } - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { [JCoreModule didReceiveRemoteNotification:response.notification.request.content.userInfo]; completionHandler(); } `; if (!newContents.includes('JCoreModule registerDeviceToken')) { newContents = newContents.replace( /@end\s*$/, `${jpushMethods}\n@end\n` ); } config.modResults.contents = newContents; } return config; }); // 4. iOS: Add required capabilities and permissions to Info.plist config = withInfoPlist(config, (config) => { config.modResults.UIBackgroundModes = config.modResults.UIBackgroundModes || []; if (!config.modResults.UIBackgroundModes.includes('remote-notification')) { config.modResults.UIBackgroundModes.push('remote-notification'); } return config; }); return config; }; module.exports = withJPush;