Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(clerk-expo)!: React Native >= 0.73 and Expo Web support #3456

Merged
merged 61 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c38211d
feat(expo): Support for Expo Web
octoper May 24, 2024
08754f5
chore(repo): Add Changeset
octoper May 28, 2024
96b3cb2
chore(expo): Update package.json
octoper May 28, 2024
bc0c3c5
fix(expo): Throw error when using component in native platforms
octoper May 28, 2024
b95559c
chore(expo): Use error thrower and move useOAuth hook
octoper May 29, 2024
bede556
chore(expo): Remove custom messages array from errorThrower
octoper May 30, 2024
625dbf2
chore(expo): Update the structure of the package
octoper May 30, 2024
c3fd056
fix(expo): Wrong type import in ClerkProvider
octoper May 30, 2024
c40d1a9
fix(expo): Fix wrong type import
octoper May 30, 2024
acad398
chore(expo): Make useOAuth base implementation to be the native one
octoper May 31, 2024
a0193c2
chore(expo): Remove console.logs from useOAuth
octoper May 31, 2024
fe55ada
chore(clerk-expo): User react 18.2.0
octoper Jun 12, 2024
847278f
chore: Update dependencies
octoper Jun 25, 2024
7a05874
feat(expo): Introducing web subpath for web only APIs & Components
octoper Jun 25, 2024
16b456a
chore(expo): Fix type issues
octoper Jun 25, 2024
b8c51f8
chore(expo): Fix web subpath exports
octoper Jun 26, 2024
47348ca
feat(expo): Only bundle Clerk in native platforms
octoper Jun 26, 2024
a5dd66f
fix(expo): Make useOAuth available to web
octoper Jun 28, 2024
e6425dd
feat(e2e): Introduce integration tests for Expo Web
octoper Jul 2, 2024
2be2f8d
chore(e2e): Setting up integration tests for Expo Web
octoper Jul 4, 2024
6643c23
chore(e2e): Fix issues for resolving dependencies in integration tests
octoper Jul 4, 2024
525ca3b
chore(e2e): Add expoWeb integration suite to CI action
octoper Jul 4, 2024
38ff442
fix(repo): Fix @clerk/types invalid version issue
octoper Jul 4, 2024
54c4d9b
fix(e2e): Remove headed mode from expo web integration script
octoper Jul 4, 2024
661f3d6
chore(repo): Add test:integration:expoWeb in Turbo
octoper Jul 4, 2024
636a6a7
chore(e2e): Use dev instead of build
octoper Jul 4, 2024
98fa6b1
chore(e2e): Update test:integration:expoWeb in Turbo
octoper Jul 4, 2024
b6c5dc3
chore(e2e): Add log for clerkExpoPath
octoper Jul 4, 2024
892683d
chore(e2e): Add log for clerkExpoPath
octoper Jul 4, 2024
7eb8ff5
chore(e2e): debug ci
octoper Jul 4, 2024
ca8fb07
chore(e2e): debug ci
octoper Jul 4, 2024
4f239c4
chore(e2e): debug ci
octoper Jul 4, 2024
b4c59b0
chore(e2e): debug ci
octoper Jul 4, 2024
307acb2
chore(e2e): debug ci
octoper Jul 4, 2024
2951bf5
chore(e2e): remove debug related stuff from metro.config.js
octoper Jul 4, 2024
5c54f27
chore(e2e): added more tests
octoper Jul 5, 2024
96b3311
chore(expo): User errorThrower in useOAuth
octoper Jul 5, 2024
8f54cfd
chore(e2e): debug ci
octoper Jul 5, 2024
837dd23
fix(expo): Handle authentication completion from popup on Web
octoper Jul 9, 2024
4e4c2f9
fix(expo): Remove react-native specific exports
octoper Jul 9, 2024
35a73ac
chore(expo): Remove not needed version range for react-native
octoper Jul 10, 2024
f70c051
feat(expo): Exported more UI components for Web
octoper Jul 12, 2024
3a8a097
fix(expo): Correctly export web subpath
octoper Jul 12, 2024
dcf2b88
fix(e2e): Fix Expo Web tests
octoper Jul 12, 2024
1243186
chore(clerk-expo): Move caches to cache so we don't introduce breakin…
octoper Jul 15, 2024
346a021
chore(clerk-expo): Lower react-native peer dep version to 0.72
octoper Jul 16, 2024
5e11b13
chore(e2e): Remove uneeded assets from expo-web template
octoper Jul 16, 2024
bd74b69
chore(clerk-expo): Remove skipNodeModulesBundle from tsup.config.ts
octoper Jul 16, 2024
6cf3ec8
chore(clerk-expo): Added process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KE…
octoper Jul 16, 2024
297edbd
chore(clerk-expo): Require react-native >= 0.73 and bump Expo peer de…
octoper Jul 18, 2024
cda7584
chore(repo): Update changeset
octoper Jul 18, 2024
7119e0b
improve changeset
LekoArts Jul 19, 2024
c216d72
improve README
LekoArts Jul 19, 2024
edd05ab
chore(e2e): Chnage expoWeb to expo-web
octoper Jul 24, 2024
a853d35
chore(clerk-expo): Use specific version of react-native-url-polyfill
octoper Jul 24, 2024
df1e607
chore(e2e): Chnage expoWeb to expo-web
octoper Jul 24, 2024
d1ca5a4
Update packages/expo/src/web/uiComponents.tsx
octoper Jul 24, 2024
c45c61c
chore(clerk-expo): Use default exports as we only support cjs
octoper Jul 24, 2024
1683660
chore(clerk-expo): Update error message for components used in native…
octoper Jul 25, 2024
999ed09
fix(clerk-expo): Fix getClerkInstance for web as it was not a function
octoper Jul 25, 2024
156c736
chore(clerk-expo): Update JSDOC of getClerkInstance
octoper Jul 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/two-wombats-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@clerk/clerk-expo': major
---

