Skip to content

Commit 849b72c

Browse files
authored
fix(CLI): update init command and Expo plugin to support RN 0.82 MainApplication.kt (#99)
* fix(CLI): update init command to support RN 0.82 MainApplication.kt * docs: update README.md with RN 0.82+ manual instructions for MainApplication.kt * fix(CLI): update Expo plugin to support RN 0.82 MainApplication.kt
1 parent 9ffd640 commit 849b72c

File tree

4 files changed

+164
-19
lines changed

4 files changed

+164
-19
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,28 @@ Then, edit `AppDelegate.swift` like below.
146146

147147
#### Edit `MainApplication` Code
148148

149-
**If you have `MainApplication.kt` (>= RN 0.73)**
149+
**(RN 0.82+) If you have `MainApplication.kt`**
150+
151+
```diff
152+
+ import com.microsoft.codepush.react.CodePush
153+
154+
class MainApplication : Application(), ReactApplication {
155+
override val reactHost: ReactHost by lazy {
156+
getDefaultReactHost(
157+
context = applicationContext,
158+
packageList =
159+
PackageList(this).packages.apply {
160+
// Packages that cannot be autolinked yet can be added manually here, for example:
161+
// add(MyReactNativePackage())
162+
},
163+
+ jsBundleFilePath = CodePush.getJSBundleFile(),
164+
)
165+
}
166+
// ...
167+
}
168+
```
169+
170+
**(RN 0.73+) If you have `MainApplication.kt`**
150171

151172
```diff
152173
+ import com.microsoft.codepush.react.CodePush
@@ -163,6 +184,7 @@ Then, edit `AppDelegate.swift` like below.
163184
}
164185
```
165186

187+
166188
**Or if you have `MainApplication.java`**
167189

168190
```diff

cli/commands/initCommand/initAndroid.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,62 @@ import path from "path";
22
import fs from "fs";
33
import { EOL } from "os";
44

5+
const IMPORT_CODE_PUSH = 'import com.microsoft.codepush.react.CodePush';
6+
const RN_LEGACY_MARKER = 'object : DefaultReactNativeHost(this)';
7+
const RN_082_MARKER = ' getDefaultReactHost(';
8+
59
export function modifyMainApplicationKt(mainApplicationContent: string) {
6-
if (mainApplicationContent.includes('CodePush.getJSBundleFile()')) {
10+
const hasCodePushImport = mainApplicationContent.includes(IMPORT_CODE_PUSH);
11+
const hasGetJSBundleFileFn = mainApplicationContent.includes('CodePush.getJSBundleFile()');
12+
13+
if (hasCodePushImport && hasGetJSBundleFileFn) {
714
console.log('log: MainApplication.kt already has CodePush initialized.');
815
return mainApplicationContent;
916
}
10-
return mainApplicationContent
11-
.replace('import com.facebook.react.ReactApplication', `import com.facebook.react.ReactApplication${EOL}import com.microsoft.codepush.react.CodePush`)
12-
.replace('override fun getJSMainModuleName(): String = "index"', `override fun getJSMainModuleName(): String = "index"${EOL} override fun getJSBundleFile(): String = CodePush.getJSBundleFile()`)
17+
18+
let updatedContent = mainApplicationContent;
19+
20+
if (!hasCodePushImport) {
21+
updatedContent = mainApplicationContent.replace('import com.facebook.react.ReactApplication', `import com.facebook.react.ReactApplication${EOL}${IMPORT_CODE_PUSH}`);
22+
}
23+
24+
const isExtendingDefaultReactNativeHost = mainApplicationContent.includes(RN_LEGACY_MARKER);
25+
26+
// RN <0.82
27+
if (isExtendingDefaultReactNativeHost) {
28+
if (!hasGetJSBundleFileFn) {
29+
updatedContent = updatedContent.replace('override fun getJSMainModuleName(): String = "index"', `override fun getJSMainModuleName(): String = "index"${EOL} override fun getJSBundleFile(): String = CodePush.getJSBundleFile()`);
30+
}
31+
return updatedContent;
32+
}
33+
34+
// RN 0.82+
35+
const isUsingGetDefaultReactHost = mainApplicationContent.includes(RN_082_MARKER);
36+
if (isUsingGetDefaultReactHost) {
37+
if (!hasGetJSBundleFileFn) {
38+
return addJsBundleFilePathArgument(updatedContent);
39+
}
40+
return updatedContent;
41+
}
42+
43+
throw new Error('Unsupported MainApplication.kt structure.');
44+
}
45+
46+
function addJsBundleFilePathArgument(mainApplicationContent: string) {
47+
const packageListArgumentPattern = /(packageList\s*=\s*\n\s*PackageList\(this\)[\s\S]+?\},\s*\n)/;
48+
49+
if (!packageListArgumentPattern.test(mainApplicationContent)) {
50+
console.log('log: Could not find packageList argument while updating MainApplication.kt.');
51+
return mainApplicationContent;
52+
}
53+
54+
return mainApplicationContent.replace(packageListArgumentPattern, (match) => {
55+
if (match.includes('jsBundleFilePath')) {
56+
return match;
57+
}
58+
59+
return `${match} jsBundleFilePath = CodePush.getJSBundleFile(),${EOL}`;
60+
});
1361
}
1462

1563
export async function initAndroid() {

cli/commands/initCommand/test/initAndroid.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,55 @@ class MainApplication : Application(), ReactApplication {
4747
}
4848
`;
4949

50+
// https://github.com/react-native-community/template/blob/0.82.1/template/android/app/src/main/java/com/helloworld/MainApplication.kt
51+
const ktRN82Template = `
52+
package com.helloworld
53+
54+
import android.app.Application
55+
import com.facebook.react.PackageList
56+
import com.facebook.react.ReactApplication
57+
import com.facebook.react.ReactHost
58+
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
59+
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
60+
61+
class MainApplication : Application(), ReactApplication {
62+
63+
override val reactHost: ReactHost by lazy {
64+
getDefaultReactHost(
65+
context = applicationContext,
66+
packageList =
67+
PackageList(this).packages.apply {
68+
// Packages that cannot be autolinked yet can be added manually here, for example:
69+
// add(MyReactNativePackage())
70+
},
71+
)
72+
}
73+
74+
override fun onCreate() {
75+
super.onCreate()
76+
loadReactNative(this)
77+
}
78+
}
79+
`;
80+
5081
describe('Android init command', () => {
5182
it('should correctly modify Kotlin MainApplication content', () => {
5283
const modifiedContent = modifyMainApplicationKt(ktTemplate);
5384
expect(modifiedContent).toContain('import com.microsoft.codepush.react.CodePush');
5485
expect(modifiedContent).toContain('override fun getJSBundleFile(): String = CodePush.getJSBundleFile()');
5586
});
5687

88+
it('should insert jsBundleFilePath argument for RN 0.82 style MainApplication', () => {
89+
const modifiedContent = modifyMainApplicationKt(ktRN82Template);
90+
91+
expect(modifiedContent).toContain('import com.microsoft.codepush.react.CodePush');
92+
expect(modifiedContent).toContain('jsBundleFilePath = CodePush.getJSBundleFile()');
93+
94+
const packageListIndex = modifiedContent.indexOf('packageList =');
95+
const jsBundleIndex = modifiedContent.indexOf('jsBundleFilePath = CodePush.getJSBundleFile()');
96+
expect(jsBundleIndex).toBeGreaterThan(packageListIndex);
97+
});
98+
5799
it('should log a message and exit if MainApplication.java is found', async () => {
58100
const originalCwd = process.cwd();
59101

expo/plugin/withCodePushAndroid.js

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
const { withMainApplication, WarningAggregator } = require('expo/config-plugins');
22

3-
function androidMainApplicationApplyImplementation(
4-
mainApplication,
5-
find,
6-
add,
7-
reverse = false,
8-
) {
3+
const IMPORT_CODE_PUSH = 'import com.microsoft.codepush.react.CodePush';
4+
const RN_082_MARKER = ' getDefaultReactHost(';
5+
const JS_BUNDLE_FILE_PATH_ARGUMENT = 'jsBundleFilePath = CodePush.getJSBundleFile()';
6+
7+
function androidMainApplicationApplyImplementation(mainApplication, find, add, reverse = false) {
98
if (mainApplication.includes(add)) {
109
return mainApplication;
1110
}
@@ -28,20 +27,54 @@ function androidMainApplicationApplyImplementation(
2827
return mainApplication;
2928
}
3029

30+
function addJsBundleFilePathArgument(mainApplication) {
31+
if (mainApplication.includes(JS_BUNDLE_FILE_PATH_ARGUMENT)) {
32+
return mainApplication;
33+
}
34+
35+
const packageListArgumentPattern = /(packageList\s*=\s*\n\s*PackageList\(this\)[\s\S]+?\},\s*\n)/;
36+
37+
if (!packageListArgumentPattern.test(mainApplication)) {
38+
WarningAggregator.addWarningAndroid(
39+
'withCodePushAndroid',
40+
`
41+
Failed to detect "packageList = PackageList(this)" block in MainApplication.kt.
42+
Please add "jsBundleFilePath = CodePush.getJSBundleFile()" inside getDefaultReactHost arguments.
43+
44+
Android manual setup: https://github.com/Soomgo-Mobile/react-native-code-push#2-1-manual-setup
45+
`,
46+
);
47+
return mainApplication;
48+
}
49+
50+
return mainApplication.replace(packageListArgumentPattern, (match) => {
51+
if (match.includes('jsBundleFilePath')) {
52+
return match;
53+
}
54+
55+
return `${match} ${JS_BUNDLE_FILE_PATH_ARGUMENT},\n`;
56+
});
57+
}
58+
3159
const withAndroidMainApplicationDependency = (config) => {
3260
return withMainApplication(config, (action) => {
3361
action.modResults.contents = androidMainApplicationApplyImplementation(
3462
action.modResults.contents,
35-
'class MainApplication : Application(), ReactApplication {',
36-
'import com.microsoft.codepush.react.CodePush\n',
37-
true,
63+
'import com.facebook.react.ReactApplication',
64+
IMPORT_CODE_PUSH,
3865
);
3966

40-
action.modResults.contents = androidMainApplicationApplyImplementation(
41-
action.modResults.contents,
42-
'object : DefaultReactNativeHost(this) {',
43-
' override fun getJSBundleFile(): String = CodePush.getJSBundleFile()\n',
44-
);
67+
if (!action.modResults.contents.includes('CodePush.getJSBundleFile()')) {
68+
if (action.modResults.contents.includes(RN_082_MARKER)) {
69+
action.modResults.contents = addJsBundleFilePathArgument(action.modResults.contents);
70+
} else {
71+
action.modResults.contents = androidMainApplicationApplyImplementation(
72+
action.modResults.contents,
73+
'object : DefaultReactNativeHost(this) {',
74+
' override fun getJSBundleFile(): String = CodePush.getJSBundleFile()\n');
75+
}
76+
}
77+
4578
return action;
4679
});
4780
};

0 commit comments

Comments
 (0)