Skip to content

Commit 93c1091

Browse files
committed
feat: set up and implement Expo plugin for code push
1 parent 932410b commit 93c1091

File tree

6 files changed

+242
-0
lines changed

6 files changed

+242
-0
lines changed

app.plugin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require("./expo/plugin/withCodePush");

expo/plugin/withCodePush.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { createRunOncePlugin } = require('expo/config-plugins');
2+
const { withAndroidBuildScriptDependency, withAndroidMainApplicationDependency } = require('./withCodePushAndroid');
3+
const { withIosBridgingHeader, withIosAppDelegateDependency } = require('./withCodePushIos');
4+
const pkg = require('../../package.json');
5+
6+
const withCodePush = (config) => {
7+
config = withAndroidBuildScriptDependency(config);
8+
config = withAndroidMainApplicationDependency(config);
9+
config = withIosBridgingHeader(config);
10+
config = withIosAppDelegateDependency(config);
11+
12+
return config;
13+
};
14+
15+
module.exports = createRunOncePlugin(withCodePush, pkg.name, pkg.version);

expo/plugin/withCodePushAndroid.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const { withAppBuildGradle, withMainApplication, WarningAggregator } = require('expo/config-plugins');
2+
3+
function androidApplyImplementation(appBuildGradle) {
4+
const codePushImplementation = `apply from: "../../node_modules/@bravemobile/react-native-code-push/android/codepush.gradle"`;
5+
6+
if (!appBuildGradle.includes(codePushImplementation)) {
7+
return `${appBuildGradle.trimEnd()}\n${codePushImplementation}\n`;
8+
}
9+
10+
return appBuildGradle;
11+
}
12+
13+
function androidMainApplicationApplyImplementation(
14+
mainApplication,
15+
find,
16+
add,
17+
reverse = false,
18+
) {
19+
if (mainApplication.includes(add)) {
20+
return mainApplication;
21+
}
22+
23+
if (mainApplication.includes(find)) {
24+
return mainApplication.replace(find, reverse ? `${add}\n${find}` : `${find}\n${add}`);
25+
}
26+
27+
WarningAggregator.addWarningAndroid(
28+
'withCodePushAndroid',
29+
`
30+
Failed to detect "${find.replace(/\n/g, '').trim()}" in the MainApplication.kt.
31+
Please add "${add.replace(/\n/g, '').trim()}" to the MainApplication.kt.
32+
Supported format: Expo SDK default template.
33+
34+
Android manual setup: https://github.com/Soomgo-Mobile/react-native-code-push#3-android-setup
35+
`,
36+
);
37+
38+
return mainApplication;
39+
}
40+
41+
const withAndroidBuildScriptDependency = (config) => {
42+
return withAppBuildGradle(config, (action) => {
43+
action.modResults.contents = androidApplyImplementation(
44+
action.modResults.contents,
45+
);
46+
47+
return action;
48+
});
49+
};
50+
51+
const withAndroidMainApplicationDependency = (config) => {
52+
return withMainApplication(config, (action) => {
53+
action.modResults.contents = androidMainApplicationApplyImplementation(
54+
action.modResults.contents,
55+
'class MainApplication : Application(), ReactApplication {',
56+
'import com.microsoft.codepush.react.CodePush\n',
57+
true,
58+
);
59+
60+
action.modResults.contents = androidMainApplicationApplyImplementation(
61+
action.modResults.contents,
62+
'object : DefaultReactNativeHost(this) {',
63+
' override fun getJSBundleFile(): String = CodePush.getJSBundleFile()\n',
64+
);
65+
return action;
66+
});
67+
};
68+
69+
module.exports = {
70+
withAndroidBuildScriptDependency,
71+
withAndroidMainApplicationDependency,
72+
};