Support for [Expo Web](https://docs.expo.dev/workflow/web/) has been added! You can now build fullstack websites with Expo, React, and Clerk. Utilize [Clerk's components](https://clerk.com/docs/components/overview) to build out your app.

You can access the components from the `/web` subpath import like so:

```tsx
import { SignUp } from "@clerk/clerk-expo/web";

export default function Page() {
return <SignUp />;
}
```

**Breaking change:** You need to use Expo 50 or later. The minimum required React Native version was bumped to `0.73`.
octoper marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:

strategy:
matrix:
test-name: ['generic', 'express', 'quickstart', 'ap-flows', 'elements', 'sessions', 'astro']
test-name: ['generic', 'express', 'quickstart', 'ap-flows', 'elements', 'sessions', 'astro', 'expo-web']
test-project: ['chrome']
include:
- test-name: 'nextjs'
Expand Down
5 changes: 4 additions & 1 deletion integration/models/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ export const application = (
const serverUrl = `http://localhost:${port}`;
// If this is ever used as a background process, we need to make sure
// it's not using the log function. See the dev() method above
const proc = run(scripts.serve, { cwd: appDirPath, env: { PORT: port.toString() } });
const proc = run(scripts.serve, {
cwd: appDirPath,
env: { PORT: port.toString() },
});
cleanupFns.push(() => awaitableTreekill(proc.pid, 'SIGKILL'));
await waitForIdleProcess(proc);
state.serverUrl = serverUrl;
Expand Down
18 changes: 18 additions & 0 deletions integration/presets/expo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { applicationConfig } from '../models/applicationConfig';
import { templates } from '../templates';

const clerkExpoLocal = `file:${process.cwd()}/packages/expo`;

const expoWeb = applicationConfig()
.setName('expo-web')
.useTemplate(templates['expo-web'])
.setEnvFormatter('public', key => `EXPO_PUBLIC_${key}`)
.addScript('setup', 'npm i')
.addScript('dev', 'npm run dev')
.addScript('build', 'npm run build')
.addScript('serve', 'npm run start')
.addDependency('@clerk/clerk-expo', clerkExpoLocal);

export const expo = {
expoWeb,
} as const;
2 changes: 2 additions & 0 deletions integration/presets/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { astro } from './astro';
import { elements } from './elements';
import { expo } from './expo';
import { envs, instanceKeys } from './envs';
import { express } from './express';
import { createLongRunningApps } from './longRunningApps';
Expand All @@ -15,6 +16,7 @@ export const appConfigs = {
react,
remix,
elements,
expo,
astro,
secrets: {
instanceKeys,
Expand Down
2 changes: 2 additions & 0 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { longRunningApplication } from '../models/longRunningApplication';
import { astro } from './astro';
import { elements } from './elements';
import { envs } from './envs';
import { expo } from './expo';
import { express } from './express';
import { next } from './next';
import { react } from './react';
Expand All @@ -24,6 +25,7 @@ export const createLongRunningApps = () => {
{ id: 'quickstart.next.appRouter', config: next.appRouterQuickstart, env: envs.withEmailCodesQuickstart },
{ id: 'elements.next.appRouter', config: elements.nextAppRouter, env: envs.withEmailCodes },
{ id: 'astro.node.withCustomRoles', config: astro.node, env: envs.withCustomRoles },
{ id: 'expo.expo-web', config: expo.expoWeb, env: envs.withEmailCodes },
] as const;

const apps = configs.map(longRunningApplication);
Expand Down
14 changes: 14 additions & 0 deletions integration/templates/expo-web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/

# macOS
.DS_Store
50 changes: 50 additions & 0 deletions integration/templates/expo-web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Welcome to your Expo app 👋

This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).

## Get started

1. Install dependencies

```bash
npm install
```

2. Start the app

```bash
npx expo start
```

In the output, you'll find options to open the app in a

- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo

You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).

## Get a fresh project

When you're ready, run:

```bash
npm run reset-project
```

This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.

## Learn more

To learn more about developing your project with Expo, look at the following resources:

- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.

## Join the community

Join our community of developers creating universal apps.

- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
34 changes: 34 additions & 0 deletions integration/templates/expo-web/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"expo": {
"name": "expo-web",
"slug": "expo-web",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/icon.png"
},
"plugins": ["expo-router"],
"experiments": {
"typedRoutes": true
}
}
}
45 changes: 45 additions & 0 deletions integration/templates/expo-web/app/+html.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ScrollViewStyleReset } from 'expo-router/html';
import type { PropsWithChildren } from 'react';

