Skip to content

Commit b438280

Browse files
committed
fix(expo): add Kotlin metadata version check skip to config plugin
clerk-android is compiled with a newer Kotlin version than Expo/RN ships, causing "Module was compiled with an incompatible version of Kotlin" build failures. The config plugin now injects -Xskip-metadata-version-check into the app's kotlinOptions block during prebuild, matching the flag already set in the library's own build.gradle.
1 parent dd1c7e9 commit b438280

1 file changed

Lines changed: 47 additions & 23 deletions

File tree

packages/expo/src/plugin/withClerkExpo.ts

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,63 @@ import { type ConfigPlugin, createRunOncePlugin, withAppBuildGradle, withInfoPli
33
import pkg from '../../package.json';
44

55
/**
6-
* Add packaging exclusions to Android app build.gradle to resolve
7-
* duplicate META-INF file conflicts from clerk-android dependencies.
6+
* Add packaging exclusions and Kotlin compatibility flags to Android app build.gradle.
7+
*
8+
* - Resolves duplicate META-INF file conflicts from clerk-android dependencies
9+
* - Adds -Xskip-metadata-version-check to kotlinOptions so the app module can
10+
* consume clerk-android (compiled with a newer Kotlin version than Expo/RN ships)
811
*/
9-
const withClerkAndroidPackaging: ConfigPlugin = config => {
12+
const withClerkAndroidConfig: ConfigPlugin = config => {
1013
return withAppBuildGradle(config, modConfig => {
1114
let buildGradle = modConfig.modResults.contents;
1215

13-
// Check if exclusion already exists
14-
if (buildGradle.includes('META-INF/versions/9/OSGI-INF/MANIFEST.MF')) {
15-
return modConfig;
16-
}
17-
18-
// AGP 8+ uses `packaging` DSL, older versions use `packagingOptions`
19-
const packagingMatch = buildGradle.match(/packaging\s*\{/) || buildGradle.match(/packagingOptions\s*\{/);
20-
if (packagingMatch) {
21-
const blockName = packagingMatch[0].trim().replace(/\s*\{$/, '');
22-
const resourcesExclude = `${blockName} {
16+
// --- META-INF exclusion ---
17+
if (!buildGradle.includes('META-INF/versions/9/OSGI-INF/MANIFEST.MF')) {
18+
// AGP 8+ uses `packaging` DSL, older versions use `packagingOptions`
19+
const packagingMatch = buildGradle.match(/packaging\s*\{/) || buildGradle.match(/packagingOptions\s*\{/);
20+
if (packagingMatch) {
21+
const blockName = packagingMatch[0].trim().replace(/\s*\{$/, '');
22+
const resourcesExclude = `${blockName} {
2323
// Clerk Android SDK: exclude duplicate META-INF files
2424
resources {
2525
excludes += ['META-INF/versions/9/OSGI-INF/MANIFEST.MF']
2626
}`;
2727

28-
buildGradle = buildGradle.replace(new RegExp(`${blockName}\\s*\\{`), resourcesExclude);
29-
modConfig.modResults.contents = buildGradle;
30-
} else {
31-
// No packaging block found; append one at the end of the android block
32-
const androidBlockEnd = buildGradle.lastIndexOf('}');
33-
if (androidBlockEnd !== -1) {
34-
const packagingBlock = `\n packaging {\n resources {\n excludes += ['META-INF/versions/9/OSGI-INF/MANIFEST.MF']\n }\n }\n`;
35-
buildGradle = buildGradle.slice(0, androidBlockEnd) + packagingBlock + buildGradle.slice(androidBlockEnd);
36-
modConfig.modResults.contents = buildGradle;
28+
buildGradle = buildGradle.replace(new RegExp(`${blockName}\\s*\\{`), resourcesExclude);
29+
} else {
30+
// No packaging block found; append one at the end of the android block
31+
const androidBlockEnd = buildGradle.lastIndexOf('}');
32+
if (androidBlockEnd !== -1) {
33+
const packagingBlock = `\n packaging {\n resources {\n excludes += ['META-INF/versions/9/OSGI-INF/MANIFEST.MF']\n }\n }\n`;
34+
buildGradle = buildGradle.slice(0, androidBlockEnd) + packagingBlock + buildGradle.slice(androidBlockEnd);
35+
}
36+
}
37+
}
38+
39+
// --- Kotlin metadata version check skip ---
40+
// clerk-android is compiled with a newer Kotlin than Expo/RN ships.
41+
// Without this flag, the app module fails to compile with:
42+
// "Module was compiled with an incompatible version of Kotlin"
43+
if (!buildGradle.includes('-Xskip-metadata-version-check')) {
44+
const kotlinOptionsMatch = buildGradle.match(/kotlinOptions\s*\{/);
45+
if (kotlinOptionsMatch) {
46+
buildGradle = buildGradle.replace(
47+
/kotlinOptions\s*\{/,
48+
`kotlinOptions {\n // Clerk: allow reading metadata from newer Kotlin versions\n freeCompilerArgs += ['-Xskip-metadata-version-check']`,
49+
);
50+
} else {
51+
// No kotlinOptions block; add one inside the android block
52+
const androidMatch = buildGradle.match(/android\s*\{/);
53+
if (androidMatch) {
54+
buildGradle = buildGradle.replace(
55+
/android\s*\{/,
56+
`android {\n kotlinOptions {\n // Clerk: allow reading metadata from newer Kotlin versions\n freeCompilerArgs += ['-Xskip-metadata-version-check']\n }`,
57+
);
58+
}
3759
}
3860
}
3961

62+
modConfig.modResults.contents = buildGradle;
4063
return modConfig;
4164
});
4265
};
@@ -84,13 +107,14 @@ const withClerkGoogleSignIn: ConfigPlugin = config => {
84107
* When this plugin is used, it:
85108
* 1. Configures iOS URL scheme for Google Sign-In (if env var is set)
86109
* 2. Adds Android packaging exclusions to resolve dependency conflicts
110+
* 3. Adds Kotlin metadata version compatibility flag for clerk-android
87111
*
88112
* Native modules are registered via react-native.config.js and standard
89113
* React Native autolinking (RCTViewManager / ReactPackage).
90114
*/
91115
const withClerkExpo: ConfigPlugin = config => {
92116
config = withClerkGoogleSignIn(config);
93-
config = withClerkAndroidPackaging(config);
117+
config = withClerkAndroidConfig(config);
94118
return config;
95119
};
96120

0 commit comments

Comments
 (0)