Skip to content

Commit

Permalink
Address some review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
sodic committed Oct 24, 2022
1 parent d88e204 commit 51b426a
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 99 deletions.
54 changes: 28 additions & 26 deletions waspc/cli/src/Wasp/Cli/Command/CreateNewProject.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ where
import Control.Monad.Except (throwError)
import Control.Monad.IO.Class (liftIO)
import Data.List (intercalate)
import StrongPath (Abs, Dir, Path, Path', System, reldir, relfile, (</>))
import qualified StrongPath as SP
import Path.IO (copyDirRecur, doesDirExist)
import StrongPath (Abs, Dir, Path, Path', System, fromAbsFile, parseAbsDir, reldir, relfile, (</>))
import StrongPath.Path (toPathAbsDir)
import System.Directory (getCurrentDirectory)
import qualified System.FilePath as FP
import Text.Printf (printf)
import Wasp.Analyzer.Parser (isValidWaspIdentifier)
import Wasp.Cli.Command (Command, CommandError (..))
import qualified Wasp.Cli.Command.Common as Command.Common
import Wasp.Cli.Common (WaspProjectDir)
import qualified Wasp.Cli.Common as Common
import Wasp.Common (WaspProjectDir)
import qualified Wasp.Common as Common (WaspProjectDir)
import qualified Wasp.Data as Data
import Wasp.Generator.FileDraft.WriteableMonad (WriteableMonad (copyDirectoryRecursive))
import Wasp.Util (indent, kebabToCamelCase)
import qualified Wasp.Util.Terminal as Term

Expand All @@ -29,8 +29,17 @@ data ProjectInfo = ProjectInfo
createNewProject :: String -> Command ()
createNewProject projectName = do
projectInfo <- parseProjectInfo projectName
createAndPopulateWaspProjectDir projectInfo
liftIO $ printInfoMessages projectInfo
createWaspProjectDir projectInfo
liftIO printGettingStartedInstructions
where
printGettingStartedInstructions = do
putStrLn $ Term.applyStyles [Term.Green] ("Created new Wasp app in ./" ++ projectName ++ " directory!")
putStrLn "To run it, do:"
putStrLn ""
putStrLn $ Term.applyStyles [Term.Bold] (" cd " ++ projectName)
putStrLn $ Term.applyStyles [Term.Bold] " wasp start"
putStrLn ""
putStrLn Command.Common.alphaWarningMessage

-- Takes a project name String
-- Returns either the ProjectInfo type that contains both the Project name
Expand All @@ -51,27 +60,20 @@ parseProjectInfo name
where
appName = kebabToCamelCase name

createAndPopulateWaspProjectDir :: ProjectInfo -> Command ()
createAndPopulateWaspProjectDir projectInfo = do
createWaspProjectDir :: ProjectInfo -> Command ()
createWaspProjectDir projectInfo = do
absWaspProjectDir <- getAbsoluteWaspProjectDir projectInfo
liftIO $ do
initializeProjectFromSkeleton absWaspProjectDir
writeMainWaspFile absWaspProjectDir projectInfo

printInfoMessages :: ProjectInfo -> IO ()
printInfoMessages (ProjectInfo projectName _) = do
putStrLn $ Term.applyStyles [Term.Green] ("Created new Wasp app in ./" ++ projectName ++ " directory!")
putStrLn "To run it, do:"
putStrLn ""
putStrLn $ Term.applyStyles [Term.Bold] (" cd " ++ projectName)
putStrLn $ Term.applyStyles [Term.Bold] " wasp start"
putStrLn ""
putStrLn Command.Common.alphaWarningMessage
dirExists <- doesDirExist $ toPathAbsDir absWaspProjectDir
if dirExists
then throwError $ CommandError "Project creation failed" $ show absWaspProjectDir ++ " is an existing directory"
else liftIO $ do
initializeProjectFromSkeleton absWaspProjectDir
writeMainWaspFile absWaspProjectDir projectInfo

getAbsoluteWaspProjectDir :: ProjectInfo -> Command (Path System Abs (Dir WaspProjectDir))
getAbsoluteWaspProjectDir (ProjectInfo projectName _) = do
absCwd <- liftIO getCurrentDirectory
case SP.parseAbsDir $ absCwd FP.</> projectName of
case parseAbsDir $ absCwd FP.</> projectName of
Right sp -> return sp
Left err ->
throwError $
Expand All @@ -83,12 +85,12 @@ initializeProjectFromSkeleton :: Path' Abs (Dir Common.WaspProjectDir) -> IO ()
initializeProjectFromSkeleton absWaspProjectDir = do
dataDir <- Data.getAbsDataDirPath
let absSkeletonDir = dataDir </> [reldir|Cli/templates/new|]
copyDirectoryRecursive absSkeletonDir absWaspProjectDir
copyDirRecur (toPathAbsDir absSkeletonDir) (toPathAbsDir absWaspProjectDir)

writeMainWaspFile :: Path System Abs (Dir WaspProjectDir) -> ProjectInfo -> IO ()
writeMainWaspFile waspProjectDir (ProjectInfo projectName appName) = writeFile mainWaspFileAbsPath mainWaspFileContent
writeMainWaspFile waspProjectDir (ProjectInfo projectName appName) = writeFile absMainWaspFile mainWaspFileContent
where
mainWaspFileAbsPath = SP.fromAbsFile $ waspProjectDir </> [relfile|main.wasp|]
absMainWaspFile = fromAbsFile $ waspProjectDir </> [relfile|main.wasp|]
mainWaspFileContent =
unlines
[ "app %s {" `printf` appName,
Expand Down
6 changes: 3 additions & 3 deletions waspc/cli/src/Wasp/Cli/Command/Deps.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ import Wasp.Cli.Terminal (title)
import qualified Wasp.Generator.NpmDependencies as N
import qualified Wasp.Generator.ServerGenerator as ServerGenerator
import qualified Wasp.Generator.WebAppGenerator as WebAppGenerator
import Wasp.Lib (analyzeWaspProject)
import Wasp.Lib (findAndAnalyzeWaspFile)
import qualified Wasp.Util.Terminal as Term

deps :: Command ()
deps = do
waspProjectDir <- findWaspProjectRootDirFromCwd
appSpecOrAnalyzerErrors <- liftIO $ analyzeWaspProject waspProjectDir (defaultCompileOptions waspProjectDir)
appSpecOrAnalyzerErrors <- liftIO $ findAndAnalyzeWaspFile waspProjectDir (defaultCompileOptions waspProjectDir)
appSpec <-
either
(throwError . CommandError "Determining dependencies failed due to a compilation error in your Wasp project" . unwords . toList)
return
appSpecOrAnalyzerErrors

liftIO . putStrLn $ depsMessage appSpec
liftIO $ putStrLn $ depsMessage appSpec

depsMessage :: AppSpec -> String
depsMessage appSpec =
Expand Down
70 changes: 29 additions & 41 deletions waspc/cli/src/Wasp/Cli/Command/Info.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ module Wasp.Cli.Command.Info
)
where

import Control.Arrow (ArrowChoice (left))
import Control.Monad.IO.Class (liftIO)
import Control.Arrow
import Control.Monad.Except
import StrongPath (Abs, Dir, Path', fromAbsFile, fromRelFile, toFilePath)
import StrongPath.Operations
import System.Directory (doesFileExist, getFileSize)
import qualified Wasp.Analyzer as Analyzer
import qualified Wasp.AppSpec as AS
import qualified Wasp.AppSpec.App as AS.App
import qualified Wasp.AppSpec.Core.Decl as AS (Decl, takeDecls)
import Wasp.Cli.Command (Command)
import Wasp.Cli.Command.Common (findWaspProjectRootDirFromCwd)
import Wasp.Cli.Command.Message (cliSendMessageC)
Expand All @@ -26,30 +26,23 @@ import Wasp.Util.IO (listDirectoryDeep)
import qualified Wasp.Util.Terminal as Term

info :: Command ()
info =
do
waspDir <- findWaspProjectRootDirFromCwd
compileInfo <- liftIO $ readCompileInformation waspDir
projectSize <- liftIO $ readDirectorySizeMB waspDir
declsOrError <- liftIO $ parseWaspFile waspDir
case declsOrError of
Left err -> cliSendMessageC $ Msg.Failure "Info failed" err
Right decls -> do
cliSendMessageC $
Msg.Info $
unlines
[ "",
title "Project information",
printInfo
"Name"
(fst $ head $ AS.takeDecls @AS.App.App decls),
printInfo
"Last compile"
compileInfo,
printInfo
"Project size"
projectSize
]
info = do
waspDir <- findWaspProjectRootDirFromCwd
compileInfo <- liftIO $ readCompileInformation waspDir
projectSize <- liftIO $ readDirectorySizeMB waspDir
declsOrError <- liftIO $ parseWaspFile waspDir
case declsOrError of
Left err -> cliSendMessageC $ Msg.Failure "Info failed" err
Right decls -> do
cliSendMessageC $
Msg.Info $
unlines
[ "",
title "Project information",
printInfo "Name" (fst $ head $ AS.takeDecls @AS.App.App decls),
printInfo "Last compile" compileInfo,
printInfo "Project size" projectSize
]

printInfo :: String -> String -> String
printInfo key value = Term.applyStyles [Term.Cyan] key ++ ": " <> Term.applyStyles [Term.White] value
Expand All @@ -70,17 +63,12 @@ readCompileInformation waspDir = do
else return "No compile information found"

parseWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either String [AS.Decl])
parseWaspFile waspDir = do
maybeWaspFile <- findWaspFile waspDir
case maybeWaspFile of
Nothing -> return (Left "Couldn't find a single *.wasp file.")
Just waspFile ->
do
waspStr <- readFile (toFilePath waspFile)
return $
left
( ("Couldn't parse .wasp file:\n" <>)
. showCompilerErrorForTerminal (waspFile, waspStr)
. Analyzer.getErrorMessageAndCtx
)
$ Analyzer.analyze waspStr
parseWaspFile waspDir = runExceptT $ do
waspFile <- ExceptT $ findWaspFile waspDir
waspStr <- liftIO $ readFile $ toFilePath waspFile
liftEither $ left (annotateErrorForCli waspFile waspStr) $ Analyzer.analyze waspStr
where
annotateErrorForCli waspFile waspStr =
("Couldn't parse .wasp file:\n" ++)
. showCompilerErrorForTerminal (waspFile, waspStr)
. Analyzer.getErrorMessageAndCtx
52 changes: 23 additions & 29 deletions waspc/src/Wasp/Lib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ module Wasp.Lib
Generator.start,
ProjectRootDir,
findWaspFile,
analyzeWaspProject,
findAndAnalyzeWaspFile,
)
where

import Control.Arrow
import Control.Monad.Except
import Data.List (find, isSuffixOf)
import Data.List.NonEmpty (NonEmpty, fromList, toList)
import StrongPath (Abs, Dir, File', Path', relfile)
import qualified StrongPath as SP
import StrongPath (Abs, Dir, File', Path', Rel, fromAbsDir, fromAbsFile, relfile, toFilePath, (</>))
import System.Directory (doesDirectoryExist, doesFileExist)
import qualified Wasp.Analyzer as Analyzer
import Wasp.Analyzer.AnalyzeError (getErrorMessageAndCtx)
Expand All @@ -27,6 +27,7 @@ import qualified Wasp.Generator as Generator
import Wasp.Generator.Common (ProjectRootDir)
import Wasp.Generator.ServerGenerator.Common (dotEnvServer)
import Wasp.Generator.WebAppGenerator.Common (dotEnvClient)
import Wasp.Util (maybeToEither)
import qualified Wasp.Util.IO as Util.IO

type CompileError = String
Expand All @@ -39,8 +40,8 @@ compile ::
CompileOptions ->
IO ([CompileWarning], [CompileError])
compile waspDir outDir options = do
appSpecOrAnalyzerErrors <- analyzeWaspProject waspDir options
analyzerWarnings <- warnIfDotEnvPresent waspDir
appSpecOrAnalyzerErrors <- findAndAnalyzeWaspFile waspDir options
compileWarnings <- warnIfDotEnvPresent waspDir
compilerWarningsAndErrors <- case appSpecOrAnalyzerErrors of
Left analyzerErrors -> return ([], toList analyzerErrors)
Right appSpec ->
Expand All @@ -49,16 +50,16 @@ compile waspDir outDir options = do
(generatorWarnings, generatorErrors) <- Generator.writeWebAppCode appSpec outDir (sendMessage options)
return (map show $ generatorWarningsFilter options generatorWarnings, map show generatorErrors)
validationErrors -> return ([], map show validationErrors)
return $ (analyzerWarnings, []) <> compilerWarningsAndErrors
return $ (compileWarnings, []) <> compilerWarningsAndErrors

analyzeWaspProject ::
findAndAnalyzeWaspFile ::
Path' Abs (Dir WaspProjectDir) ->
CompileOptions ->
IO (Either (NonEmpty CompileError) AppSpec)
analyzeWaspProject waspDir options = runExceptT $ do
waspFilePath <- ExceptT $ findWaspFilePath waspDir
findAndAnalyzeWaspFile waspDir options = runExceptT $ do
waspFilePath <- ExceptT $ left pure <$> findWaspFile waspDir
declarations <- ExceptT $ analyzeWaspFileContent waspFilePath
liftIO $ createAppSpec waspDir options declarations
liftIO $ constructAppSpec waspDir options declarations

-- | Checks the wasp directory for potential problems, and issues warnings if any are found.
warnIfDotEnvPresent :: Path' Abs (Dir WaspProjectDir) -> IO [CompileWarning]
Expand All @@ -68,16 +69,9 @@ warnIfDotEnvPresent waspDir = do
Nothing -> []
Just _ -> ["Wasp .env files should be named .env.server or .env.client, depending on their use."]

findWaspFilePath :: Path' Abs (Dir WaspProjectDir) -> IO (Either (NonEmpty CompileError) (Path' Abs File'))
findWaspFilePath waspDir = do
maybeWaspFilePath <- findWaspFile waspDir
return $ case maybeWaspFilePath of
Nothing -> Left $ fromList ["Couldn't find a single *.wasp file."]
Just waspFilePath -> Right waspFilePath

analyzeWaspFileContent :: Path' Abs File' -> IO (Either (NonEmpty CompileError) [Decl])
analyzeWaspFileContent waspFilePath = do
waspFileContent <- readFile (SP.fromAbsFile waspFilePath)
waspFileContent <- readFile (fromAbsFile waspFilePath)
return $ case Analyzer.analyze waspFileContent of
Right decls -> Right decls
Left analyzeError ->
Expand All @@ -88,8 +82,8 @@ analyzeWaspFileContent waspFilePath = do
(getErrorMessageAndCtx analyzeError)
]