/**
* This file is web-only and used to configure the root HTML for every web page during static rendering.
* The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs.
*/
export default function Root({ children }: PropsWithChildren) {
return (
<html lang='en'>
<head>
<meta charSet='utf-8' />
<meta
httpEquiv='X-UA-Compatible'
content='IE=edge'
/>
<meta
name='viewport'
content='width=device-width, initial-scale=1, shrink-to-fit=no'
/>

{/*
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
*/}
<ScrollViewStyleReset />

{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
{/* Add any additional <head> elements that you want globally available on web... */}
</head>
<body>{children}</body>
</html>
);
}

const responsiveBackground = `
body {
background-color: #fff;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #000;
}
}`;
14 changes: 14 additions & 0 deletions integration/templates/expo-web/app/+not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Link, Stack } from 'expo-router';
import { StyleSheet, Text } from 'react-native';

export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: 'Oops!', headerShown: true }} />
<Text>This screen doesn't exist.</Text>
<Link href='/'>
<Text>Go to home screen!</Text>
</Link>
</>
);
}
19 changes: 19 additions & 0 deletions integration/templates/expo-web/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Stack, useRouter } from 'expo-router';
import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo';

export default function RootLayout() {
const router = useRouter();

return (
<ClerkProvider
routerPush={(to: string) => router.push(to)}
routerReplace={to => router.replace(to)}
>
<ClerkLoaded>
<Stack>
<Stack.Screen name='index' />
</Stack>
</ClerkLoaded>
</ClerkProvider>
);
}
67 changes: 67 additions & 0 deletions integration/templates/expo-web/app/custom-sign-in.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useSignIn } from '@clerk/clerk-expo';
import { Link, useRouter } from 'expo-router';
import { Text, TextInput, Button, View } from 'react-native';
import React from 'react';

export default function Page() {
const { signIn, setActive, isLoaded } = useSignIn();
const router = useRouter();

const [emailAddress, setEmailAddress] = React.useState('');
const [password, setPassword] = React.useState('');

const onSignInPress = React.useCallback(async () => {
if (!isLoaded) {
return;
}

try {
const signInAttempt = await signIn.create({
identifier: emailAddress,
password,
});

if (signInAttempt.status === 'complete') {
await setActive({ session: signInAttempt.createdSessionId });
router.replace('/');
} else {
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(signInAttempt, null, 2));
}
} catch (err: any) {
console.error(JSON.stringify(err, null, 2));
}
}, [isLoaded, emailAddress, password]);

return (
<View>
<TextInput
autoCapitalize='none'
value={emailAddress}
placeholder='Email...'
id='emailAddress'
testID='emailAddress-input'
onChangeText={emailAddress => setEmailAddress(emailAddress)}
/>
<TextInput
value={password}
placeholder='Password...'
secureTextEntry={true}
id='password'
testID='password-input'
onChangeText={password => setPassword(password)}
/>
<Button
title='Sign In'
onPress={onSignInPress}
/>
<View>
<Text>Don't have an account?</Text>
<Link href='/sign-up'>
<Text>Sign up</Text>
</Link>
</View>
</View>
);
}
Loading
Loading