mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-09-11 15:26:19 +03:00
feature: Makes CLI output easier to see success and failure (#418)
Closes #212
This commit is contained in:
parent
86441c8cee
commit
cdee0ff0d3
@ -9,6 +9,7 @@ where
|
||||
|
||||
import Control.Monad.Except (ExceptT, MonadError, runExceptT)
|
||||
import Control.Monad.IO.Class (MonadIO)
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
|
||||
newtype Command a = Command {_runCommand :: ExceptT CommandError IO a}
|
||||
deriving (Functor, Applicative, Monad, MonadIO, MonadError CommandError)
|
||||
@ -17,7 +18,7 @@ runCommand :: Command a -> IO ()
|
||||
runCommand cmd = do
|
||||
errorOrResult <- runExceptT $ _runCommand cmd
|
||||
case errorOrResult of
|
||||
Left cmdError -> putStrLn $ "Error: " ++ _errorMsg cmdError
|
||||
Left cmdError -> putStrLn $ Term.applyStyles [Term.Red] (_errorMsg cmdError)
|
||||
Right _ -> return ()
|
||||
|
||||
-- TODO: What if we want to recognize errors in order to handle them?
|
||||
|
@ -16,9 +16,11 @@ import Wasp.Cli.Command (Command, CommandError (..))
|
||||
import Wasp.Cli.Command.Common
|
||||
( alphaWarningMessage,
|
||||
findWaspProjectRootDirFromCwd,
|
||||
waspSaysC,
|
||||
)
|
||||
import Wasp.Cli.Command.Compile (compileIOWithOptions)
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage)
|
||||
import Wasp.CompileOptions (CompileOptions (..))
|
||||
import qualified Wasp.Lib
|
||||
|
||||
@ -31,18 +33,17 @@ build = do
|
||||
buildDirFilePath = SP.fromAbsDir buildDir
|
||||
|
||||
doesBuildDirExist <- liftIO $ doesDirectoryExist buildDirFilePath
|
||||
when doesBuildDirExist $
|
||||
liftIO $ do
|
||||
putStrLn "Clearing the content of the .wasp/build directory..."
|
||||
removeDirectoryRecursive buildDirFilePath
|
||||
putStrLn "Successfully cleared the contents of the .wasp/build directory.\n"
|
||||
when doesBuildDirExist $ do
|
||||
waspSaysC $ asWaspStartMessage "Clearing the content of the .wasp/build directory..."
|
||||
liftIO $ removeDirectoryRecursive buildDirFilePath
|
||||
waspSaysC $ asWaspSuccessMessage "Successfully cleared the contents of the .wasp/build directory."
|
||||
|
||||
liftIO $ putStrLn "Building wasp project..."
|
||||
waspSaysC $ asWaspStartMessage "Building wasp project..."
|
||||
buildResult <- liftIO $ buildIO waspProjectDir buildDir
|
||||
case buildResult of
|
||||
Left compileError -> throwError $ CommandError $ "Build failed: " ++ compileError
|
||||
Right () -> liftIO $ putStrLn "Code has been successfully built! Check it out in .wasp/build directory.\n"
|
||||
liftIO $ putStrLn alphaWarningMessage
|
||||
Left compileError -> throwError $ CommandError $ asWaspFailureMessage "Build failed:" ++ compileError
|
||||
Right () -> waspSaysC $ asWaspSuccessMessage "Code has been successfully built! Check it out in .wasp/build directory."
|
||||
waspSaysC alphaWarningMessage
|
||||
|
||||
buildIO ::
|
||||
Path' Abs (Dir Common.WaspProjectDir) ->
|
||||
|
@ -9,19 +9,19 @@ import System.Directory
|
||||
( doesDirectoryExist,
|
||||
removeDirectoryRecursive,
|
||||
)
|
||||
import System.IO (hFlush, stdout)
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Command.Common (findWaspProjectRootDirFromCwd)
|
||||
import Wasp.Cli.Command.Common (findWaspProjectRootDirFromCwd, waspSaysC)
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspStartMessage, asWaspSuccessMessage)
|
||||
|
||||
clean :: Command ()
|
||||
clean = do
|
||||
waspProjectDir <- findWaspProjectRootDirFromCwd
|
||||
let dotWaspDirFp = SP.toFilePath $ waspProjectDir SP.</> Common.dotWaspDirInWaspProjectDir
|
||||
liftIO $ putStrLn "Deleting .wasp/ directory..." >> hFlush stdout
|
||||
waspSaysC $ asWaspStartMessage "Deleting .wasp/ directory..."
|
||||
doesDotWaspDirExist <- liftIO $ doesDirectoryExist dotWaspDirFp
|
||||
if doesDotWaspDirExist
|
||||
then liftIO $ do
|
||||
removeDirectoryRecursive dotWaspDirFp
|
||||
putStrLn "Deleted .wasp/ directory."
|
||||
else liftIO $ putStrLn "Nothing to delete: .wasp directory does not exist."
|
||||
then do
|
||||
liftIO $ removeDirectoryRecursive dotWaspDirFp
|
||||
waspSaysC $ asWaspSuccessMessage "Deleted .wasp/ directory."
|
||||
else waspSaysC "Nothing to delete: .wasp directory does not exist."
|
||||
|
@ -23,6 +23,7 @@ import Wasp.Cli.Common
|
||||
( dotWaspRootFileInWaspProjectDir,
|
||||
waspSays,
|
||||
)
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage)
|
||||
import Wasp.Common (WaspProjectDir)
|
||||
|
||||
findWaspProjectRoot :: Path' Abs (Dir ()) -> Command (Path' Abs (Dir WaspProjectDir))
|
||||
@ -40,10 +41,11 @@ findWaspProjectRoot currentDir = do
|
||||
findWaspProjectRoot parentDir
|
||||
where
|
||||
notFoundError =
|
||||
CommandError
|
||||
( "Couldn't find wasp project root - make sure"
|
||||
++ " you are running this command from Wasp project."
|
||||
)
|
||||
CommandError $
|
||||
asWaspFailureMessage "Wasp command failed:"
|
||||
++ ( "Couldn't find wasp project root - make sure"
|
||||
++ " you are running this command from a Wasp project."
|
||||
)
|
||||
|
||||
findWaspProjectRootDirFromCwd :: Command (Path' Abs (Dir WaspProjectDir))
|
||||
findWaspProjectRootDirFromCwd = do
|
||||
|
@ -18,6 +18,7 @@ import Wasp.Cli.Command.Db.Migrate
|
||||
copyDbMigrationsDir,
|
||||
)
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage)
|
||||
import Wasp.Common (WaspProjectDir)
|
||||
import Wasp.CompileOptions (CompileOptions (..))
|
||||
import qualified Wasp.Lib
|
||||
@ -29,11 +30,11 @@ compile = do
|
||||
waspProjectDir </> Common.dotWaspDirInWaspProjectDir
|
||||
</> Common.generatedCodeDirInDotWaspDir
|
||||
|
||||
waspSaysC "Compiling wasp code..."
|
||||
waspSaysC $ asWaspStartMessage "Compiling wasp code..."
|
||||
compilationResult <- liftIO $ compileIO waspProjectDir outDir
|
||||
case compilationResult of
|
||||
Left compileError -> throwError $ CommandError $ "Compilation failed: " ++ compileError
|
||||
Right () -> waspSaysC "Code has been successfully compiled, project has been generated.\n"
|
||||
Left compileError -> throwError $ CommandError $ asWaspFailureMessage "Compilation failed:" ++ compileError
|
||||
Right () -> waspSaysC $ asWaspSuccessMessage "Code has been successfully compiled, project has been generated."
|
||||
|
||||
-- | Compiles Wasp source code in waspProjectDir directory and generates a project
|
||||
-- in given outDir directory.
|
||||
|
@ -19,6 +19,7 @@ import Wasp.AppSpec.ExternalCode (SourceExternalCodeDir)
|
||||
import Wasp.Cli.Command (Command, CommandError (..))
|
||||
import qualified Wasp.Cli.Command.Common as Command.Common
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage)
|
||||
import qualified Wasp.Data
|
||||
import Wasp.Lexer (reservedNames)
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
@ -27,9 +28,9 @@ newtype ProjectName = ProjectName {_projectName :: String}
|
||||
|
||||
createNewProject :: String -> Command ()
|
||||
createNewProject (all isLetter -> False) =
|
||||
throwError $ CommandError "Please use only letters for a new project's name."
|
||||
throwError $ CommandError $ asWaspFailureMessage "Project creation failed:" ++ "Please use only letters for a new project's name."
|
||||
createNewProject ((`elem` reservedNames) -> True) =
|
||||
throwError . CommandError $ "Please pick a project name not one of these reserved words:\n\t" ++ intercalate "\n\t" reservedNames
|
||||
throwError . CommandError $ asWaspFailureMessage "Project creation failed:" ++ "Please pick a project name not one of these reserved words:\n\t" ++ intercalate "\n\t" reservedNames
|
||||
createNewProject name = createNewProject' (ProjectName name)
|
||||
|
||||
createNewProject' :: ProjectName -> Command ()
|
||||
@ -38,10 +39,11 @@ createNewProject' (ProjectName projectName) = do
|
||||
waspProjectDir <- case SP.parseAbsDir $ absCwd FP.</> projectName of
|
||||
Left err ->
|
||||
throwError $
|
||||
CommandError
|
||||
( "Failed to parse absolute path to wasp project dir: "
|
||||
++ show err
|
||||
)
|
||||
CommandError $
|
||||
asWaspFailureMessage "Project creation failed:"
|
||||
++ ( "Failed to parse absolute path to wasp project dir: "
|
||||
++ show err
|
||||
)
|
||||
Right sp -> return sp
|
||||
liftIO $ do
|
||||
createDirectorySP waspProjectDir
|
||||
|
@ -14,6 +14,7 @@ import Wasp.Cli.Command (Command, CommandError (..), runCommand)
|
||||
import Wasp.Cli.Command.Common (findWaspProjectRootDirFromCwd, waspSaysC)
|
||||
import Wasp.Cli.Command.Compile (compile)
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage)
|
||||
import Wasp.Generator.DbGenerator.Jobs (runStudio)
|
||||
import Wasp.Generator.Job.IO (readJobMessagesAndPrintThemPrefixed)
|
||||
import Wasp.Generator.ServerGenerator.Setup (setupServer)
|
||||
@ -35,18 +36,18 @@ makeDbCommand cmd = do
|
||||
-- NOTE(matija): First we need make sure the code is generated.
|
||||
compile
|
||||
|
||||
waspSaysC "\nSetting up database..."
|
||||
waspSaysC $ asWaspStartMessage "Setting up database..."
|
||||
chan <- liftIO newChan
|
||||
-- NOTE(matija): What we do here is make sure that Prisma CLI is installed because db commands
|
||||
-- (e.g. migrate) depend on it. We run setupServer which does even more than that, so we could make
|
||||
-- this function more lightweight if needed.
|
||||
(_, dbSetupResult) <- liftIO (concurrently (readJobMessagesAndPrintThemPrefixed chan) (setupServer genProjectDir chan))
|
||||
case dbSetupResult of
|
||||
ExitSuccess -> waspSaysC "\nDatabase successfully set up!" >> cmd
|
||||
exitCode -> throwError $ CommandError $ dbSetupFailedMessage exitCode
|
||||
ExitSuccess -> waspSaysC (asWaspSuccessMessage "Database successfully set up!") >> cmd
|
||||
exitCode -> throwError $ CommandError $ asWaspFailureMessage $ dbSetupFailedMessage exitCode
|
||||
where
|
||||
dbSetupFailedMessage exitCode =
|
||||
"\nDatabase setup failed"
|
||||
"Database setup failed"
|
||||
++ case exitCode of
|
||||
ExitFailure code -> ": " ++ show code
|
||||
_ -> ""
|
||||
@ -59,7 +60,7 @@ studio = do
|
||||
waspProjectDir </> Common.dotWaspDirInWaspProjectDir
|
||||
</> Common.generatedCodeDirInDotWaspDir
|
||||
|
||||
waspSaysC "Running studio..."
|
||||
waspSaysC $ asWaspStartMessage "Running studio..."
|
||||
chan <- liftIO newChan
|
||||
|
||||
_ <- liftIO $ concurrently (readJobMessagesAndPrintThemPrefixed chan) (runStudio genProjectDir chan)
|
||||
|
@ -20,6 +20,7 @@ import Wasp.Cli.Command.Common
|
||||
waspSaysC,
|
||||
)
|
||||
import qualified Wasp.Cli.Common as Cli.Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage)
|
||||
import Wasp.Common (WaspProjectDir, dbMigrationsDirInWaspProjectDir)
|
||||
import Wasp.Generator.Common (ProjectRootDir)
|
||||
import Wasp.Generator.DbGenerator (dbMigrationsDirInDbRootDir, dbRootDirInProjectRootDir)
|
||||
@ -42,27 +43,27 @@ migrateDev = do
|
||||
-- all the latest migrations are in the generated project (e.g. Wasp dev checked out something
|
||||
-- new) - otherwise "dev" would create a new migration for that and we would end up with two
|
||||
-- migrations doing the same thing (which might result in conflict, e.g. during db creation).
|
||||
waspSaysC "Copying migrations folder from Wasp to Prisma project..."
|
||||
waspSaysC $ asWaspStartMessage "Copying migrations folder from Wasp to Prisma project..."
|
||||
copyDbMigrationDir waspProjectDir genProjectRootDir CopyMigDirDown
|
||||
|
||||
waspSaysC "Performing migration..."
|
||||
waspSaysC $ asWaspStartMessage "Performing migration..."
|
||||
migrateResult <- liftIO $ DbOps.migrateDev genProjectRootDir
|
||||
case migrateResult of
|
||||
Left migrateError ->
|
||||
throwError $ CommandError $ "Migrate dev failed: " <> migrateError
|
||||
Right () -> waspSaysC "Migration done."
|
||||
throwError $ CommandError $ asWaspFailureMessage "Migrate dev failed:" ++ migrateError
|
||||
Right () -> waspSaysC $ asWaspSuccessMessage "Migration done."
|
||||
|
||||
waspSaysC "Copying migrations folder from Prisma to Wasp project..."
|
||||
waspSaysC $ asWaspStartMessage "Copying migrations folder from Prisma to Wasp project..."
|
||||
copyDbMigrationDir waspProjectDir genProjectRootDir CopyMigDirUp
|
||||
|
||||
waspSaysC "All done!"
|
||||
waspSaysC $ asWaspSuccessMessage "All done!"
|
||||
where
|
||||
copyDbMigrationDir waspProjectDir genProjectRootDir copyDirection = do
|
||||
copyDbMigDirResult <-
|
||||
liftIO $ copyDbMigrationsDir copyDirection waspProjectDir genProjectRootDir
|
||||
case copyDbMigDirResult of
|
||||
Nothing -> waspSaysC "Done copying migrations folder."
|
||||
Just err -> throwError $ CommandError $ "Copying migration folder failed: " ++ err
|
||||
Nothing -> waspSaysC $ asWaspSuccessMessage "Done copying migrations folder."
|
||||
Just err -> throwError $ CommandError $ asWaspFailureMessage "Copying migration folder failed:" ++ err
|
||||
|
||||
data MigrationDirCopyDirection = CopyMigDirUp | CopyMigDirDown deriving (Eq)
|
||||
|
||||
|
@ -15,6 +15,7 @@ import Wasp.Cli.Command.Common
|
||||
import Wasp.Cli.Command.Compile (compileIO)
|
||||
import Wasp.Cli.Command.Watch (watch)
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage)
|
||||
import qualified Wasp.Lib
|
||||
|
||||
-- | Does initial compile of wasp code and then runs the generated project.
|
||||
@ -24,11 +25,11 @@ start = do
|
||||
waspRoot <- findWaspProjectRootDirFromCwd
|
||||
let outDir = waspRoot </> Common.dotWaspDirInWaspProjectDir </> Common.generatedCodeDirInDotWaspDir
|
||||
|
||||
waspSaysC "Compiling wasp code..."
|
||||
waspSaysC $ asWaspStartMessage "Compiling wasp code..."
|
||||
compilationResult <- liftIO $ compileIO waspRoot outDir
|
||||
case compilationResult of
|
||||
Left compileError -> throwError $ CommandError $ "Compilation failed: " ++ compileError
|
||||
Right () -> waspSaysC "Code has been successfully compiled, project has been generated.\n"
|
||||
Left compileError -> throwError $ CommandError $ asWaspFailureMessage "Compilation failed:" ++ compileError
|
||||
Right () -> waspSaysC $ asWaspSuccessMessage "Code has been successfully compiled, project has been generated."
|
||||
|
||||
-- TODO: Do smart install -> if we need to install stuff, install it, otherwise don't.
|
||||
-- This should be responsibility of Generator, it should tell us how to install stuff.
|
||||
@ -37,17 +38,17 @@ start = do
|
||||
-- Then, next time, we give it data we have about last installation, and it uses that
|
||||
-- to decide if installation needs to happen or not. If it happens, it returnes new data again.
|
||||
-- Right now we have setup/installation being called, but it has not support for being "smart" yet.
|
||||
waspSaysC "Setting up generated project..."
|
||||
waspSaysC $ asWaspStartMessage "Setting up generated project..."
|
||||
setupResult <- liftIO $ Wasp.Lib.setup outDir
|
||||
case setupResult of
|
||||
Left setupError -> throwError $ CommandError $ "\nSetup failed: " ++ setupError
|
||||
Right () -> waspSaysC "\nSetup successful.\n"
|
||||
Left setupError -> throwError $ CommandError $ asWaspFailureMessage "Setup failed:" ++ setupError
|
||||
Right () -> waspSaysC $ asWaspSuccessMessage "Setup successful."
|
||||
|
||||
waspSaysC "\nListening for file changes..."
|
||||
waspSaysC "Starting up generated project..."
|
||||
waspSaysC $ asWaspStartMessage "Listening for file changes..."
|
||||
waspSaysC $ asWaspStartMessage "Starting up generated project..."
|
||||
watchOrStartResult <- liftIO $ race (watch waspRoot outDir) (Wasp.Lib.start outDir)
|
||||
case watchOrStartResult of
|
||||
Left () -> error "This should never happen, listening for file changes should never end but it did."
|
||||
Right startResult -> case startResult of
|
||||
Left startError -> throwError $ CommandError $ "Start failed: " ++ startError
|
||||
Left startError -> throwError $ CommandError $ asWaspFailureMessage "Start failed:" ++ startError
|
||||
Right () -> error "This should never happen, start should never end but it did."
|
||||
|
@ -17,6 +17,7 @@ import Wasp.Cli.Command.Common (waspSaysC)
|
||||
import Wasp.Cli.Command.Telemetry.Common (ensureTelemetryCacheDirExists)
|
||||
import qualified Wasp.Cli.Command.Telemetry.Project as TlmProject
|
||||
import qualified Wasp.Cli.Command.Telemetry.User as TlmUser
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage)
|
||||
|
||||
isTelemetryDisabled :: IO Bool
|
||||
isTelemetryDisabled = isJust <$> ENV.lookupEnv "WASP_TELEMETRY_DISABLE"
|
||||
@ -53,7 +54,7 @@ telemetry = do
|
||||
considerSendingData :: Command.Call.Call -> Command ()
|
||||
considerSendingData cmdCall = (`catchError` const (return ())) $ do
|
||||
telemetryDisabled <- liftIO isTelemetryDisabled
|
||||
when telemetryDisabled $ throwError $ CommandError "Telemetry disabled by user."
|
||||
when telemetryDisabled $ throwError $ CommandError $ asWaspFailureMessage "Telemetry disabled by user."
|
||||
|
||||
telemetryCacheDirPath <- liftIO ensureTelemetryCacheDirExists
|
||||
|
||||
|
@ -16,6 +16,7 @@ import qualified System.FilePath as FP
|
||||
import Wasp.Cli.Command.Compile (compileIO)
|
||||
import Wasp.Cli.Common (waspSays)
|
||||
import qualified Wasp.Cli.Common as Common
|
||||
import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage)
|
||||
import qualified Wasp.Lib
|
||||
|
||||
-- TODO: Another possible problem: on re-generation, wasp re-generates a lot of files, even those that should not
|
||||
@ -78,11 +79,11 @@ watch waspProjectDir outDir = FSN.withManager $ \mgr -> do
|
||||
|
||||
recompile :: IO ()
|
||||
recompile = do
|
||||
waspSays "Recompiling on file change..."
|
||||
waspSays $ asWaspStartMessage "Recompiling on file change..."
|
||||
compilationResult <- compileIO waspProjectDir outDir
|
||||
case compilationResult of
|
||||
Left err -> waspSays $ "Recompilation on file change failed: " ++ err
|
||||
Right () -> waspSays "Recompilation on file change succeeded."
|
||||
Left err -> waspSays $ asWaspFailureMessage "Recompilation on file change failed:" ++ err
|
||||
Right () -> waspSays $ asWaspSuccessMessage "Recompilation on file change succeeded."
|
||||
return ()
|
||||
|
||||
-- TODO: This is a hardcoded approach to ignoring most of the common tmp files that editors
|
||||
|
@ -1,5 +1,9 @@
|
||||
module Wasp.Cli.Terminal
|
||||
( title,
|
||||
asWaspMessage,
|
||||
asWaspStartMessage,
|
||||
asWaspSuccessMessage,
|
||||
asWaspFailureMessage,
|
||||
)
|
||||
where
|
||||
|
||||
@ -7,3 +11,30 @@ import qualified Wasp.Util.Terminal as Term
|
||||
|
||||
title :: String -> String
|
||||
title = Term.applyStyles [Term.Bold]
|
||||
|
||||
asWaspMessage :: String -> String
|
||||
asWaspMessage = waspMessageWithEmoji ""
|
||||
|
||||
asWaspStartMessage :: String -> String
|
||||
asWaspStartMessage = waspMessageWithEmoji "🐝"
|
||||
|
||||
asWaspSuccessMessage :: String -> String
|
||||
asWaspSuccessMessage = waspMessageWithEmoji "✅"
|
||||
|
||||
asWaspFailureMessage :: String -> String
|
||||
-- Add a bit more padding on errors for more pronounced
|
||||
-- visibility and better display of any following error context.
|
||||
asWaspFailureMessage str = concat ["\n", waspMessageWithEmoji "❌" errorStr, "\n"]
|
||||
where
|
||||
errorStr = "[Error] " ++ str
|
||||
|
||||
waspMessageWithEmoji :: String -> String -> String
|
||||
waspMessageWithEmoji emoji message = concat ["\n", prefix, " ", message, " ", suffix, "\n"]
|
||||
where
|
||||
prefix = emoji ++ " ---"
|
||||
prefixAndMessageLength = length prefix + length message
|
||||
idealLength = 80
|
||||
-- Pad suffix until returned message is the ideal length. However, if we have to go
|
||||
-- beyond ideal length due to input length, just use 3 at the end to match the prefix.
|
||||
rightPadLength = max 3 (idealLength - prefixAndMessageLength)
|
||||
suffix = concat (replicate rightPadLength "-")
|
||||
|
Loading…
Reference in New Issue
Block a user