createAppSpec :: Path' Abs (Dir WaspProjectDir) -> CompileOptions -> [Decl] -> IO AS.AppSpec
createAppSpec waspDir options decls = do
constructAppSpec :: Path' Abs (Dir WaspProjectDir) -> CompileOptions -> [Decl] -> IO AS.AppSpec
constructAppSpec waspDir options decls = do
externalServerCodeFiles <-
ExternalCode.readFiles (CompileOptions.externalServerCodeDirPath options)
externalClientCodeFiles <-
Expand All @@ -108,14 +102,14 @@ createAppSpec waspDir options decls = do
AS.isBuild = CompileOptions.isBuild options
}

findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs File'))
findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either String (Path' Abs File'))
findWaspFile waspDir = do
files <- fst <$> Util.IO.listDirectory waspDir
return $ (waspDir SP.</>) <$> find isWaspFile files
return $ maybeToEither "Couldn't find a single *.wasp file." $ (waspDir </>) <$> find isWaspFile files
where
isWaspFile path =
".wasp" `isSuffixOf` SP.toFilePath path
&& (length (SP.toFilePath path) > length (".wasp" :: String))
".wasp" `isSuffixOf` toFilePath path
&& (length (toFilePath path) > length (".wasp" :: String))

findDotEnvServer :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs File'))
findDotEnvServer waspDir = findFileInWaspProjectDir waspDir dotEnvServer
Expand All @@ -128,17 +122,17 @@ findDotEnv waspDir = findFileInWaspProjectDir waspDir [relfile|.env|]

