Skip to content

Commit 5cbd2e6

Browse files
committed
test(expo): explore aligned Kotlin dependencies on SDK 58
1 parent 2ee976b commit 5cbd2e6

9 files changed

Lines changed: 363 additions & 0 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/.expo/
2+
/.env*
3+
/android/
4+
/ios/
5+
/node_modules/
6+
/pnpm-lock.yaml
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React, { useState } from 'react';
2+
import { Button, Text, View } from 'react-native';
3+
import { ClerkProvider, useAuth, useClerk } from '@clerk/expo';
4+
import { AuthView, UserProfileView } from '@clerk/expo/native';
5+
import { tokenCache } from '@clerk/expo/token-cache';
6+
7+
function Probe() {
8+
const { isLoaded, isSignedIn, getToken } = useAuth();
9+
const { signOut } = useClerk();
10+
const [message, setMessage] = useState('');
11+
const [profile, setProfile] = useState(false);
12+
const [mounted, setMounted] = useState(true);
13+
14+
async function checkToken() {
15+
try {
16+
setMessage((await getToken()) ? 'Session token obtained' : 'No session token');
17+
} catch (error) {
18+
setMessage(error.message);
19+
}
20+
}
21+
22+
return (
23+
<View style={{ flex: 1, paddingTop: 50, paddingBottom: 35 }}>
24+
<Text>Clerk Kotlin alignment probe</Text>
25+
<Text>
26+
Loaded: {String(isLoaded)}; signed in: {String(isSignedIn)}
27+
</Text>
28+
<Text>{message}</Text>
29+
<Button
30+
title={mounted ? 'Unmount native view' : 'Mount native view'}
31+
onPress={() => setMounted(!mounted)}
32+
/>
33+
{isSignedIn && (
34+
<>
35+
<Button
36+
title='Check session token'
37+
onPress={checkToken}
38+
/>
39+
<Button
40+
title='Toggle profile'
41+
onPress={() => setProfile(!profile)}
42+
/>
43+
<Button
44+
title='Sign out'
45+
onPress={async () => {
46+
await signOut();
47+
setMessage('Signed out');
48+
}}
49+
/>
50+
</>
51+
)}
52+
{mounted &&
53+
(isSignedIn ? profile ? <UserProfileView /> : <Text>Authenticated</Text> : <AuthView mode='signInOrUp' />)}
54+
</View>
55+
);
56+
}
57+
58+
export default function App() {
59+
const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
60+
if (!publishableKey?.startsWith('pk_test_')) {
61+
return (
62+
<View style={{ padding: 40 }}>
63+
<Text>Set a development EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY and rebuild.</Text>
64+
</View>
65+
);
66+
}
67+
return (
68+
<ClerkProvider
69+
publishableKey={publishableKey}
70+
tokenCache={tokenCache}
71+
>
72+
<Probe />
73+
</ClerkProvider>
74+
);
75+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Expo 58 Kotlin alignment experiment
2+
3+
This isolated Android fixture explores a fully aligned Kotlin toolchain without
4+
changing Clerk's shipped defaults. It is pinned to published `@clerk/expo@4.7.1`,
5+
not the current monorepo package, so the experiment remains independent of changes
6+
to Clerk's native dependencies and package split.
7+
8+
It complements [#9791](https://github.com/clerk/javascript/pull/9791), which addresses
9+
the immediate release-build failure, and
10+
[#9955](https://github.com/clerk/javascript/pull/9955), which makes native components
11+
optional. [#9974](https://github.com/clerk/javascript/pull/9974) separately enables
12+
optimization in the existing Android integration workflow.
13+
14+
## Configuration
15+
16+
| Input | Version |
17+
| ---------------------------------- | ------------------------------------------- |
18+
| Expo | 58.0.0-preview.8 |
19+
| React Native | 0.88.0-rc.2 |
20+
| React / React DOM | 19.3.0 |
21+
| Clerk Expo | 4.7.1 |
22+
| Clerk native API / UI | 1.1.8, selected by the pinned Clerk package |
23+
| Kotlin / resolved Compose compiler | 2.4.20 |
24+
| SavedState Compose | 1.3.3 |
25+
26+
The official `expo-build-properties` plugin selects Kotlin 2.4.20 and enables
27+
minification and resource shrinking. The fixture-local pnpm patch adds SavedState
28+
Compose, removes six stdlib exclusions, and removes the local, cross-project and
29+
config-plugin metadata-check bypasses. It does not add a `dontwarn` rule.
30+
31+
The dependency reports from the original experiment resolved both the Kotlin
32+
compiler and Compose compiler to 2.4.20 even though Clerk's plugin declaration still
33+
says 2.1.20. No additional Compose patch was necessary on this exact stack.
34+
35+
Only this fixture consumes the patch. It is not an installation recommendation for
36+
older Expo SDKs and does not change Clerk's declared Expo support range. The
37+
expected Expo peer-range warning is retained rather than hidden.
38+
39+
## Run
40+
41+
Use Node 24.15 or newer, pnpm, JDK 17 and an Android SDK. Start an arm64 emulator;
42+
this fixture intentionally builds only `arm64-v8a` to match the original test.
43+
44+
From this directory:
45+
46+
```sh
47+
pnpm install
48+
```
49+
50+
Create an ignored `.env` with your own development-instance publishable key:
51+
52+
```dotenv
53+
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_REPLACE_WITH_YOUR_DEVELOPMENT_KEY
54+
```
55+
56+
```sh
57+
pnpm android --device YOUR_EMULATOR_NAME
58+
```
59+
60+
The publication fixture also passed a fresh pnpm install and optimized build.
61+
Expo CLI subsequently tried to open a development-client URI that this standalone
62+
release does not handle; the build and installation succeeded. If that occurs,
63+
open the installed app from the emulator launcher.
64+
65+
Expo manages prebuild and compilation. After changing native configuration, run
66+
`pnpm prebuild:android` before building again. The fixture refuses a production
67+
publishable key. No secret key or test credentials belong in this directory.
68+
69+
Confirm that `android/app/build/outputs/mapping/release/mapping.txt` is nonempty.
70+
Inspect the generated merged R8 configuration to confirm no
71+
`-dontwarn kotlin.MustUseReturnValues` rule was added, and inspect the patched
72+
Clerk Gradle/plugin files and generated app Gradle file for absent
73+
`-Xskip-metadata-version-check` flags. Dependency reports should confirm the actual
74+
compiler artifacts, not only the configuration property.
75+
76+
## Manual authentication checks
77+
78+
Use an account in the development instance:
79+
80+
1. Sign in through the native form. Complete any verification challenge.
81+
2. Select **Check session token**; the app reports presence, never the token.
82+
3. Force-stop and reopen the app; check that the session and token remain available.
83+
4. Open the profile, unmount/remount it, and confirm it renders again.
84+
5. Sign out and confirm the signed-out form returns.
85+
86+
The original isolated alignment build passed these checks on an API 36 arm64
87+
emulator. Independent development Backend API reads confirmed the session changed
88+
from active to removed. The development test account was deleted afterwards.
89+
The publication fixture removes the original probe's test-user deletion control;
90+
manage any account cleanup separately in your development instance.
91+
92+
## Comparison evidence
93+
94+
These are isolated reproduction results against published Clerk 4.7.1, not a full
95+
execution of #9791 or a certification of all patch releases and auth providers.
96+
Both small-fix variants kept minification and resource shrinking enabled.
97+
98+
| Expo | React Native | Stock | Remove exclusions | Exact annotation rule |
99+
| ---------------- | ------------ | ------------------------------------------- | ----------------- | --------------------- |
100+
| 54.0.37 | 0.81.5 | Missing annotation | Build/auth pass | Build/auth pass |
101+
| 55.0.31 | 0.83.10 | Missing annotation | Build/auth pass | Build/auth pass |
102+
| 56.0.23 | 0.85.3 | Missing annotation | Build/auth pass | Build/auth pass |
103+
| 57.0.25 | 0.86.3 | Missing annotation | Build/auth pass | Build/auth pass |
104+
| 58.0.0-preview.8 | 0.88.0-rc.2 | SavedState failure, then missing annotation | Build/auth pass | Build/auth pass |
105+
106+
The Expo 54 narrow/runtime checks used native Clerk 1.1.9; the 55–58 checks used
107+
1.1.8. On 58, both small fixes included SavedState Compose 1.3.3; the 1.4.0 addition
108+
from #9791 was separately build/startup-tested on 57. The 58 consumer-rule build
109+
produced identical DEX to the authenticated app-level-rule APK, and merged R8
110+
configuration confirmed delivery from Clerk's library.
111+
112+
Older SDKs emitted metadata warnings with both small fixes. The 55 narrow build
113+
completed R8 with 353 warnings before a disk-full interruption during lint; its
114+
successful unchanged retry reused R8 outputs and must not be called warning-free.
115+
The fully aligned 58 experiment had no R8 metadata warnings.
116+
117+
## Boundaries and next decision
118+
119+
The native publication floor, older Expo compiler constraints and supported
120+
version policy still need maintainer direction. This fixture is deliberately not
121+
a conditional production implementation based on SDK version.
122+
123+
Physical devices, Google/Apple OAuth, passkeys, long-duration token expiry,
124+
existing-install session migration and the full upstream CI suite are outside
125+
this experiment's completed coverage. The package versions are pinned directly;
126+
transitive JavaScript dependencies are resolved at install time, following the
127+
existing standalone fixture convention. Record the generated lockfile when
128+
comparing a later dependency graph.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"expo": {
3+
"name": "Clerk Kotlin Alignment",
4+
"slug": "clerk-kotlin-alignment",
5+
"version": "1.0.0",
6+
"android": {
7+
"package": "com.clerk.expo.kotlin_alignment"
8+
},
9+
"plugins": [
10+
"@clerk/expo",
11+
[
12+
"expo-build-properties",
13+
{
14+
"android": {
15+
"enableMinifyInReleaseBuilds": true,
16+
"enableShrinkResourcesInReleaseBuilds": true,
17+
"buildArchs": ["arm64-v8a"],
18+
"kotlinVersion": "2.4.20"
19+
}
20+
}
21+
]
22+
]
23+
}
24+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { registerRootComponent } from 'expo';
2+
import App from './App';
3+
4+
registerRootComponent(App);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "clerk-expo-kotlin-alignment-fixture",
3+
"version": "1.0.0",
4+
"private": true,
5+
"main": "index.js",
6+
"scripts": {
7+
"android": "expo run:android --variant release",
8+
"prebuild:android": "expo prebuild --clean --platform android"
9+
},
10+
"dependencies": {
11+
"@clerk/expo": "4.7.1",
12+
"expo": "58.0.0-preview.8",
13+
"expo-auth-session": "58.0.5",
14+
"expo-build-properties": "58.0.8",
15+
"expo-constants": "58.0.8",
16+
"expo-crypto": "58.0.2",
17+
"expo-secure-store": "58.0.0",
18+
"expo-status-bar": "58.0.1",
19+
"expo-web-browser": "58.0.2",
20+
"react": "19.3.0",
21+
"react-dom": "19.3.0",
22+
"react-native": "0.88.0-rc.2"
23+
},
24+
"packageManager": "pnpm@10.34.5"
25+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
diff --git a/android/build.gradle b/android/build.gradle
2+
--- a/android/build.gradle
3+
+++ b/android/build.gradle
4+
@@ -59,10 +59,6 @@
5+
6+
kotlinOptions {
7+
jvmTarget = "17"
8+
- // TODO: Remove once clerk-android pins its transitive deps to a Kotlin version compatible with Expo.
9+
- // clerk-android transitive deps (e.g. kotlinx-serialization-core:1.10.0) ship Kotlin 2.3.x metadata,
10+
- // but Expo SDK 54/55 uses Kotlin 2.1.x. This flag lets the 2.1.x compiler read 2.3.x metadata safely.
11+
- freeCompilerArgs += ['-Xskip-metadata-version-check']
12+
}
13+
14+
buildFeatures {
15+
@@ -83,25 +79,9 @@
16+
}
17+
}
18+
19+
-// clerk-android is compiled with Kotlin 2.3.x, but Expo/RN projects typically use an older
20+
-// Kotlin version (e.g. 2.1.x). This causes "incompatible version of Kotlin" metadata errors
21+
-// at :app:compileDebugKotlin. Applying -Xskip-metadata-version-check allows the older
22+
-// compiler to read 2.3.x metadata without failing.
23+
-try {
24+
- rootProject.allprojects {
25+
- tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
26+
- compilerOptions {
27+
- freeCompilerArgs.add('-Xskip-metadata-version-check')
28+
- }
29+
- }
30+
- }
31+
-} catch (Exception ignored) {
32+
- // Future Gradle versions with Isolated Projects may block cross-project configuration.
33+
- // In that case, users should add '@clerk/expo' to their app.json plugins array instead,
34+
- // which applies the flag via the config plugin's withClerkAndroid.
35+
-}
36+
37+
dependencies {
38+
+ implementation "androidx.savedstate:savedstate-compose:1.3.3"
39+
testImplementation "junit:junit:4.13.2"
40+
41+
implementation project(':expo-modules-core')
42+
@@ -110,12 +90,8 @@
43+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxCoroutinesVersion"
44+
45+
// Clerk Android SDK with prebuilt UI
46+
- // Exclude kotlin-stdlib to prevent 2.3.0 from polluting the project
47+
// Exclude transitive OkHttp modules so the compatible versions below control resolution.
48+
implementation("com.clerk:clerk-android-ui:$clerkAndroidUiVersion") {
49+
- exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
50+
- exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
51+
- exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
52+
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
53+
exclude group: 'com.squareup.okhttp3', module: 'okhttp-urlconnection'
54+
}
55+
@@ -123,9 +99,6 @@
56+
// clerk-android-api. Pinning the api explicitly here keeps consumers
57+
// compiling against the same version we ship the UI from.
58+
implementation("com.clerk:clerk-android-api:$clerkAndroidApiVersion") {
59+
- exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
60+
- exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
61+
- exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
62+
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
63+
exclude group: 'com.squareup.okhttp3', module: 'okhttp-urlconnection'
64+
}
65+
diff --git a/app.plugin.js b/app.plugin.js
66+
--- a/app.plugin.js
67+
+++ b/app.plugin.js
68+
@@ -159,26 +159,6 @@
69+
}
70+
}
71+
console.log('✅ Clerk Android packaging exclusions added');
72+
- }
73+
-
74+
- // --- Kotlin metadata version check skip ---
75+
- if (!buildGradle.includes('-Xskip-metadata-version-check')) {
76+
- const kotlinOptionsMatch = buildGradle.match(/kotlinOptions\s*\{/);
77+
- if (kotlinOptionsMatch) {
78+
- buildGradle = buildGradle.replace(
79+
- /kotlinOptions\s*\{/,
80+
- `kotlinOptions {\n // Clerk: allow reading metadata from newer Kotlin versions\n freeCompilerArgs += ['-Xskip-metadata-version-check']`,
81+
- );
82+
- } else {
83+
- const androidMatch = buildGradle.match(/android\s*\{/);
84+
- if (androidMatch) {
85+
- buildGradle = buildGradle.replace(
86+
- /android\s*\{/,
87+
- `android {\n kotlinOptions {\n // Clerk: allow reading metadata from newer Kotlin versions\n freeCompilerArgs += ['-Xskip-metadata-version-check']\n }`,
88+
- );
89+
- }
90+
- }
91+
- console.log('✅ Clerk Android Kotlin metadata version check skip added');
92+
}
93+
94+
modConfig.modResults.contents = buildGradle;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
packages:
2+
- '.'
3+
4+
patchedDependencies:
5+
'@clerk/expo@4.7.1': patches/clerk-expo.patch

0 commit comments

Comments
 (0)