Skip to content

Commit

Permalink
feat: Add Kotlin autolinker for React Native 0.63.x+
Browse files Browse the repository at this point in the history
Issue: #7821
  • Loading branch information
retyui committed Dec 3, 2024
1 parent efee2bd commit d924541
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 41 deletions.
59 changes: 34 additions & 25 deletions autolink/postlink/activityLinker.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,20 @@ class ActivityLinker {
}

_removeGetMainComponentName(contents) {
var match = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*String\s*getMainComponentName\s*\(\)\s*{\s*return.+\s*\}/.exec(
contents
);
if (match) {
debugn(' Removing getMainComponentName function');
return contents.replace(
/\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*String\s*getMainComponentName\s*\(\)\s*{\s*return.+\s*\}/,
''
);
var javaRegex = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*String\s*getMainComponentName\s*\(\)\s*{\s*return.+\s*\}/;
var javaMatch = javaRegex.exec(contents);
if (javaMatch) {
debugn(' [Java] Removing getMainComponentName function');
return contents.replace(javaRegex, '');
}

var kotlinRegex = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*override\sfun\sgetMainComponentName\(\):\sString\s=\s.+/;
var kotlinMatch = kotlinRegex.exec(contents);
if (kotlinMatch) {
debugn(' [Kotlin] Removing getMainComponentName function');
return contents.replace(kotlinRegex, '');
}

warnn(' getMainComponentName function was not found.');
return contents;
}
Expand All @@ -78,6 +82,7 @@ class ActivityLinker {
debugn(' Extending NavigationActivity');
return activityContent
.replace(/extends\s+ReactActivity\s*/, 'extends NavigationActivity ')
.replace(/ReactActivity\(\)/, 'NavigationActivity()')
.replace(
/public\s+MainActivityDelegate\s*\(\s*ReactActivity\s+activity,\s*String\s+mainComponentName\s*\)/,
'public MainActivityDelegate(NavigationActivity activity, String mainComponentName)'
Expand All @@ -94,30 +99,34 @@ class ActivityLinker {
}

_doesActivityExtendReactActivity(activityContent) {
return /public\s+class\s+MainActivity\s+extends\s+ReactActivity\s*/.test(activityContent);
return (
/public\s+class\s+MainActivity\s+extends\s+ReactActivity\s*/.test(activityContent) ||
/class\sMainActivity\s?:\s?ReactActivity\(\)/.test(activityContent)
);
}

_hasAlreadyExtendNavigationActivity(activityContent) {
return /public\s+class\s+MainActivity\s+extends\s+NavigationActivity\s*/.test(activityContent);
return (
/public\s+class\s+MainActivity\s+extends\s+NavigationActivity\s*/.test(activityContent) ||
/class\sMainActivity\s:\sNavigationActivity\(\)\s\{/.test(activityContent)
);
}

_removeCreateReactActivityDelegate(activityContent) {
if (this._hasCreateReactActivityDelegate(activityContent)) {
debugn(' Removing createReactActivityDelegate function');
return activityContent.replace(
/\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*ReactActivityDelegate\s*createReactActivityDelegate\s*\(\)\s*{\s*return((.|\r|\s)*?)}/,
''
);
} else {
warnn(' createReactActivityDelegate is already not defined in MainActivity');
return activityContent;
var javaRegex = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*ReactActivityDelegate\s*createReactActivityDelegate\s*\(\)\s*{\s*return((.|\r|\s)*?)}/;
if (javaRegex.test(activityContent)) {
debugn(' [Java] Removing createReactActivityDelegate function');
return activityContent.replace(javaRegex, '');
}
}

_hasCreateReactActivityDelegate(activityContent) {
return /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*ReactActivityDelegate\s*createReactActivityDelegate\s*\(\)\s*{\s*return((.|\r|\s)*?)}/.test(
activityContent
);
var kotlinRegex = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*override\sfun\screateReactActivityDelegate\(\):\sReactActivityDelegate\s=\s+DefaultReactActivityDelegate\(this,\smainComponentName,\sfabricEnabled\)/;
if (kotlinRegex.test(activityContent)) {
debugn(' [Kotlin] Removing createReactActivityDelegate function');
return activityContent.replace(kotlinRegex, '');
}

warnn(' createReactActivityDelegate is already not defined in MainActivity');
return activityContent;
}
}

Expand Down
45 changes: 34 additions & 11 deletions autolink/postlink/applicationLinker.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class ApplicationLinker {
/extends\s+Application\s+implements\s+ReactApplication/gi,
'extends NavigationApplication'
)
.replace(/:\sApplication\(\),\sReactApplication/, ': NavigationApplication()')
.replace(
'import com.facebook.react.ReactApplication;',
'import com.reactnativenavigation.NavigationApplication;'
Expand All @@ -88,13 +89,18 @@ class ApplicationLinker {
}

_doesExtendApplication(applicationContent) {
return /\s+MainApplication\s+extends\s+Application\s+implements\s+ReactApplication\s+/.test(
applicationContent
return (
/\s+MainApplication\s+extends\s+Application\s+implements\s+ReactApplication\s+/.test(
applicationContent
) || /class\sMainApplication\s:\sApplication\(\),\sReactApplication/.test(applicationContent)
);
}

_hasAlreadyLinkedApplication(applicationContent) {
return /\s+extends\s+NavigationApplication\s+/.test(applicationContent);
return (
/\s+extends\s+NavigationApplication\s+/.test(applicationContent) ||
/class\sMainApplication\s:\sNavigationApplication\(\)/.test(applicationContent)
);
}

_extendNavigationHost(applicationContent) {
Expand All @@ -114,7 +120,8 @@ class ApplicationLinker {
} else if (this._doesExtendDefaultReactNativeHost(applicationContent)) {
debugn(' Changing host implementation to NavigationReactNativeHost');
return applicationContent
.replace('new DefaultReactNativeHost(this)', 'new NavigationReactNativeHost(this)')
.replace('new DefaultReactNativeHost(this)', 'new NavigationReactNativeHost(this)') // Java
.replace('DefaultReactNativeHost(this)', 'NavigationReactNativeHost(this)') // Kotlin
.replace(
'import com.facebook.react.defaults.DefaultReactNativeHost;',
'import com.facebook.react.defaults.DefaultReactNativeHost;\nimport com.reactnativenavigation.react.NavigationReactNativeHost;'
Expand All @@ -129,27 +136,43 @@ class ApplicationLinker {
}

_doesExtendDefaultReactNativeHost(applicationContent) {
return /\s*new DefaultReactNativeHost\(this\)\s*/.test(applicationContent);
return (
/\s*new DefaultReactNativeHost\(this\)\s*/.test(applicationContent) ||
/DefaultReactNativeHost\(this\)/.test(applicationContent)
);
}

_hasAlreadyLinkedNavigationHost(applicationContent) {
return /\s*new NavigationReactNativeHost\(this\)\s*/.test(applicationContent);
return (
/\s*new NavigationReactNativeHost\(this\)\s*/.test(applicationContent) ||
/NavigationReactNativeHost\(this\)/.test(applicationContent)
);
}

_removeSOLoaderInit(applicationContent) {
if (this._isSOLoaderInitCalled(applicationContent)) {
debugn(' Removing call to SOLoader.init()');
return applicationContent.replace(
/SoLoader.init\(\s*this\s*,\s*[/* native exopackage */]*\s*false\s*\);/,
''
);
return applicationContent
.replace(
// Java
/SoLoader.init\(\s*this\s*,\s*[/* native exopackage */]*\s*false\s*\);/,
''
)
.replace(
// Kotlin
/SoLoader\.init\(this,\sfalse\)/,
''
);
}
warnn(' SOLoader.init() is not called, skipping.');
return applicationContent;
}

_isSOLoaderInitCalled(applicationContent) {
return /SoLoader.init\(this,\s*[/* native exopackage */]*\s*false\);/.test(applicationContent);
return (
/SoLoader.init\(this,\s*[/* native exopackage */]*\s*false\);/.test(applicationContent) ||
/SoLoader\.init\(this,\sfalse\)/.test(applicationContent)
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion autolink/postlink/gradleLinker.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class GradleLinker {
* @param {string} contents
*/
_isKotlinPluginDeclared(contents) {
return /org.jetbrains.kotlin:kotlin-gradle-plugin:/.test(contents);
return /org\.jetbrains\.kotlin:kotlin-gradle-plugin:?/.test(contents);
}
}

Expand Down
10 changes: 6 additions & 4 deletions autolink/postlink/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ var ignoreFolders = {
ignore: ['node_modules/**', '**/build/**', '**/Build/**', '**/DerivedData/**', '**/*-tvOS*/**'],
};

exports.mainActivityJava = glob.sync('**/MainActivity.java', ignoreFolders)[0];
exports.mainActivityKotlin = glob.sync('**/MainActivity.kt', ignoreFolders)[0];
var mainApplicationJava = glob.sync('**/MainApplication.java', ignoreFolders)[0];
exports.mainActivityJava = glob.sync('**/MainActivity.{java,kt}', ignoreFolders)[0];
var mainApplicationJava = glob.sync('**/MainApplication.{java,kt}', ignoreFolders)[0];
exports.mainApplicationJava = mainApplicationJava;
exports.rootGradle = mainApplicationJava.replace(/android\/app\/.*\.java/, 'android/build.gradle');
exports.rootGradle = mainApplicationJava.replace(
/android\/app\/.*\.(java|kt)/,
'android/build.gradle'
);

var reactNativeVersion = require('../../../react-native/package.json').version;
exports.appDelegate = glob.sync(
Expand Down

0 comments on commit d924541

Please sign in to comment.