Skip to content

Instantly share code, notes, and snippets.

@xixilive
Last active January 3, 2024 16:18
Show Gist options
  • Save xixilive/204e8a1bae02d6edc0f0d6337d8295c2 to your computer and use it in GitHub Desktop.
Save xixilive/204e8a1bae02d6edc0f0d6337d8295c2 to your computer and use it in GitHub Desktop.
a expo config plugin for native-wechat
import fs from 'node:fs'
import path from 'node:path'
import { ExpoConfig } from '@expo/config-types'
import {
ConfigPlugin, createRunOncePlugin, IOSConfig, AndroidConfig,
withInfoPlist, withEntitlementsPlist, withXcodeProject,
withMainApplication, withAndroidManifest, AndroidManifest
} from 'expo/config-plugins'
export type NativeWechatPluginOptions = {
appid: string
}
type ManifestIntent = {
action: {
$: {
'android:name': string
}
}
data: {
$: {
'android:scheme': string
}
}
}
type ManifestWithQueries = AndroidManifest['manifest'] & {
queries?: Array<{
package?: {
$: {
"android:name": string
}
},
intend?: ManifestIntent[]
}>
}
const androidTemplates = {
WXEntryActivity: `
package <PACKAGE_NAME>.wxapi;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class WXEntryActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
Intent intent = getIntent();
Intent intentToBroadcast = new Intent();
intentToBroadcast.setAction("com.hector.nativewechat.ACTION_REDIRECT_INTENT");
intentToBroadcast.putExtra("intent", intent);
sendBroadcast(intentToBroadcast);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
}
`,
WXPayEntryActivity: `
package <PACKAGE_NAME>.wxapi;
import android.app.Activity;
import android.os.Bundle;
public class WXPayEntryActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
finish();
}
}
`
}
const updateAppDelegateHeaderFile = (hFile: string) => {
let lines = fs.readFileSync(hFile, 'utf-8').split('\n')
const findInterfaceIndex = () => lines.findIndex(line => /^@interface\s+AppDelegate\s*:\s*EXAppDelegateWrapper/.test(line))
let ifIndex = findInterfaceIndex()
if(ifIndex < 0) {
throw new Error('syntax error in AppDelegate.h')
}
const importIndex = lines.findIndex(line => /^#import\s+"WXApi.h"/.test(line))
if(importIndex < 0) {
lines = lines.slice(0, ifIndex).concat(['', '#import "WXApi.h"', '']).concat(lines.slice(ifIndex))
ifIndex = findInterfaceIndex()
const m = lines[ifIndex].match(/EXAppDelegateWrapper(<(.+)>)?/)
if(!m![1]) {
lines[ifIndex] = lines[ifIndex].replace('EXAppDelegateWrapper', 'EXAppDelegateWrapper<WXApiDelegate>')
} else {
const delegates = m![2].split(/,+/)
if(!delegates.includes('WXApiDelegate')) {
delegates.push('WXApiDelegate')
}
lines[ifIndex] = lines[ifIndex].replace('EXAppDelegateWrapper', `EXAppDelegateWrapper<${delegates.join(', ')}>`)
}
}
fs.writeFileSync(hFile, lines.join('\n'), 'utf-8')
}
const createWxApiActivityFiles = (baseDir: string, options: any = {}) => {
const destDir = path.resolve(baseDir, 'wxapi')
fs.mkdirSync(destDir, {recursive: true})
fs.writeFileSync(path.resolve(destDir, 'WXEntryActivity.java'), androidTemplates.WXEntryActivity.replace('<PACKAGE_NAME>', options.package), 'utf-8')
fs.writeFileSync(path.resolve(destDir, 'WXPayEntryActivity.java'), androidTemplates.WXPayEntryActivity.replace('<PACKAGE_NAME>', options.package), 'utf-8')
}
const withIOSConfig = (config: ExpoConfig, options: NativeWechatPluginOptions) => {
if(!options?.appid) {
console.warn('wechat appid is missing from plugin options')
}
withXcodeProject(config, mod => {
updateAppDelegateHeaderFile(IOSConfig.Paths.getAppDelegateHeaderFilePath(mod.modRequest.projectRoot))
return mod
})
withEntitlementsPlist(config, (mod) => {
// associated domains
if(!Array.isArray(mod.modResults['com.apple.developer.associated-domains'])) {
mod.modResults['com.apple.developer.associated-domains'] = []
}
const domains = mod.modResults['com.apple.developer.associated-domains']
const items = ['applinks:app.magiilife.com']
items.forEach(d => {
if(!domains.includes(d)) {
domains.push(d)
}
})
return mod
})
// add URL types and Queried URL schemas
withInfoPlist(config, (mod) => {
if(!Array.isArray(mod.modResults.CFBundleURLTypes)) {
mod.modResults.CFBundleURLTypes = []
}
if(!Array.isArray(mod.modResults.LSApplicationQueriesSchemes)) {
mod.modResults.LSApplicationQueriesSchemes = []
}
const schema = mod.modResults.CFBundleURLTypes.find(x => x.CFBundleURLName === 'weixin')
if(!schema) {
mod.modResults.CFBundleURLTypes.push({
CFBundleURLName: 'weixin', CFBundleURLSchemes: [options.appid]
})
}
const queriedUrlSchemas = ['weixin', 'wechat', 'weixinULAPI']
queriedUrlSchemas.forEach(s => {
if(!mod.modResults.LSApplicationQueriesSchemes!.includes(s)) {
mod.modResults.LSApplicationQueriesSchemes!.push(s)
}
})
return mod
})
}
const withAndroidConfig = (config: ExpoConfig, options: NativeWechatPluginOptions) => {
// append wxapi activity files
withMainApplication(config, mod => {
createWxApiActivityFiles(path.dirname(mod.modResults.path), {package: config.android!.package})
return mod
})
withAndroidManifest(config, mod => {
const manifest = mod.modResults.manifest as ManifestWithQueries
const queryPkg = {$: {'android:name': 'com.tencent.mm'}}
if(!Array.isArray(manifest.queries)) {
manifest.queries = []
}
if(manifest.queries.length === 0) {
manifest.queries.push({package: queryPkg})
} else {
manifest.queries[0].package = {$: {'android:name': 'com.tencent.mm'}}
}
const application = manifest.application!.find(x => x.$['android:name'] === '.MainApplication')!
// WXEntryActivity
if(!application.activity!.find(x => x.$['android:name'] === '.wxapi.WXEntryActivity')) {
application.activity!.push({$: {
'android:name': '.wxapi.WXEntryActivity',
'android:exported': 'true',
'android:label': '@string/app_name',
'android:launchMode': 'singleTask',
'android:taskAffinity': config.android!.package,
'android:theme': '@android:style/Theme.Translucent.NoTitleBar'
}})
}
// WXPayEntryActivity
if(!application.activity!.find(x => x.$['android:name'] === '.wxapi.WXPayEntryActivity')) {
application.activity!.push({$: {
'android:name': '.wxapi.WXPayEntryActivity',
'android:exported': 'true',
'android:label': '@string/app_name'
}})
}
return mod
})
}
const withNativeWechat: ConfigPlugin<NativeWechatPluginOptions> = (config, options) => {
withIOSConfig(config, options)
withAndroidConfig(config, options)
return config
}
export default createRunOncePlugin(withNativeWechat, 'native-wechat', '1.0.3')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment