mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-08-16 18:50:44 +03:00
Adds Auth and Email Sender recipes
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
This commit is contained in:
parent
e58f4ecacc
commit
bd9c09471f
@ -5,11 +5,13 @@ import Data.List.NonEmpty (fromList)
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject), require)
|
||||
import Wasp.Cli.Command.UseRecipe.Auth (useAuth)
|
||||
import Wasp.Cli.Command.UseRecipe.EmailSender (useEmailSender)
|
||||
import Wasp.Cli.Command.UseRecipe.Tailwind (useTailwind)
|
||||
import qualified Wasp.Cli.Interactive as Interactive
|
||||
|
||||
data Recipe = Recipe
|
||||
{ recipeName :: String,
|
||||
recipeDescription :: String,
|
||||
execute :: Command ()
|
||||
}
|
||||
|
||||
@ -18,20 +20,27 @@ instance Show Recipe where
|
||||
|
||||
instance Interactive.Option Recipe where
|
||||
showOption = show
|
||||
showOptionDescription _ = Nothing
|
||||
showOptionDescription = Just . recipeDescription
|
||||
|
||||
useRecipe :: [String] -> Command ()
|
||||
useRecipe _args = do
|
||||
InWaspProject waspProjectDir <- require
|
||||
InWaspProject _ <- require
|
||||
|
||||
let recipes =
|
||||
[ Recipe
|
||||
{ recipeName = "tailwind",
|
||||
execute = useTailwind waspProjectDir
|
||||
{ recipeName = "Tailwind",
|
||||
recipeDescription = "Add support for Tailwind CSS",
|
||||
execute = useTailwind
|
||||
},
|
||||
Recipe
|
||||
{ recipeName = "auth",
|
||||
{ recipeName = "Auth",
|
||||
recipeDescription = "Add authentication to your app",
|
||||
execute = useAuth
|
||||
},
|
||||
Recipe
|
||||
{ recipeName = "Email sender",
|
||||
recipeDescription = "Set up an email sender",
|
||||
execute = useEmailSender
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
{-# LANGUAGE InstanceSigs #-}
|
||||
|
||||
module Wasp.Cli.Command.UseRecipe.Auth where
|
||||
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Data.List.NonEmpty (fromList)
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command.Message (cliSendMessageC)
|
||||
import Wasp.Cli.Command.UseRecipe.Auth.Email (useEmail)
|
||||
import Wasp.Cli.Command.UseRecipe.Auth.Local (useLocal)
|
||||
import Wasp.Cli.Command.UseRecipe.Auth.Social (useGithub, useGoogle)
|
||||
import qualified Wasp.Cli.Interactive as Interactive
|
||||
import qualified Wasp.Message as Msg
|
||||
|
||||
@ -16,20 +21,23 @@ instance Show AuthMethod where
|
||||
show Github = "Github"
|
||||
|
||||
instance Interactive.Option AuthMethod where
|
||||
showOption :: AuthMethod -> String
|
||||
showOption = show
|
||||
showOptionDescription :: AuthMethod -> Maybe String
|
||||
showOptionDescription _ = Nothing
|
||||
|
||||
useAuth :: Command ()
|
||||
useAuth = do
|
||||
method <- liftIO selectMethod
|
||||
|
||||
cliSendMessageC $ Msg.Start "Installing authentication..."
|
||||
|
||||
-- Create React pages for each method
|
||||
-- Edit the Wasp file (or prompt the user to do so)
|
||||
|
||||
cliSendMessageC $ Msg.Success $ "Installed " <> show method <> " authentication!"
|
||||
cliSendMessageC $ Msg.Start $ "Installing " <> show method <> " authentication..."
|
||||
useMethod method
|
||||
where
|
||||
useMethod Email = useEmail
|
||||
useMethod UsernameAndPassword = useLocal
|
||||
useMethod Google = useGoogle
|
||||
useMethod Github = useGithub
|
||||
|
||||
methods =
|
||||
[ Email,
|
||||
UsernameAndPassword,
|
||||
@ -39,6 +47,6 @@ useAuth = do
|
||||
|
||||
selectMethod =
|
||||
Interactive.askToChoose
|
||||
"What authentication method do you want to use?"
|
||||
"Which authentication method do you want to use?"
|
||||
(fromList methods)
|
||||
Interactive.ChooserConfig {Interactive.hasDefaultOption = False}
|
||||
|
60
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Common.hs
Normal file
60
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Common.hs
Normal file
@ -0,0 +1,60 @@
|
||||
module Wasp.Cli.Command.UseRecipe.Auth.Common where
|
||||
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import StrongPath (Abs, Dir, Path', Rel, reldir, relfile, (</>))
|
||||
import Wasp.Cli.Command (Command, require)
|
||||
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject))
|
||||
import Wasp.Cli.Command.UseRecipe.Common (RecipesDir, copyFileIfDoesNotExist)
|
||||
import Wasp.Cli.Common (WaspProjectDir)
|
||||
|
||||
data AuthRecipeDir
|
||||
|
||||
authDirInRecipesDir :: Path' (Rel RecipesDir) (Dir AuthRecipeDir)
|
||||
authDirInRecipesDir = [reldir|auth|]
|
||||
|
||||
copyLoginPage :: Command ()
|
||||
copyLoginPage = do
|
||||
authTargetDir <- getAuthTargetDir
|
||||
liftIO $ copyFileIfDoesNotExist (authDirInRecipesDir </> loginPagePath) (authTargetDir </> loginPagePath)
|
||||
where
|
||||
loginPagePath = [relfile|LoginPage.jsx|]
|
||||
|
||||
copySignupPage :: Command ()
|
||||
copySignupPage = do
|
||||
authTargetDir <- getAuthTargetDir
|
||||
liftIO $ copyFileIfDoesNotExist (authDirInRecipesDir </> signupPagePath) (authTargetDir </> signupPagePath)
|
||||
where
|
||||
signupPagePath = [relfile|SignupPage.jsx|]
|
||||
|
||||
copyRequestPasswordResetPage :: Command ()
|
||||
copyRequestPasswordResetPage = do
|
||||
authTargetDir <- getAuthTargetDir
|
||||
liftIO $ copyFileIfDoesNotExist (authDirInRecipesDir </> requestPasswordResetPagePath) (authTargetDir </> requestPasswordResetPagePath)
|
||||
where
|
||||
requestPasswordResetPagePath = [relfile|RequestPasswordResetPage.jsx|]
|
||||
|
||||
copyResetPasswordPage :: Command ()
|
||||
copyResetPasswordPage = do
|
||||
authTargetDir <- getAuthTargetDir
|
||||
liftIO $ copyFileIfDoesNotExist (authDirInRecipesDir </> resetPasswordPagePath) (authTargetDir </> resetPasswordPagePath)
|
||||
where
|
||||
resetPasswordPagePath = [relfile|ResetPasswordPage.jsx|]
|
||||
|
||||
copyEmailVerificationPage :: Command ()
|
||||
copyEmailVerificationPage = do
|
||||
authTargetDir <- getAuthTargetDir
|
||||
liftIO $ copyFileIfDoesNotExist (authDirInRecipesDir </> emailVerificationPagePath) (authTargetDir </> emailVerificationPagePath)
|
||||
where
|
||||
emailVerificationPagePath = [relfile|EmailVerificationPage.jsx|]
|
||||
|
||||
showRouteAndPageDSL :: String -> String -> String -> String
|
||||
showRouteAndPageDSL routeName path pageName = unlines [route, page]
|
||||
where
|
||||
route = "route " <> routeName <> " { path: \"" <> path <> "\", to: " <> pageName <> " }"
|
||||
page = "page " <> pageName <> " { component: import { " <> pageName <> " } from \"@client/auth/" <> pageName <> ".jsx\" }"
|
||||
|
||||
getAuthTargetDir :: Command (Path' Abs (Dir WaspProjectDir))
|
||||
getAuthTargetDir = do
|
||||
InWaspProject waspProjectDir <- require
|
||||
let authTargetDir = waspProjectDir </> [reldir|src/client/auth|]
|
||||
return authTargetDir
|
64
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Email.hs
Normal file
64
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Email.hs
Normal file
@ -0,0 +1,64 @@
|
||||
module Wasp.Cli.Command.UseRecipe.Auth.Email where
|
||||
|
||||
import Wasp.Cli.Command
|
||||
import Wasp.Cli.Command.Message
|
||||
import Wasp.Cli.Command.UseRecipe.Auth.Common (copyEmailVerificationPage, copyLoginPage, copyRequestPasswordResetPage, copyResetPasswordPage, copySignupPage, showRouteAndPageDSL)
|
||||
import Wasp.Cli.Command.UseRecipe.EmailSender (useEmailSender)
|
||||
import qualified Wasp.Message as Msg
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
|
||||
useEmail :: Command ()
|
||||
useEmail = do
|
||||
useEmailSender
|
||||
|
||||
copyLoginPage
|
||||
copySignupPage
|
||||
copyRequestPasswordResetPage
|
||||
copyResetPasswordPage
|
||||
copyEmailVerificationPage
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following entity definition to your main.wasp file:\n"
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "entity User {=psl",
|
||||
" id Int @id @default(autoincrement())",
|
||||
" email String? @unique",
|
||||
" password String?",
|
||||
" isEmailVerified Boolean @default(false)",
|
||||
" emailVerificationSentAt DateTime?",
|
||||
" passwordResetSentAt DateTime?",
|
||||
"psl=}"
|
||||
]
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following auth block to the app block in your main.wasp file:\n"
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "auth: {",
|
||||
" userEntity: User,",
|
||||
" methods: {",
|
||||
" email: {",
|
||||
" fromField: {",
|
||||
" name: \"My App\",",
|
||||
" email: \"myapp@domain.com\"",
|
||||
" },",
|
||||
" emailVerification: {",
|
||||
" clientRoute: EmailVerificationRoute,",
|
||||
" },",
|
||||
" passwordReset: {",
|
||||
" clientRoute: ResetPasswordRoute",
|
||||
" },",
|
||||
" allowUnverifiedLogin: false,",
|
||||
" },",
|
||||
" },",
|
||||
" onAuthFailedRedirectTo: \"/login\"",
|
||||
"}"
|
||||
]
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following routes and pages to your main.wasp file:\n"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "LoginRoute" "/login" "LoginPage"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "SignupRoute" "/signup" "SignupPage"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "RequestPasswordResetRoute" "/request-password-reset" "RequestPasswordResetPage"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "ResetPasswordRoute" "/reset-password" "ResetPasswordPage"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "EmailVerificationRoute" "/email-verification" "EmailVerificationPage"
|
40
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Local.hs
Normal file
40
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Local.hs
Normal file
@ -0,0 +1,40 @@
|
||||
module Wasp.Cli.Command.UseRecipe.Auth.Local where
|
||||
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command.Message (cliSendMessageC)
|
||||
import Wasp.Cli.Command.UseRecipe.Auth.Common (copyLoginPage, copySignupPage, showRouteAndPageDSL)
|
||||
import qualified Wasp.Message as Msg
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
|
||||
useLocal :: Command ()
|
||||
useLocal = do
|
||||
copyLoginPage
|
||||
copySignupPage
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following entity definition to your main.wasp file:\n"
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "entity User {=psl",
|
||||
" id Int @id @default(autoincrement())",
|
||||
" username String @unique",
|
||||
" password String",
|
||||
"psl=}"
|
||||
]
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following auth block to the app block in your main.wasp file:\n"
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "auth: {",
|
||||
" userEntity: User,",
|
||||
" methods: {",
|
||||
" usernameAndPassword: {},",
|
||||
" },",
|
||||
" onAuthFailedRedirectTo: \"/login\"",
|
||||
"}"
|
||||
]
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following routes and pages to your main.wasp file:\n"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "LoginRoute" "/login" "LoginPage"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "SignupRoute" "/signup" "SignupPage"
|
79
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Social.hs
Normal file
79
waspc/cli/src/Wasp/Cli/Command/UseRecipe/Auth/Social.hs
Normal file
@ -0,0 +1,79 @@
|
||||
module Wasp.Cli.Command.UseRecipe.Auth.Social where
|
||||
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command.Message (cliSendMessageC)
|
||||
import Wasp.Cli.Command.UseRecipe.Auth.Common (copyLoginPage, showRouteAndPageDSL)
|
||||
import Wasp.Cli.Command.UseRecipe.Common (appendToServerEnv)
|
||||
import qualified Wasp.Message as Msg
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
|
||||
useGoogle :: Command ()
|
||||
useGoogle = do
|
||||
copyLoginPage
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following entities to your main.wasp file:\n"
|
||||
printSocialLoginEntities
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following auth block to the app block in your main.wasp file:\n"
|
||||
printSocialLoginAuthBlock "google"
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following routes and pages to your main.wasp file:\n"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "LoginRoute" "/login" "LoginPage"
|
||||
|
||||
appendToServerEnv $ unlines ["GOOGLE_CLIENT_ID=\"\"", "GOOGLE_CLIENT_SECRET=\"\""]
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Fill the values for GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in .env.server file."
|
||||
|
||||
useGithub :: Command ()
|
||||
useGithub = do
|
||||
copyLoginPage
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following entities to your main.wasp file:\n"
|
||||
printSocialLoginEntities
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following auth block to the app block in your main.wasp file:\n"
|
||||
printSocialLoginAuthBlock "github"
|
||||
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following routes and pages to your main.wasp file:\n"
|
||||
cliSendMessageC $ Msg.Info $ showRouteAndPageDSL "LoginRoute" "/login" "LoginPage"
|
||||
|
||||
appendToServerEnv $ unlines ["GITHUB_CLIENT_ID=\"\"", "GITHUB_CLIENT_SECRET=\"\""]
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Fill the values for GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET in .env.server file."
|
||||
|
||||
printSocialLoginEntities :: Command ()
|
||||
printSocialLoginEntities = do
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "entity User {=psl",
|
||||
" id Int @id @default(autoincrement())",
|
||||
" externalAuthAssociations SocialLogin[]",
|
||||
"psl=}"
|
||||
]
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "entity SocialLogin {=psl",
|
||||
" id Int @id @default(autoincrement())",
|
||||
" provider String",
|
||||
" providerId String",
|
||||
" user User @relation(fields: [userId], references: [id], onDelete: Cascade)",
|
||||
" userId Int",
|
||||
" createdAt DateTime @default(now())",
|
||||
" @@unique([provider, providerId, userId])",
|
||||
"psl=}"
|
||||
]
|
||||
|
||||
printSocialLoginAuthBlock :: String -> Command ()
|
||||
printSocialLoginAuthBlock provider = do
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "auth: {",
|
||||
" userEntity: User,",
|
||||
" externalAuthEntity: SocialLogin,",
|
||||
" methods: {",
|
||||
" " <> provider <> ": {},",
|
||||
" },",
|
||||
" onAuthFailedRedirectTo: \"/login\"",
|
||||
"}"
|
||||
]
|
@ -1,10 +1,14 @@
|
||||
module Wasp.Cli.Command.UseRecipe.Common where
|
||||
|
||||
import Control.Monad (unless)
|
||||
import StrongPath (Abs, Dir, File, Path', Rel, reldir, toFilePath, (</>))
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import StrongPath (Abs, Dir, File, Path', Rel, reldir, relfile, toFilePath, (</>))
|
||||
import System.Directory (copyFile, createDirectoryIfMissing, doesFileExist)
|
||||
import System.FilePath (takeDirectory)
|
||||
import Wasp.Cli.Command (Command, require)
|
||||
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject))
|
||||
import qualified Wasp.Data
|
||||
import Wasp.Util.IO (appendToFile)
|
||||
|
||||
data RecipesDir
|
||||
|
||||
@ -24,9 +28,14 @@ copyFileIfDoesNotExist pathInRecipesDir pathInProjectDir = do
|
||||
let pathInProjectDirStr = toFilePath pathInProjectDir
|
||||
let pathInRecipesDirStr = toFilePath $ recipesDir </> pathInRecipesDir
|
||||
|
||||
-- _ <- error $ "Copying " <> pathInRecipesDirStr <> " to " <> pathInProjectDirStr
|
||||
|
||||
isExistingFile <- doesFileExist pathInProjectDirStr
|
||||
unless isExistingFile $ do
|
||||
createDirectoryIfMissing True $ takeDirectory pathInProjectDirStr
|
||||
copyFile pathInRecipesDirStr pathInProjectDirStr
|
||||
|
||||
appendToServerEnv :: String -> Command ()
|
||||
appendToServerEnv content = do
|
||||
InWaspProject waspProjectDir <- require
|
||||
|
||||
let serverEnvPath = waspProjectDir </> [relfile|.env.server|]
|
||||
liftIO $ appendToFile serverEnvPath content
|
||||
|
66
waspc/cli/src/Wasp/Cli/Command/UseRecipe/EmailSender.hs
Normal file
66
waspc/cli/src/Wasp/Cli/Command/UseRecipe/EmailSender.hs
Normal file
@ -0,0 +1,66 @@
|
||||
module Wasp.Cli.Command.UseRecipe.EmailSender where
|
||||
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Data.List.NonEmpty (fromList)
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command.Message (cliSendMessageC)
|
||||
import Wasp.Cli.Command.UseRecipe.Common (appendToServerEnv)
|
||||
import qualified Wasp.Cli.Interactive as Interactive
|
||||
import qualified Wasp.Message as Msg
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
|
||||
data EmailProvider = SMTP | SendGrid | Mailgun
|
||||
|
||||
instance Show EmailProvider where
|
||||
show SMTP = "SMTP"
|
||||
show SendGrid = "SendGrid"
|
||||
show Mailgun = "Mailgun"
|
||||
|
||||
instance Interactive.Option EmailProvider where
|
||||
showOption = show
|
||||
|
||||
showOptionDescription _ = Nothing
|
||||
|
||||
useEmailSender :: Command ()
|
||||
useEmailSender = do
|
||||
provider <- liftIO selectProvider
|
||||
|
||||
cliSendMessageC $ Msg.Start $ "Setting up " <> show provider <> " email sender..."
|
||||
|
||||
useProvider provider
|
||||
where
|
||||
providers =
|
||||
[ SMTP,
|
||||
SendGrid,
|
||||
Mailgun
|
||||
]
|
||||
selectProvider =
|
||||
Interactive.askToChoose
|
||||
"Which email sender do you want to use?"
|
||||
(fromList providers)
|
||||
Interactive.ChooserConfig {Interactive.hasDefaultOption = False}
|
||||
|
||||
useProvider :: EmailProvider -> Command ()
|
||||
useProvider SMTP = useEmailProvider "SMTP" ["SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD"]
|
||||
useProvider SendGrid = useEmailProvider "SendGrid" ["SENDGRID_API_KEY"]
|
||||
useProvider Mailgun = useEmailProvider "Mailgun" ["MAILGUN_API_KEY", "MAILGUN_DOMAIN"]
|
||||
|
||||
useEmailProvider :: String -> [String] -> Command ()
|
||||
useEmailProvider providerName providerEnvVariableNames = do
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] "Add the following email sender block to the app block in your main.wasp file:\n"
|
||||
cliSendMessageC $
|
||||
Msg.Info $
|
||||
unlines
|
||||
[ "emailSender: {",
|
||||
" provider: " <> providerName,
|
||||
"}"
|
||||
]
|
||||
|
||||
appendToServerEnv $ unlines $ map (<> "=\"\"") providerEnvVariableNames
|
||||
cliSendMessageC $ Msg.Info $ Term.applyStyles [Term.Bold] $ "Fill the values for " <> joinForSentence providerEnvVariableNames <> " in .env.server file."
|
||||
|
||||
joinForSentence :: [String] -> String
|
||||
joinForSentence [] = ""
|
||||
joinForSentence [x] = x
|
||||
joinForSentence [x, y] = x <> " and " <> y
|
||||
joinForSentence (x : xs) = x <> ", " <> joinForSentence xs
|
@ -2,21 +2,20 @@ module Wasp.Cli.Command.UseRecipe.Tailwind where
|
||||
|
||||
import Control.Monad.Cont (MonadIO (liftIO))
|
||||
import StrongPath
|
||||
( Abs,
|
||||
Dir,
|
||||
( Dir,
|
||||
Path',
|
||||
Rel,
|
||||
reldir,
|
||||
relfile,
|
||||
(</>),
|
||||
)
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command (Command, require)
|
||||
import Wasp.Cli.Command.Message (cliSendMessageC)
|
||||
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject))
|
||||
import Wasp.Cli.Command.UseRecipe.Common
|
||||
( RecipesDir,
|
||||
copyFileIfDoesNotExist,
|
||||
)
|
||||
import Wasp.Cli.Common (WaspProjectDir)
|
||||
import qualified Wasp.Message as Msg
|
||||
|
||||
data TailwindDir
|
||||
@ -24,8 +23,11 @@ data TailwindDir
|
||||
tailwindDirInRecipesDir :: Path' (Rel RecipesDir) (Dir TailwindDir)
|
||||
tailwindDirInRecipesDir = [reldir|tailwind|]
|
||||
|
||||
useTailwind :: Path' Abs (Dir WaspProjectDir) -> Command ()
|
||||
useTailwind waspProjectDir = do
|
||||
useTailwind :: Command ()
|
||||
useTailwind = do
|
||||
InWaspProject waspProjectDir <- require
|
||||
let tailwindTargetDir = waspProjectDir
|
||||
|
||||
cliSendMessageC $ Msg.Start "Setting up Tailwind config files..."
|
||||
|
||||
liftIO $ do
|
||||
@ -44,7 +46,5 @@ useTailwind waspProjectDir = do
|
||||
"You can now use Tailwind classes in your CSS files."
|
||||
]
|
||||
where
|
||||
tailwindTargetDir = waspProjectDir
|
||||
|
||||
tailwindConfigPath = [relfile|tailwind.config.cjs|]
|
||||
postcssConfigPath = [relfile|postcss.config.cjs|]
|
||||
|
5
waspc/data/Cli/recipes/auth/EmailVerificationPage.jsx
Normal file
5
waspc/data/Cli/recipes/auth/EmailVerificationPage.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { VerifyEmailForm } from "@wasp/auth/forms/VerifyEmail";
|
||||
|
||||
export function EmailVerificationPage() {
|
||||
return <VerifyEmailForm />;
|
||||
}
|
5
waspc/data/Cli/recipes/auth/LoginPage.jsx
Normal file
5
waspc/data/Cli/recipes/auth/LoginPage.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { LoginForm } from "@wasp/auth/forms/Login";
|
||||
|
||||
export function LoginPage() {
|
||||
return <LoginForm />;
|
||||
}
|
5
waspc/data/Cli/recipes/auth/RequestPasswordResetPage.jsx
Normal file
5
waspc/data/Cli/recipes/auth/RequestPasswordResetPage.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { ForgotPasswordForm } from "@wasp/auth/forms/ForgotPassword";
|
||||
|
||||
export function RequestPasswordResetPage() {
|
||||
return <ForgotPasswordForm />;
|
||||
}
|
5
waspc/data/Cli/recipes/auth/ResetPasswordPage.jsx
Normal file
5
waspc/data/Cli/recipes/auth/ResetPasswordPage.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { ResetPasswordForm } from "@wasp/auth/forms/ResetPassword";
|
||||
|
||||
export function ResetPasswordPage() {
|
||||
return <ResetPasswordForm />;
|
||||
}
|
5
waspc/data/Cli/recipes/auth/SignupPage.jsx
Normal file
5
waspc/data/Cli/recipes/auth/SignupPage.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { SignupForm } from "@wasp/auth/forms/Signup";
|
||||
|
||||
export function SignupPage() {
|
||||
return <SignupForm />;
|
||||
}
|
@ -12,6 +12,7 @@ module Wasp.Util.IO
|
||||
removeFile,
|
||||
isDirectoryEmpty,
|
||||
writeFileFromText,
|
||||
appendToFile,
|
||||
)
|
||||
where
|
||||
|
||||
@ -111,3 +112,6 @@ isDirectoryEmpty :: Path' Abs (Dir d) -> IO Bool
|
||||
isDirectoryEmpty dirPath = do
|
||||
(files, dirs) <- listDirectory dirPath
|
||||
return $ null files && null dirs
|
||||
|
||||
appendToFile :: Path' Abs (File f) -> String -> IO ()
|
||||
appendToFile = P.appendFile . SP.fromAbsFile
|
||||
|
@ -480,6 +480,11 @@ library cli-lib
|
||||
Wasp.Cli.Command.UseRecipe.Auth
|
||||
Wasp.Cli.Command.UseRecipe.Tailwind
|
||||
Wasp.Cli.Command.UseRecipe.Common
|
||||
Wasp.Cli.Command.UseRecipe.Auth.Common
|
||||
Wasp.Cli.Command.UseRecipe.Auth.Email
|
||||
Wasp.Cli.Command.UseRecipe.Auth.Local
|
||||
Wasp.Cli.Command.UseRecipe.Auth.Social
|
||||
Wasp.Cli.Command.UseRecipe.EmailSender
|
||||
Wasp.Cli.Common
|
||||
Wasp.Cli.Terminal
|
||||
Wasp.Cli.Command.Message
|
||||
|
Loading…
Reference in New Issue
Block a user