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" }, ],