findFileInWaspProjectDir ::
Path' Abs (Dir WaspProjectDir) ->
Path' (SP.Rel WaspProjectDir) File' ->
Path' (Rel WaspProjectDir) File' ->
IO (Maybe (Path' Abs File'))
findFileInWaspProjectDir waspDir file = do
let fileAbsFp = waspDir SP.</> file
fileExists <- doesFileExist $ SP.toFilePath fileAbsFp
let fileAbsFp = waspDir </> file
fileExists <- doesFileExist $ toFilePath fileAbsFp
return $ if fileExists then Just fileAbsFp else Nothing

findMigrationsDir ::
Path' Abs (Dir WaspProjectDir) ->
IO (Maybe (Path' Abs (Dir DbMigrationsDir)))
findMigrationsDir waspDir = do
let migrationsAbsPath = waspDir SP.</> dbMigrationsDirInWaspProjectDir
migrationsExists <- doesDirectoryExist $ SP.fromAbsDir migrationsAbsPath
let migrationsAbsPath = waspDir </> dbMigrationsDirInWaspProjectDir
migrationsExists <- doesDirectoryExist $ fromAbsDir migrationsAbsPath
return $ if migrationsExists then Just migrationsAbsPath else Nothing
6 changes: 6 additions & 0 deletions waspc/src/Wasp/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module Wasp.Util
orIfNothing,
orIfNothingM,
kebabToCamelCase,
maybeToEither
)
where

Expand Down Expand Up @@ -211,3 +212,8 @@ orIfNothing = flip fromMaybe

orIfNothingM :: (Monad m) => m (Maybe a) -> m a -> m a
orIfNothingM = flip fromMaybeM

maybeToEither :: a -> Maybe b -> Either a b
maybeToEither leftValue = \case
Nothing -> Left leftValue
Just rightValue -> Right rightValue

0 comments on commit 51b426a

Please sign in to comment.