expo/plugin/withCodePushIos.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
const { withAppDelegate, withXcodeProject, WarningAggregator } = require('expo/config-plugins');
2+
const { getAppDelegate } = require('@expo/config-plugins/build/ios/Paths');
3+
const path = require('path');
4+
const fs = require('fs');
5+
6+
function iosApplyImplementation(
7+
appDelegate,
8+
find,
9+
add,
10+
replace,
11+
) {
12+
if (appDelegate.includes(add)) {
13+
return appDelegate;
14+
}
15+
16+
if (appDelegate.includes(find)) {
17+
return appDelegate.replace(find, replace ? add : `${find}\n${add}`);
18+
}
19+
20+
WarningAggregator.addWarningIOS(
21+
'withCodePushIos',
22+
`
23+
Failed to detect "${find.replace(/\n/g, '').trim()}" in the AppDelegate.(m|swift).
24+
Please ${replace ? 'replace' : 'add'} "${add.replace(/\n/g, '').trim()}" to the AppDelegate.(m|swift).
25+
Supported format: Expo SDK default template.
26+
27+
iOS manual setup: https://github.com/Soomgo-Mobile/react-native-code-push#2-ios-setup
28+
`,
29+
);
30+
31+
return appDelegate;
32+
}
33+
34+
function getBridgingHeaderPathFromXcode(project) {
35+
const buildConfigs = project.pbxXCBuildConfigurationSection();
36+
37+
for (const key in buildConfigs) {
38+
const config = buildConfigs[key];
39+
if (
40+
typeof config === 'object' &&
41+
config.buildSettings &&
42+
config.buildSettings['SWIFT_OBJC_BRIDGING_HEADER']
43+
) {
44+
const bridgingHeaderPath = config.buildSettings[
45+
'SWIFT_OBJC_BRIDGING_HEADER'
46+
].replace(/"/g, '');
47+
48+
return bridgingHeaderPath;
49+
}
50+
}
51+
return null;
52+
}
53+
54+
const withIosAppDelegateDependency = (config) => {
55+
return withAppDelegate(config, (action) => {
56+
const language = action.modResults.language;
57+
58+
if (['objc', 'objcpp'].includes(language)) {
59+
action.modResults.contents = iosApplyImplementation(
60+
action.modResults.contents,
61+
`#import "AppDelegate.h"`,
62+
`#import <CodePush/CodePush.h>`,
63+
);
64+
action.modResults.contents = iosApplyImplementation(
65+
action.modResults.contents,
66+
`return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];`,
67+
`return [CodePush bundleURL];`,
68+
true,
69+
);
70+
71+
return action;
72+
}
73+
74+
if (language === 'swift') {
75+
action.modResults.contents = iosApplyImplementation(
76+
action.modResults.contents,
77+
`return Bundle.main.url(forResource: "main", withExtension: "jsbundle")`,
78+
`return CodePush.bundleURL()`,
79+
true,
80+
);
81+
82+
return action;
83+
}
84+
85+
WarningAggregator.addWarningIOS(
86+
'withIosAppDelegate',
87+
`${language} AppDelegate file is not supported yet.`,
88+
);
89+
90+
return action;
91+
});
92+
};
93+
94+
const withIosBridgingHeader = (config) => {
95+
return withXcodeProject(config, (action) => {
96+
const projectRoot = action.modRequest.projectRoot;
97+
const appDelegate = getAppDelegate(projectRoot);
98+
99+
if (appDelegate.language === 'swift') {
100+
const bridgingHeaderPath = getBridgingHeaderPathFromXcode(
101+
action.modResults,
102+
);
103+
104+
const bridgingHeaderFilePath = path.join(
105+
action.modRequest.platformProjectRoot,
106+
bridgingHeaderPath,
107+
);
108+
109+
if (fs.existsSync(bridgingHeaderFilePath)) {
110+
let content = fs.readFileSync(bridgingHeaderFilePath, 'utf8');
111+
const codePushImport = '#import <CodePush/CodePush.h>';
112+
113+
if (!content.includes(codePushImport)) {
114+
content += `${codePushImport}\n`;
115+
fs.writeFileSync(bridgingHeaderFilePath, content);
116+
}
117+
118+
return action;
119+
}
120+
121+
WarningAggregator.addWarningIOS(
122+
'withIosBridgingHeader',
123+
`
124+
Failed to detect ${bridgingHeaderFilePath} file.
125+
Please add CodePush integration manually:
126+
#import <CodePush/CodePush.h>
127+
128+
Supported format: Expo SDK default template.
129+
`
130+
);
131+
132+
return action;
133+
}
134+
135+
return action;
136+
});
137+
};
138+
139+
module.exports = {
140+
withIosAppDelegateDependency,
141+
withIosBridgingHeader,
142+
};

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,14 @@
4848
"yazl": "^3.3.1"
4949
},
5050
"peerDependencies": {
51+
"expo": ">=50.0.0",
5152
"react-native": "*"
5253
},
54+
"peerDependenciesMeta": {
55+
"expo": {
56+
"optional": true
57+
}
58+
},
5359
"devDependencies": {
5460
"@babel/core": "^7.26.0",
5561
"@babel/preset-env": "^7.26.0",

0 commit comments

Comments
 (0)