diff --git a/.changeset/nine-jobs-occur.md b/.changeset/nine-jobs-occur.md
new file mode 100644
index 00000000..3fe19683
--- /dev/null
+++ b/.changeset/nine-jobs-occur.md
@@ -0,0 +1,5 @@
+---
+'create-expo-stack': minor
+---
+
+Add clerk authentication support
diff --git a/cli/__tests__/__snapshots__/cli-integration.test.ts.snap b/cli/__tests__/__snapshots__/cli-integration.test.ts.snap
index 0dced646..e14142f6 100644
--- a/cli/__tests__/__snapshots__/cli-integration.test.ts.snap
+++ b/cli/__tests__/__snapshots__/cli-integration.test.ts.snap
@@ -1388,3 +1388,137 @@ exports[`generates a project with --expo-router --drawer+tabs --nativewindui --b
./myTestProject/tsconfig.json
"
`;
+
+exports[`generates a project with --expo-router --tabs --clerk --nativewind --bun --overwrite: --expo-router, --tabs, --clerk, --nativewind, --bun, --overwrite-package-json 1`] = `
+{
+ "dependencies": {
+ "@expo/vector-icons": "",
+ "@react-navigation/native": "",
+ "expo": "",
+ "expo-constants": "",
+ "expo-linking": "",
+ "expo-router": "",
+ "expo-status-bar": "",
+ "expo-system-ui": "",
+ "expo-web-browser": "",
+ "nativewind": "",
+ "react": "",
+ "react-dom": "",
+ "react-native": "",
+ "react-native-gesture-handler": "",
+ "react-native-reanimated": "",
+ "react-native-safe-area-context": "",
+ "react-native-screens": "",
+ "react-native-web": "",
+ },
+ "devDependencies": {
+ "@babel/core": "",
+ "@types/react": "",
+ "@typescript-eslint/eslint-plugin": "",
+ "@typescript-eslint/parser": "",
+ "ajv": "",
+ "eslint": "",
+ "eslint-config-universe": "",
+ "prettier": "",
+ "prettier-plugin-tailwindcss": "",
+ "tailwindcss": "",
+ "typescript": "",
+ },
+ "eslintConfig": {
+ "extends": "universe/native",
+ "root": true,
+ },
+ "expo": {
+ "install": {
+ "exclude": [
+ "react-native-safe-area-context",
+ ],
+ },
+ },
+ "main": "expo-router/entry",
+ "name": "myTestProject",
+ "private": true,
+ "scripts": {
+ "android": "expo start --android",
+ "format": "eslint "**/*.{js,jsx,ts,tsx}" --fix && prettier "**/*.{js,jsx,ts,tsx,json}" --write",
+ "ios": "expo start --ios",
+ "lint": "eslint "**/*.{js,jsx,ts,tsx}" && prettier -c "**/*.{js,jsx,ts,tsx,json}"",
+ "prebuild": "expo prebuild",
+ "start": "expo start",
+ "web": "expo start --web",
+ },
+ "version": "1.0.0",
+}
+`;
+
+exports[`generates a project with --expo-router --tabs --clerk --nativewind --bun --overwrite: --expo-router, --tabs, --clerk, --nativewind, --bun, --overwrite-ces-config-json 1`] = `
+{
+ "cesVersion": undefined,
+ "flags": {
+ "eas": false,
+ "importAlias": true,
+ "noGit": false,
+ "noInstall": false,
+ "overwrite": true,
+ "packageManager": "bun",
+ },
+ "os": {},
+ "packageManager": {
+ "type": "bun",
+ "version": undefined,
+ },
+ "packages": [
+ {
+ "name": "expo-router",
+ "options": {
+ "type": "stack",
+ },
+ "type": "navigation",
+ },
+ {
+ "name": "nativewind",
+ "type": "styling",
+ },
+ ],
+ "projectName": "myTestProject",
+}
+`;
+
+exports[`generates a project with --expo-router --tabs --clerk --nativewind --bun --overwrite: --expo-router, --tabs, --clerk, --nativewind, --bun, --overwrite-file-list 1`] = `
+"./myTestProject
+./myTestProject/.env
+./myTestProject/app
+./myTestProject/app-env.d.ts
+./myTestProject/app.json
+./myTestProject/app/(tabs)
+./myTestProject/app/(tabs)/_layout.tsx
+./myTestProject/app/(tabs)/index.tsx
+./myTestProject/app/(tabs)/two.tsx
+./myTestProject/app/+html.tsx
+./myTestProject/app/+not-found.tsx
+./myTestProject/app/_layout.tsx
+./myTestProject/app/modal.tsx
+./myTestProject/assets
+./myTestProject/assets/adaptive-icon.png
+./myTestProject/assets/favicon.png
+./myTestProject/assets/icon.png
+./myTestProject/assets/splash.png
+./myTestProject/babel.config.js
+./myTestProject/bun.lockb
+./myTestProject/cesconfig.json
+./myTestProject/components
+./myTestProject/components/Button.tsx
+./myTestProject/components/Container.tsx
+./myTestProject/components/EditScreenInfo.tsx
+./myTestProject/components/HeaderButton.tsx
+./myTestProject/components/ScreenContent.tsx
+./myTestProject/components/TabBarIcon.tsx
+./myTestProject/expo-env.d.ts
+./myTestProject/global.css
+./myTestProject/metro.config.js
+./myTestProject/package.json
+./myTestProject/prettier.config.js
+./myTestProject/tailwind.config.js
+./myTestProject/tsconfig.json
+"
+`;
diff --git a/cli/__tests__/cli-integration.test.ts b/cli/__tests__/cli-integration.test.ts
index 3e0a42d8..71e53358 100644
--- a/cli/__tests__/cli-integration.test.ts
+++ b/cli/__tests__/cli-integration.test.ts
@@ -82,7 +82,9 @@ const popularCombinations = [
// no install is important for the website cli that generates a project zip file
['--nativewindui', '--no-install'],
// nativewindui blank
- ['--expo-router', '--drawer+tabs', '--nativewindui', '--blank', '--expo-router']
+ ['--expo-router', '--drawer+tabs', '--nativewindui', '--blank', '--expo-router'],
+ // clerk expo-router tabs nativewind
+ ['--expo-router', '--tabs', '--clerk', '--nativewind']
] as const;
const projectName = `myTestProject`;
diff --git a/cli/src/commands/create-expo-stack.ts b/cli/src/commands/create-expo-stack.ts
index 37851f80..bea159c4 100644
--- a/cli/src/commands/create-expo-stack.ts
+++ b/cli/src/commands/create-expo-stack.ts
@@ -296,6 +296,14 @@ const command: GluegunCommand = {
});
}
+ if (options.clerk) {
+ // Add clerk package
+ cliResults.packages.push({
+ name: 'clerk',
+ type: 'authentication'
+ });
+ }
+
// State Management packages
if (options.zustand) {
// Add zustand package
diff --git a/cli/src/templates/base/.gitignore.ejs b/cli/src/templates/base/.gitignore.ejs
index 5040396e..ebc7ec29 100644
--- a/cli/src/templates/base/.gitignore.ejs
+++ b/cli/src/templates/base/.gitignore.ejs
@@ -13,7 +13,7 @@ web-build/
expo-env.d.ts<% } %>
<% if (props.stylingPackage?.name === "tamagui") { %># tamagui
.tamagui/<% } %>
-<% if ((props.authenticationPackage?.name === "supabase") || (props.authenticationPackage?.name === "firebase" || (props.analyticsPackage?.name === 'vexo-analytics'))) { %># firebase/supabase/vexo
+<% if ((props.authenticationPackage?.name === "clerk") || (props.authenticationPackage?.name === "supabase") || (props.authenticationPackage?.name === "firebase" || (props.analyticsPackage?.name === 'vexo-analytics'))) { %># clerk/firebase/supabase/vexo
.env<% } %>
ios
diff --git a/cli/src/templates/base/App.tsx.ejs b/cli/src/templates/base/App.tsx.ejs
index 112a2feb..c18c7965 100644
--- a/cli/src/templates/base/App.tsx.ejs
+++ b/cli/src/templates/base/App.tsx.ejs
@@ -1,6 +1,11 @@
import { ScreenContent } from 'components/ScreenContent';
import { StatusBar } from 'expo-status-bar';
+<% if (props.authenticationPackage?.name === "clerk") { %>
+ import * as SecureStore from 'expo-secure-store'
+ import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo';
+<% } %>
+
<% if (props.internalizationPackage?.name === "i18next") { %>
import './translation';
import { InternalizationExample } from 'components/InternalizationExample';
@@ -28,46 +33,79 @@ import { StatusBar } from 'expo-status-bar';
vexo(process.env.EXPO_PUBLIC_VEXO_API_KEY);
<% } %>
-<% if (props.stylingPackage?.name === "restyle") {%>
- export default function App() {
- return (
-
-
- <% if (props.internalizationPackage?.name === "i18next") { %>
-
- <% } %>
-
-
-
- );
+export default function App() {
+<% if (props.authenticationPackage?.name === "clerk") { %>
+ const tokenCache = {
+ async getToken(key: string) {
+ try {
+ const item = await SecureStore.getItemAsync(key)
+ if (item) {
+ console.log(`${key} was used 🔐 \n`)
+ } else {
+ console.log('No values stored under key: ' + key)
+ }
+ return item
+ } catch (error) {
+ console.error('SecureStore get item error: ', error)
+ await SecureStore.deleteItemAsync(key)
+ return null
+ }
+ },
+ async saveToken(key: string, value: string) {
+ try {
+ return SecureStore.setItemAsync(key, value)
+ } catch (err) {
+ return
+ }
+ },
}
-<% } else if (props.stylingPackage?.name === "tamagui") {%>
- export default function App() {
- return (
-
-
- <% if (props.internalizationPackage?.name === "i18next") { %>
-
- <% } else { %>
- Open up App.tsx to start working on your app!
- <% } %>
-
-
-
- );
+
+ const clerkPublishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
+ if (!clerkPublishableKey) {
+ throw new Error('Missing EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY, add one in your .env file');
}
+<% } %>
+
+ return (
+<% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+<% } else { %>
+ <>
+<% } %>
+<% if (props.stylingPackage?.name === "restyle") {%>
+
+
+ <% if (props.internalizationPackage?.name === "i18next") { %>
+
+ <% } %>
+
+
+
+<% } else if (props.stylingPackage?.name === "tamagui") {%>
+
+
+ <% if (props.internalizationPackage?.name === "i18next") { %>
+
+ <% } else { %>
+ Open up App.tsx to start working on your app!
+ <% } %>
+
+
+
<% } else { %>
- export default function App() {
- return (
- <>
<% if (props.internalizationPackage?.name === "i18next") { %>
<% } %>
- >
- );
- }
<% } %>
-
+<% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+<% } else { %>
+ >
+<% } %>
+ );
+}
\ No newline at end of file
diff --git a/cli/src/templates/base/package.json.ejs b/cli/src/templates/base/package.json.ejs
index 5822640c..f913bb46 100644
--- a/cli/src/templates/base/package.json.ejs
+++ b/cli/src/templates/base/package.json.ejs
@@ -142,7 +142,16 @@
<% if (props.authenticationPackage?.name === "firebase") { %>
"firebase": "^10.5.2",
<% } %>
-
+
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+ "@clerk/clerk-expo": "2.2.5",
+ "expo-auth-session": "5.5.2",
+ "expo-secure-store": "13.0.2",
+ <% if (props.navigationPackage?.name !== "expo-router") { %>
+ "react-dom": "18.2.0",
+ <% } %>
+ <% } %>
+
<% if (props.internalizationPackage?.name === "i18next") { %>
"i18next": "^23.7.20",
"react-i18next": "^14.0.1",
diff --git a/cli/src/templates/packages/clerk/.env.ejs b/cli/src/templates/packages/clerk/.env.ejs
new file mode 100644
index 00000000..d1bab076
--- /dev/null
+++ b/cli/src/templates/packages/clerk/.env.ejs
@@ -0,0 +1 @@
+EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=
\ No newline at end of file
diff --git a/cli/src/templates/packages/expo-router/drawer/app/_layout.tsx.ejs b/cli/src/templates/packages/expo-router/drawer/app/_layout.tsx.ejs
index c5bf2d2a..d2659e0f 100644
--- a/cli/src/templates/packages/expo-router/drawer/app/_layout.tsx.ejs
+++ b/cli/src/templates/packages/expo-router/drawer/app/_layout.tsx.ejs
@@ -12,6 +12,10 @@ import '../unistyles';
import '../translation';
<% } %>
+<% if (props.authenticationPackage?.name === "clerk") { %>
+ import * as SecureStore from 'expo-secure-store'
+ import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo';
+<% } %>
import 'react-native-gesture-handler';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Stack } from 'expo-router';
@@ -43,6 +47,39 @@ export const unstable_settings = {
};
export default function RootLayout() {
+
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+ const tokenCache = {
+ async getToken(key: string) {
+ try {
+ const item = await SecureStore.getItemAsync(key)
+ if (item) {
+ console.log(`${key} was used 🔐 \n`)
+ } else {
+ console.log('No values stored under key: ' + key)
+ }
+ return item
+ } catch (error) {
+ console.error('SecureStore get item error: ', error)
+ await SecureStore.deleteItemAsync(key)
+ return null
+ }
+ },
+ async saveToken(key: string, value: string) {
+ try {
+ return SecureStore.setItemAsync(key, value)
+ } catch (err) {
+ return
+ }
+ },
+ }
+
+ const clerkPublishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
+ if (!clerkPublishableKey) {
+ throw new Error('Missing EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY, add one in your .env file');
+ }
+ <% } %>
+
<% if (props.stylingPackage?.name==="tamagui" ) { %>
const [loaded] = useFonts({
Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"),
@@ -64,12 +101,20 @@ export default function RootLayout() {
<% } else if (props.stylingPackage?.name === "restyle") { %>
<% } %>
-
-
-
-
-
-
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
+
+
+
+
+
+
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
<% if (props.stylingPackage?.name === "tamagui") { %>
<% } else if (props.stylingPackage?.name === "restyle") { %>
diff --git a/cli/src/templates/packages/expo-router/stack/app/_layout.tsx.ejs b/cli/src/templates/packages/expo-router/stack/app/_layout.tsx.ejs
index 5220239c..93e7c7a5 100644
--- a/cli/src/templates/packages/expo-router/stack/app/_layout.tsx.ejs
+++ b/cli/src/templates/packages/expo-router/stack/app/_layout.tsx.ejs
@@ -9,6 +9,10 @@ import '../unistyles';
import '../translation';
<% } %>
+<% if (props.authenticationPackage?.name === "clerk") { %>
+ import * as SecureStore from 'expo-secure-store'
+ import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo';
+<% } %>
<% if (props.stylingPackage?.name === "tamagui") { %>
import { useFonts } from 'expo-font';
import { SplashScreen, Stack } from 'expo-router';
@@ -30,6 +34,38 @@ import '../unistyles';
export default function Layout() {
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+ const tokenCache = {
+ async getToken(key: string) {
+ try {
+ const item = await SecureStore.getItemAsync(key)
+ if (item) {
+ console.log(`${key} was used 🔐 \n`)
+ } else {
+ console.log('No values stored under key: ' + key)
+ }
+ return item
+ } catch (error) {
+ console.error('SecureStore get item error: ', error)
+ await SecureStore.deleteItemAsync(key)
+ return null
+ }
+ },
+ async saveToken(key: string, value: string) {
+ try {
+ return SecureStore.setItemAsync(key, value)
+ } catch (err) {
+ return
+ }
+ },
+ }
+
+ const clerkPublishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
+ if (!clerkPublishableKey) {
+ throw new Error('Missing EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY, add one in your .env file');
+ }
+ <% } %>
+
<% if (props.stylingPackage?.name === "tamagui") { %>
const [loaded] = useFonts({
Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"),
@@ -46,16 +82,26 @@ export default function Layout() {
<% } %>
return (
+
<% if (props.stylingPackage?.name === "tamagui") { %>
<% } else if (props.stylingPackage?.name === "restyle") { %>
<% } %>
-
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
+
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
<% if (props.stylingPackage?.name === "tamagui") { %>
<% } else if (props.stylingPackage?.name === "restyle") { %>
<% } %>
+
);
}
diff --git a/cli/src/templates/packages/expo-router/tabs/app/_layout.tsx.ejs b/cli/src/templates/packages/expo-router/tabs/app/_layout.tsx.ejs
index 489ca9de..26e14d26 100644
--- a/cli/src/templates/packages/expo-router/tabs/app/_layout.tsx.ejs
+++ b/cli/src/templates/packages/expo-router/tabs/app/_layout.tsx.ejs
@@ -11,6 +11,10 @@ import '../unistyles';
import '../translation';
<% } %>
+<% if (props.authenticationPackage?.name === "clerk") { %>
+ import * as SecureStore from 'expo-secure-store'
+ import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo';
+<% } %>
<% if (props.stylingPackage?.name === "tamagui") { %>
import React, { useEffect } from "react";
import { TamaguiProvider } from 'tamagui'
@@ -40,6 +44,38 @@ export const unstable_settings = {
};
export default function RootLayout() {
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+ const tokenCache = {
+ async getToken(key: string) {
+ try {
+ const item = await SecureStore.getItemAsync(key)
+ if (item) {
+ console.log(`${key} was used 🔐 \n`)
+ } else {
+ console.log('No values stored under key: ' + key)
+ }
+ return item
+ } catch (error) {
+ console.error('SecureStore get item error: ', error)
+ await SecureStore.deleteItemAsync(key)
+ return null
+ }
+ },
+ async saveToken(key: string, value: string) {
+ try {
+ return SecureStore.setItemAsync(key, value)
+ } catch (err) {
+ return
+ }
+ },
+ }
+
+ const clerkPublishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
+ if (!clerkPublishableKey) {
+ throw new Error('Missing EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY, add one in your .env file');
+ }
+ <% } %>
+
<% if (props.stylingPackage?.name === "tamagui") { %>
const [loaded] = useFonts({
Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"),
@@ -61,10 +97,18 @@ export default function RootLayout() {
<% } else if (props.stylingPackage?.name === "restyle") { %>
<% } %>
-
-
-
-
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
+
+
+
+
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
<% if (props.stylingPackage?.name === "tamagui") { %>
<% } else if (props.stylingPackage?.name === "restyle") { %>
diff --git a/cli/src/templates/packages/react-navigation/App.tsx.ejs b/cli/src/templates/packages/react-navigation/App.tsx.ejs
index 88baafae..9eeb9d4e 100644
--- a/cli/src/templates/packages/react-navigation/App.tsx.ejs
+++ b/cli/src/templates/packages/react-navigation/App.tsx.ejs
@@ -10,6 +10,11 @@ import './unistyles';
<% if (props.internalizationPackage?.name === "i18next") { %>
import './translation';
<% } %>
+
+<% if (props.authenticationPackage?.name === "clerk") { %>
+ import * as SecureStore from 'expo-secure-store'
+ import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo';
+<% } %>
import "react-native-gesture-handler";
<% if (props.stylingPackage?.name === "tamagui") { %>
import React, { useEffect } from "react";
@@ -36,6 +41,38 @@ import "react-native-gesture-handler";
<% } %>
export default function App() {
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+ const tokenCache = {
+ async getToken(key: string) {
+ try {
+ const item = await SecureStore.getItemAsync(key)
+ if (item) {
+ console.log(`${key} was used 🔐 \n`)
+ } else {
+ console.log('No values stored under key: ' + key)
+ }
+ return item
+ } catch (error) {
+ console.error('SecureStore get item error: ', error)
+ await SecureStore.deleteItemAsync(key)
+ return null
+ }
+ },
+ async saveToken(key: string, value: string) {
+ try {
+ return SecureStore.setItemAsync(key, value)
+ } catch (err) {
+ return
+ }
+ },
+ }
+
+ const clerkPublishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
+ if (!clerkPublishableKey) {
+ throw new Error('Missing EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY, add one in your .env file');
+ }
+ <% } %>
+
<% if (props.stylingPackage?.name === "tamagui") { %>
const [loaded] = useFonts({
Inter: require("@tamagui/font-inter/otf/Inter-Medium.otf"),
@@ -51,19 +88,29 @@ export default function App() {
if (!loaded) {
return null;
}
+ <% } %>
- return (
-
-
-
- );
- <% } else if (props.stylingPackage?.name === "restyle") { %>
- return (
+ return (
+ <% if (props.stylingPackage?.name === "tamagui") { %>
+
+ <% } %>
+ <% if (props.stylingPackage?.name === "restyle") { %>
-
+ <% } %>
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
+
+ <% if (props.authenticationPackage?.name === "clerk") { %>
+
+
+ <% } %>
+ <% if (props.stylingPackage?.name === "restyle") { %>
- );
- <% } else { %>
- return ;
- <% } %>
+ <% } %>
+ <% if (props.stylingPackage?.name === "tamagui") { %>
+
+ <% } %>
+ );
}
diff --git a/cli/src/templates/packages/vexo-analytics/.env.ejs b/cli/src/templates/packages/vexo-analytics/.env.ejs
index 391e06fc..ad70ad79 100644
--- a/cli/src/templates/packages/vexo-analytics/.env.ejs
+++ b/cli/src/templates/packages/vexo-analytics/.env.ejs
@@ -4,4 +4,7 @@ EXPO_PUBLIC_VEXO_API_KEY=
<% } %>
<% if (props.authenticationPackage?.name === "firebase") { %>
<%- include('../firebase/.env') -%>
+<% } %>
+<% if (props.authenticationPackage?.name === "clerk") { %>
+<%- include('../clerk/.env') -%>
<% } %>
\ No newline at end of file
diff --git a/cli/src/types.ts b/cli/src/types.ts
index ef0b22c9..cddd7b0f 100644
--- a/cli/src/types.ts
+++ b/cli/src/types.ts
@@ -10,6 +10,7 @@ export interface CliFlags {
export const availablePackages = [
'@react-navigation/drawer',
+ 'clerk',
'expo-router',
'expoRouter',
'firebase',
@@ -30,7 +31,7 @@ export const availablePackages = [
'vexo-analytics'
] as const;
-export type AuthenticationSelect = 'supabase' | 'firebase' | undefined;
+export type AuthenticationSelect = 'supabase' | 'firebase' | 'clerk' | undefined;
export type NavigationSelect = 'react-navigation' | 'expo-router' | undefined;
diff --git a/cli/src/utilities/configureProjectFiles.ts b/cli/src/utilities/configureProjectFiles.ts
index 0e5a216e..53f86ed6 100644
--- a/cli/src/utilities/configureProjectFiles.ts
+++ b/cli/src/utilities/configureProjectFiles.ts
@@ -309,6 +309,13 @@ export function configureProjectFiles(
files = [...files, ...expoRouterFiles];
}
+ // add clerk files if needed
+ if (authenticationPackage?.name === 'clerk') {
+ const clerkFiles = ['packages/clerk/.env.ejs'];
+
+ files = [...files, ...clerkFiles];
+ }
+
// add supabase files if needed
if (authenticationPackage?.name === 'supabase') {
const supabaseFiles = ['packages/supabase/utils/supabase.ts.ejs', 'packages/supabase/.env.ejs'];
@@ -316,7 +323,7 @@ export function configureProjectFiles(
files = [...files, ...supabaseFiles];
}
- // add supabase files if needed
+ // add firebase files if needed
if (authenticationPackage?.name === 'firebase') {
const firebaseFiles = [
'packages/firebase/utils/firebase.ts.ejs',
diff --git a/cli/src/utilities/generateProjectFiles.ts b/cli/src/utilities/generateProjectFiles.ts
index 431e841b..3a610ca7 100644
--- a/cli/src/utilities/generateProjectFiles.ts
+++ b/cli/src/utilities/generateProjectFiles.ts
@@ -20,6 +20,10 @@ export function generateProjectFiles(
const template = file;
let target = file.replace('.ejs', '');
+ if (authenticationPackage?.name === 'clerk') {
+ target = target.replace('packages/clerk/', '');
+ }
+
if (authenticationPackage?.name === 'supabase') {
target = target.replace('packages/supabase/', '');
}
diff --git a/cli/src/utilities/printOutput.ts b/cli/src/utilities/printOutput.ts
index 2fcd53ad..ddadfea3 100644
--- a/cli/src/utilities/printOutput.ts
+++ b/cli/src/utilities/printOutput.ts
@@ -142,8 +142,20 @@ export async function printOutput(
info(``);
};
- // check if packages includes package with name "supabase"
- if (cliResults.packages.some((pkg) => pkg.name === 'supabase')) {
+ if (cliResults.packages.some((pkg) => pkg.name === 'clerk')) {
+ success(`\nSuccess! 🎉 Now, here's what's next:`);
+ info(``);
+ highlight('Head over to https://dashboard.clerk.dev to create a new Clerk project.');
+ info(``);
+ highlight(`Get the Clerk Publishable API Key:`);
+ info(`1. Go to the API keys page in the Dashboard.`);
+ info(`2. Find your Publishable API Key on this page.`);
+ info(`3. Copy this key and paste it into your .env file.`);
+ info(`4. Optionally, follow one of these guides to get started with Clerk:`);
+ highlight(`https://clerk.com/docs/quickstarts/expo`);
+ success(`Once you're done, run the following to get started: `);
+ info(``);
+ } else if (cliResults.packages.some((pkg) => pkg.name === 'supabase')) {
success(`\nSuccess! 🎉 Now, here's what's next:`);
info(``);
highlight('Head over to https://database.new to create a new Supabase project.');
diff --git a/cli/src/utilities/runCLI.ts b/cli/src/utilities/runCLI.ts
index 810d76f5..7d09ad83 100644
--- a/cli/src/utilities/runCLI.ts
+++ b/cli/src/utilities/runCLI.ts
@@ -358,6 +358,7 @@ export async function runCLI(toolbox: Toolbox, projectName: string): Promise p.type === "authentication") || undefined;
- // check if packages includes package with name "supabase"
- if (authenticationPackage && authenticationPackage.name === "supabase") {
+ // check if packages includes package with name "clerk"
+ if (authenticationPackage && authenticationPackage.name === "clerk") {
+ return `${color.green(`\nSuccess! 🎉 Now, here's what's next:`)}
+ \n ${color.blue(
+ "Head over to https://dashboard.clerk.dev to create a new Clerk project.",
+ )}
+ \n${color.blue(`Get the Clerk Publishable API Key:`)}
+ \n1. Go to the API keys page in the Dashboard.
+ \n2. Find your Publishable API Key on this page.
+ \n3. Copy this key and paste it into your .env file.
+ \n4. Optionally, follow one of these guides to get started with Clerk:
+ \n${color.blue(`https://clerk.com/docs/quickstarts/expo`)}
+ \n${color.green(
+ `Once you're done, run the following to get started: `,
+ )}\n\n${stepsToRunProject}
+ `;
+ // check if packages includes package with name "supabase"
+ } else if (
+ authenticationPackage &&
+ authenticationPackage.name === "supabase"
+ ) {
return `${color.green(`\nSuccess! 🎉 Now, here's what's next:`)}
\n${color.blue(
"Head over to https://database.new to create a new Supabase project.",
diff --git a/www/demo/steps/setAuthentication.js b/www/demo/steps/setAuthentication.js
index 380b5e79..cd0c2e7c 100644
--- a/www/demo/steps/setAuthentication.js
+++ b/www/demo/steps/setAuthentication.js
@@ -6,6 +6,7 @@ export async function setAuthentication(cliResults) {
message: "What would you like to use for authentication?",
options: [
{ value: undefined, label: "None" },
+ { value: "clerk", label: "Clerk" },
{ value: "supabase", label: "Supabase" },
{ value: "firebase", label: "Firebase" },
],