From f55d39110ece0f020d52d8488fc77ff9e865afae Mon Sep 17 00:00:00 2001 From: Henri VDM <30859194+jan-vdm@users.noreply.github.com> Date: Fri, 30 Aug 2024 22:52:01 +1000 Subject: [PATCH 1/6] feat: add clerk support --- .changeset/nine-jobs-occur.md | 5 ++ cli/src/commands/create-expo-stack.ts | 8 ++ cli/src/templates/base/.gitignore.ejs | 2 +- cli/src/templates/base/App.tsx.ejs | 77 +++++++++++-------- cli/src/templates/base/package.json.ejs | 4 + cli/src/templates/packages/clerk/.env.ejs | 1 + .../expo-router/drawer/app/_layout.tsx.ejs | 31 ++++++-- .../expo-router/stack/app/_layout.tsx.ejs | 22 +++++- .../expo-router/tabs/app/_layout.tsx.ejs | 26 ++++++- .../packages/vexo-analytics/.env.ejs | 3 + cli/src/types.ts | 3 +- cli/src/utilities/configureProjectFiles.ts | 9 ++- cli/src/utilities/generateProjectFiles.ts | 4 + cli/src/utilities/printOutput.ts | 16 +++- cli/src/utilities/runCLI.ts | 1 + cli/src/utilities/showHelp.ts | 1 + docs/src/content/docs/en/installation.md | 1 + www/demo/steps/printOutput.js | 23 +++++- www/demo/steps/setAuthentication.js | 1 + 19 files changed, 188 insertions(+), 50 deletions(-) create mode 100644 .changeset/nine-jobs-occur.md create mode 100644 cli/src/templates/packages/clerk/.env.ejs 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/src/commands/create-expo-stack.ts b/cli/src/commands/create-expo-stack.ts index 16e14522..ad65d21c 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' + }); + } + // Internalization packages if (options.i18next) { cliResults.packages.push({ 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..16159ded 100644 --- a/cli/src/templates/base/App.tsx.ejs +++ b/cli/src/templates/base/App.tsx.ejs @@ -1,6 +1,10 @@ import { ScreenContent } from 'components/ScreenContent'; import { StatusBar } from 'expo-status-bar'; +<% if (props.authenticationPackage?.name === "clerk") { %> + import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; +<% } %> + <% if (props.internalizationPackage?.name === "i18next") { %> import './translation'; import { InternalizationExample } from 'components/InternalizationExample'; @@ -28,46 +32,55 @@ 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 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") {%> - export default function App() { - return ( - - - <% if (props.internalizationPackage?.name === "i18next") { %> - - <% } else { %> - Open up App.tsx to start working on your app! - <% } %> - - - - ); - } + + + <% 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 { %> + +<% } %> + ); +} diff --git a/cli/src/templates/base/package.json.ejs b/cli/src/templates/base/package.json.ejs index ef886880..3a9cf238 100644 --- a/cli/src/templates/base/package.json.ejs +++ b/cli/src/templates/base/package.json.ejs @@ -130,6 +130,10 @@ <% if (props.authenticationPackage?.name === "firebase") { %> "firebase": "^10.5.2", <% } %> + + <% if (props.authenticationPackage?.name === "clerk") { %> + "@clerk/clerk-expo": "^2.2.5", + <% } %> <% if (props.internalizationPackage?.name === "i18next") { %> "i18next": "^23.7.20", 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..bf4b2dd1 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,9 @@ import '../unistyles'; import '../translation'; <% } %> +<% if (props.authenticationPackage?.name === "clerk") { %> + 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 +46,14 @@ export const unstable_settings = { }; export default function RootLayout() { + + <% if (props.authenticationPackage?.name === "clerk") { %> + 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 +75,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..eb782fb6 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,9 @@ import '../unistyles'; import '../translation'; <% } %> +<% if (props.authenticationPackage?.name === "clerk") { %> + 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 +33,13 @@ import '../unistyles'; export default function Layout() { + <% if (props.authenticationPackage?.name === "clerk") { %> + 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 +56,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..d2e6462b 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,9 @@ import '../unistyles'; import '../translation'; <% } %> +<% if (props.authenticationPackage?.name === "clerk") { %> + import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; +<% } %> <% if (props.stylingPackage?.name === "tamagui") { %> import React, { useEffect } from "react"; import { TamaguiProvider } from 'tamagui' @@ -40,6 +43,13 @@ export const unstable_settings = { }; export default function RootLayout() { + <% if (props.authenticationPackage?.name === "clerk") { %> + 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 +71,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/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 48931900..2ba9f8da 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', @@ -29,7 +30,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 9f130d99..2cfbf54e 100644 --- a/cli/src/utilities/configureProjectFiles.ts +++ b/cli/src/utilities/configureProjectFiles.ts @@ -308,6 +308,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']; @@ -315,7 +322,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 15dd1ad5..8be34902 100644 --- a/cli/src/utilities/generateProjectFiles.ts +++ b/cli/src/utilities/generateProjectFiles.ts @@ -19,6 +19,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 6a7c6553..684fa4b3 100644 --- a/cli/src/utilities/runCLI.ts +++ b/cli/src/utilities/runCLI.ts @@ -331,6 +331,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" }, ], From 7c5cdeed53705d14cf9604218452dc9e1a141d70 Mon Sep 17 00:00:00 2001 From: Henri VDM <30859194+jan-vdm@users.noreply.github.com> Date: Sat, 31 Aug 2024 01:09:11 +1000 Subject: [PATCH 2/6] test: added a clerk expo-router test scenario --- .../cli-integration.test.ts.snap | 134 ++++++++++++++++++ cli/__tests__/cli-integration.test.ts | 4 +- cli/src/templates/base/App.tsx.ejs | 3 +- cli/src/templates/base/package.json.ejs | 5 +- 4 files changed, 142 insertions(+), 4 deletions(-) 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/templates/base/App.tsx.ejs b/cli/src/templates/base/App.tsx.ejs index 16159ded..e3ebdecd 100644 --- a/cli/src/templates/base/App.tsx.ejs +++ b/cli/src/templates/base/App.tsx.ejs @@ -82,5 +82,4 @@ export default function App() { <% } %> ); -} - +} \ 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 3a9cf238..e1fd8044 100644 --- a/cli/src/templates/base/package.json.ejs +++ b/cli/src/templates/base/package.json.ejs @@ -133,8 +133,11 @@ <% if (props.authenticationPackage?.name === "clerk") { %> "@clerk/clerk-expo": "^2.2.5", + <% 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", From efbf19d3bc072deb5bd34c44d71a8f194bda3f5c Mon Sep 17 00:00:00 2001 From: Henri VDM <30859194+jan-vdm@users.noreply.github.com> Date: Sat, 31 Aug 2024 01:26:00 +1000 Subject: [PATCH 3/6] feat: added clerk support for react navigation --- .../packages/react-navigation/App.tsx.ejs | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/cli/src/templates/packages/react-navigation/App.tsx.ejs b/cli/src/templates/packages/react-navigation/App.tsx.ejs index 88baafae..68665fdd 100644 --- a/cli/src/templates/packages/react-navigation/App.tsx.ejs +++ b/cli/src/templates/packages/react-navigation/App.tsx.ejs @@ -10,6 +10,10 @@ import './unistyles'; <% if (props.internalizationPackage?.name === "i18next") { %> import './translation'; <% } %> + +<% if (props.authenticationPackage?.name === "clerk") { %> + import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; +<% } %> import "react-native-gesture-handler"; <% if (props.stylingPackage?.name === "tamagui") { %> import React, { useEffect } from "react"; @@ -36,6 +40,13 @@ import "react-native-gesture-handler"; <% } %> export default function App() { + <% if (props.authenticationPackage?.name === "clerk") { %> + 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 +62,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") { %> + + <% } %> + ); } From c11d6227e00622343a8a543ff214047460b2521c Mon Sep 17 00:00:00 2001 From: Henri VDM <30859194+jan-vdm@users.noreply.github.com> Date: Wed, 4 Sep 2024 23:30:43 +1000 Subject: [PATCH 4/6] chore: update dependencies --- cli/src/templates/base/package.json.ejs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/templates/base/package.json.ejs b/cli/src/templates/base/package.json.ejs index e1fd8044..fc3cef76 100644 --- a/cli/src/templates/base/package.json.ejs +++ b/cli/src/templates/base/package.json.ejs @@ -132,7 +132,8 @@ <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> - "@clerk/clerk-expo": "^2.2.5", + "@clerk/clerk-expo": "2.2.7", + "expo-auth-session": "5.5.2", <% if (props.navigationPackage?.name !== "expo-router") { %> "react-dom": "18.2.0", <% } %> From aed67cca1fd62d4884280c2e174d9031dbb50823 Mon Sep 17 00:00:00 2001 From: Henri VDM <30859194+jan-vdm@users.noreply.github.com> Date: Wed, 4 Sep 2024 23:42:07 +1000 Subject: [PATCH 5/6] chore: reverted dependency to 2.2.5 as the new versions have breaking bugs --- cli/src/templates/base/package.json.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/templates/base/package.json.ejs b/cli/src/templates/base/package.json.ejs index fc3cef76..dd58e2a3 100644 --- a/cli/src/templates/base/package.json.ejs +++ b/cli/src/templates/base/package.json.ejs @@ -132,7 +132,7 @@ <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> - "@clerk/clerk-expo": "2.2.7", + "@clerk/clerk-expo": "2.2.5", "expo-auth-session": "5.5.2", <% if (props.navigationPackage?.name !== "expo-router") { %> "react-dom": "18.2.0", From f65846c4b56e9c32b8765345148eea6b3f1d1141 Mon Sep 17 00:00:00 2001 From: Henri VDM <30859194+jan-vdm@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:38:07 +1100 Subject: [PATCH 6/6] feat: add expo secure store to clerk template --- cli/src/templates/base/App.tsx.ejs | 28 ++++++++++++++++++- cli/src/templates/base/package.json.ejs | 1 + .../expo-router/drawer/app/_layout.tsx.ejs | 28 ++++++++++++++++++- .../expo-router/stack/app/_layout.tsx.ejs | 28 ++++++++++++++++++- .../expo-router/tabs/app/_layout.tsx.ejs | 28 ++++++++++++++++++- .../packages/react-navigation/App.tsx.ejs | 28 ++++++++++++++++++- 6 files changed, 136 insertions(+), 5 deletions(-) diff --git a/cli/src/templates/base/App.tsx.ejs b/cli/src/templates/base/App.tsx.ejs index e3ebdecd..c18c7965 100644 --- a/cli/src/templates/base/App.tsx.ejs +++ b/cli/src/templates/base/App.tsx.ejs @@ -2,6 +2,7 @@ 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'; <% } %> @@ -34,6 +35,31 @@ import { StatusBar } from 'expo-status-bar'; 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'); @@ -42,7 +68,7 @@ export default function App() { return ( <% if (props.authenticationPackage?.name === "clerk") { %> - + <% } else { %> <> diff --git a/cli/src/templates/base/package.json.ejs b/cli/src/templates/base/package.json.ejs index dd58e2a3..3f5b8e5f 100644 --- a/cli/src/templates/base/package.json.ejs +++ b/cli/src/templates/base/package.json.ejs @@ -134,6 +134,7 @@ <% 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", <% } %> 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 bf4b2dd1..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 @@ -13,6 +13,7 @@ import '../unistyles'; <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> + import * as SecureStore from 'expo-secure-store' import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; <% } %> import 'react-native-gesture-handler'; @@ -48,6 +49,31 @@ 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'); @@ -76,7 +102,7 @@ export default function RootLayout() { <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> - + <% } %> 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 eb782fb6..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 @@ -10,6 +10,7 @@ import '../unistyles'; <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> + import * as SecureStore from 'expo-secure-store' import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; <% } %> <% if (props.stylingPackage?.name === "tamagui") { %> @@ -34,6 +35,31 @@ 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'); @@ -63,7 +89,7 @@ export default function Layout() { <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> - + <% } %> 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 d2e6462b..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 @@ -12,6 +12,7 @@ import '../unistyles'; <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> + import * as SecureStore from 'expo-secure-store' import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; <% } %> <% if (props.stylingPackage?.name === "tamagui") { %> @@ -44,6 +45,31 @@ 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'); @@ -72,7 +98,7 @@ export default function RootLayout() { <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> - + <% } %> diff --git a/cli/src/templates/packages/react-navigation/App.tsx.ejs b/cli/src/templates/packages/react-navigation/App.tsx.ejs index 68665fdd..9eeb9d4e 100644 --- a/cli/src/templates/packages/react-navigation/App.tsx.ejs +++ b/cli/src/templates/packages/react-navigation/App.tsx.ejs @@ -12,6 +12,7 @@ import './unistyles'; <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> + import * as SecureStore from 'expo-secure-store' import { ClerkLoaded, ClerkProvider } from '@clerk/clerk-expo'; <% } %> import "react-native-gesture-handler"; @@ -41,6 +42,31 @@ 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'); @@ -72,7 +98,7 @@ export default function App() { <% } %> <% if (props.authenticationPackage?.name === "clerk") { %> - + <% } %>