Merge commit resolutions I ommited commiting by accident.

This commit is contained in:
Martin Sosic 2023-06-20 18:32:50 +02:00
parent 8b6fdc7b6e
commit 6d4a39a976
8 changed files with 93 additions and 127 deletions

View File

@ -45,6 +45,10 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
args <- getArgs
let commandCall = case args of
("new" : newArgs) -> Command.Call.New newArgs
-- new-ai-machine is meant to be called and consumed programatically (e.g. by our Wasp AI
-- web app).
["new-ai-machine", projectName, appDescription] ->
Command.Call.NewAiForMachine projectName appDescription
["start"] -> Command.Call.Start
["start", "db"] -> Command.Call.StartDb
["clean"] -> Command.Call.Clean
@ -79,6 +83,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
case commandCall of
Command.Call.New newArgs -> runCommand $ createNewProject newArgs
Command.Call.NewAiForMachine projectName appDescription -> runCommand $ Command.CreateNewProject.AI.createNewProjectForMachine projectName appDescription
Command.Call.Start -> runCommand start
Command.Call.StartDb -> runCommand Command.Start.Db.start
Command.Call.Clean -> runCommand clean

View File

@ -2,6 +2,7 @@ module Wasp.Cli.Command.Call where
data Call
= New Arguments
| NewAiForMachine String String -- projectName, appDescription
| Start
| StartDb
| Clean

View File

@ -1,39 +1,15 @@
module Wasp.Cli.Command.CreateNewProject
( createNewProject,
-- TODO: I just exported whatever I needed, I should think more through how to abstract this really.
createInitialWaspProjectDir,
parseProjectInfo,
ProjectInfo (..),
getAbsoluteWaspProjectDir,
readCoreWaspProjectFiles,
createEmptyWaspProjectDir,
)
where
import Control.Monad (when)
import Control.Monad.Except (throwError)
import Control.Monad.IO.Class (liftIO)
import Data.List (intercalate)
import Data.Text (Text)
import Path.IO (copyDirRecur, doesDirExist)
import StrongPath (Abs, Dir, File', Path, Path', Rel, System, fromAbsDir, parseAbsDir, reldir, relfile, (</>))
import StrongPath.Path (toPathAbsDir)
import System.Directory (createDirectory, 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.Data as Data
import Wasp.Project (WaspProjectDir)
import Wasp.Util (indent, kebabToCamelCase)
import Wasp.Util.IO (readFileStrict)
import qualified Wasp.Util.IO as IOUtil
import Control.Monad.IO.Class (liftIO)
import Data.Function ((&))
import StrongPath (Abs, Dir, Path')
import qualified StrongPath as SP
import Wasp.Cli.Command (Command)
import Wasp.Cli.Command.Call (Arguments)
import Wasp.Cli.Command.CreateNewProject.AI (createNewProjectForHuman)
import Wasp.Cli.Command.CreateNewProject.ArgumentsParser (parseNewProjectArgs)
import Wasp.Cli.Command.CreateNewProject.Common (throwProjectCreationError)
import Wasp.Cli.Command.CreateNewProject.ProjectDescription
@ -51,12 +27,7 @@ import Wasp.Cli.Common (WaspProjectDir)
import qualified Wasp.Message as Msg
import qualified Wasp.Util.Terminal as Term
-- TODO(merge): what about new ai?
-- One for humans we could offer as an option during interactive choosing of template.
-- One for machine should have its own command. Probably `new:ai` would be fine.
-- I have to figure out how to fit all this in with the new changes.
-- It receives all of the arguments that were passed to the `wasp new` command.
-- | It receives all of the arguments that were passed to the `wasp new` command.
createNewProject :: Arguments -> Command ()
createNewProject args = do
newProjectArgs <- parseNewProjectArgs args & either throwProjectCreationError return
@ -80,52 +51,6 @@ createNewProject args = do
putStrLn $ Term.applyStyles [Term.Bold] " wasp start"
{- ORMOLU_ENABLE -}
-- TODO(merge): I think I need this one, do I?
-- | Given project info, creates a new empty wasp app directory with appropriate name and no content
-- in it. Throws if such directory already exists. Returns path to the newly created directory.
createEmptyWaspProjectDir :: ProjectInfo -> Command (Path System Abs (Dir WaspProjectDir))
createEmptyWaspProjectDir projectInfo = do
waspProjectDir <- determineWaspProjectDirAndThrowIfTaken projectInfo
liftIO $ createDirectory $ fromAbsDir waspProjectDir
return waspProjectDir
-- TODO(merge): seems like I need this one for the function above, but check if there
-- is some newer logic that does this.
determineWaspProjectDirAndThrowIfTaken :: ProjectInfo -> Command (Path System Abs (Dir WaspProjectDir))
determineWaspProjectDirAndThrowIfTaken projectInfo = do
absWaspProjectDir <- getAbsoluteWaspProjectDir projectInfo
dirExists <- doesDirExist $ toPathAbsDir absWaspProjectDir
when dirExists $
throwProjectCreationError $ show absWaspProjectDir ++ " is an existing directory"
return absWaspProjectDir
-- TODO(merge): This is now repeating what is in templates/new which is not great.
coreWaspProjectFiles :: [Path System (Rel WaspProjectDir) File']
coreWaspProjectFiles =
[ [relfile|.gitignore|],
[relfile|.wasproot|],
[relfile|src/.waspignore|],
[relfile|src/client/tsconfig.json|],
[relfile|src/client/vite-env.d.ts|],
[relfile|src/server/tsconfig.json|],
[relfile|src/shared/tsconfig.json|]
]
-- TODO(merge): Reorganize Cli/templates/new(or basic) into two dirs:
-- 1. templates/core
-- 2. templates/basic
-- Core would contain only the most neccessary files to get started.
-- Other templates, like basic, would build on top of it.
-- So creating new wasp project from local template would first copy files from "core",
-- then from the actual template (e.g. "basic").
readCoreWaspProjectFiles :: IO [(Path System (Rel WaspProjectDir) File', Text)]
readCoreWaspProjectFiles = do
dataDir <- Data.getAbsDataDirPath
let templatesNewDir = dataDir </> [reldir|Cli/templates/new|]
contents <- mapM (readFileStrict . (templatesNewDir </>)) coreWaspProjectFiles
return $ zip coreWaspProjectFiles contents
=======
createProjectOnDisk :: NewProjectDescription -> Command ()
createProjectOnDisk
NewProjectDescription
@ -140,4 +65,5 @@ createProjectOnDisk
createProjectOnDiskFromRemoteTemplate absWaspProjectDir projectName appName remoteTemplateName
LocalStarterTemplate localTemplateName ->
liftIO $ createProjectOnDiskFromLocalTemplate absWaspProjectDir projectName appName localTemplateName
>>>>>>> main
AiGeneratedStarterTemplate ->
createNewProjectForHuman absWaspProjectDir appName

View File

@ -1,6 +1,3 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE QuasiQuotes #-}
module Wasp.Cli.Command.CreateNewProject.AI
( createNewProjectForHuman,
createNewProjectForMachine,
@ -11,9 +8,9 @@ import Control.Arrow ()
import Control.Monad.Except (MonadError (throwError), MonadIO (liftIO))
import qualified Data.Text as T
import qualified Data.Text.IO as T.IO
import StrongPath (fromAbsDir)
import StrongPath (Abs, Dir, Path', fromAbsDir)
import StrongPath.Operations ()
import System.Directory (createDirectoryIfMissing, setCurrentDirectory)
import System.Directory (createDirectory, createDirectoryIfMissing, setCurrentDirectory)
import System.Environment (lookupEnv)
import System.FilePath (takeDirectory)
import System.IO (hFlush, stdout)
@ -22,25 +19,20 @@ import qualified Wasp.AI.GenerateNewProject as GNP
import Wasp.AI.GenerateNewProject.Common (AuthProvider (..), NewProjectDetails (..))
import Wasp.AI.OpenAI (OpenAIApiKey)
import Wasp.Cli.Command (Command, CommandError (CommandError))
import Wasp.Cli.Command.CreateNewProject (readCoreWaspProjectFiles)
import qualified Wasp.Cli.Command.CreateNewProject as CNP
import Wasp.Cli.Command.CreateNewProject.ProjectDescription (NewProjectAppName (..), parseWaspProjectNameIntoAppName)
import Wasp.Cli.Command.CreateNewProject.StarterTemplates (readCoreWaspProjectFiles)
import Wasp.Cli.Common (WaspProjectDir)
import qualified Wasp.Cli.Interactive as Interactive
createNewProjectForHuman :: Command ()
createNewProjectForHuman = do
createNewProjectForHuman :: Path' Abs (Dir WaspProjectDir) -> NewProjectAppName -> Command ()
createNewProjectForHuman waspProjectDir appName = do
openAIApiKey <- getOpenAIApiKey
-- TODO: Use new fancy logic we have for interactive stuff like this! We need to merge with main though first.
(webAppName, webAppDescription) <- liftIO $ do
putStrLn "App name (e.g. MyFirstApp):"
appName <- getLine
putStrLn "Describe your app in a couple of sentences:"
desc <- getLine
return (appName, desc)
appDescription <- liftIO $ Interactive.askForRequiredInput "Describe your app in a couple of sentences:\n"
projectInfo <- CNP.parseProjectInfo webAppName
absWaspProjectDir <- CNP.createEmptyWaspProjectDir projectInfo
liftIO $ setCurrentDirectory $ fromAbsDir absWaspProjectDir
liftIO $ do
createDirectory $ fromAbsDir waspProjectDir
setCurrentDirectory $ fromAbsDir waspProjectDir
let codeAgentConfig =
CA.CodeAgentConfig
@ -49,7 +41,7 @@ createNewProjectForHuman = do
CA._writeLog = forwardLogToStdout
}
liftIO $ generateNewProject codeAgentConfig webAppName webAppDescription
liftIO $ generateNewProject codeAgentConfig appName appDescription
where
writeFileToDisk path content = do
createDirectoryIfMissing True (takeDirectory path)
@ -62,10 +54,12 @@ createNewProjectForHuman = do
hFlush stdout
createNewProjectForMachine :: String -> String -> Command ()
createNewProjectForMachine webAppName webAppDescription = do
createNewProjectForMachine projectName appDescription = do
openAIApiKey <- getOpenAIApiKey
_projectInfo <- CNP.parseProjectInfo webAppName
appName <- case parseWaspProjectNameIntoAppName projectName of
Right appName -> pure appName
Left err -> throwError $ CommandError "Invalid project name" err
let codeAgentConfig =
CA.CodeAgentConfig
@ -74,7 +68,7 @@ createNewProjectForMachine webAppName webAppDescription = do
CA._writeLog = writeLogToStdoutWithDelimiters
}
liftIO $ generateNewProject codeAgentConfig webAppName webAppDescription
liftIO $ generateNewProject codeAgentConfig appName appDescription
where
writeFileToStdoutWithDelimiters path content =
writeToStdoutWithDelimiters "WRITE FILE" [T.pack path, content]
@ -94,11 +88,11 @@ createNewProjectForMachine webAppName webAppDescription = do
"===/ WASP AI: " <> title <> " ===="
]
generateNewProject :: CA.CodeAgentConfig -> String -> String -> IO ()
generateNewProject codeAgentConfig webAppName webAppDescription = do
generateNewProject :: CA.CodeAgentConfig -> NewProjectAppName -> String -> IO ()
generateNewProject codeAgentConfig (NewProjectAppName appName) appDescription = do
coreWaspProjectFiles <- readCoreWaspProjectFiles
CA.runCodeAgent codeAgentConfig $
GNP.generateNewProject (newProjectDetails webAppName webAppDescription) coreWaspProjectFiles
GNP.generateNewProject (newProjectDetails appName appDescription) coreWaspProjectFiles
getOpenAIApiKey :: Command OpenAIApiKey
getOpenAIApiKey =

View File

@ -3,6 +3,7 @@ module Wasp.Cli.Command.CreateNewProject.ProjectDescription
NewProjectDescription (..),
NewProjectName (..),
NewProjectAppName (..),
parseWaspProjectNameIntoAppName,
)
where
@ -122,23 +123,25 @@ obtainAvailableProjectDirPath projectName = do
"Directory \"" ++ projectDirName ++ "\" is not empty."
mkNewProjectDescription :: String -> Path' Abs (Dir WaspProjectDir) -> StarterTemplateName -> Command NewProjectDescription
mkNewProjectDescription projectName absWaspProjectDir templateName
| isValidWaspIdentifier appName =
return $
NewProjectDescription
{ _projectName = NewProjectName projectName,
_appName = NewProjectAppName appName,
_templateName = templateName,
_absWaspProjectDir = absWaspProjectDir
}
mkNewProjectDescription projectName absWaspProjectDir templateName = do
appName <- either throwProjectCreationError pure $ parseWaspProjectNameIntoAppName projectName
return $
NewProjectDescription
{ _projectName = NewProjectName projectName,
_appName = appName,
_templateName = templateName,
_absWaspProjectDir = absWaspProjectDir
}
parseWaspProjectNameIntoAppName :: String -> Either String NewProjectAppName
parseWaspProjectNameIntoAppName projectName
| isValidWaspIdentifier appName = Right $ NewProjectAppName appName
| otherwise =
throwProjectCreationError $
intercalate
"\n"
[ "The project's name is not in the valid format!",
indent 2 "- It can start with a letter or an underscore.",
indent 2 "- It can contain only letters, numbers, dashes, or underscores.",
indent 2 "- It can't be a Wasp keyword."
]
Left . intercalate "\n" $
[ "The project's name is not in the valid format!",
indent 2 "- It can start with a letter or an underscore.",
indent 2 "- It can contain only letters, numbers, dashes, or underscores.",
indent 2 "- It can't be a Wasp keyword."
]
where
appName = kebabToCamelCase projectName

View File

@ -3,25 +3,36 @@ module Wasp.Cli.Command.CreateNewProject.StarterTemplates
StarterTemplateName (..),
findTemplateNameByString,
defaultStarterTemplateName,
coreWaspProjectFiles,
readCoreWaspProjectFiles,
)
where
import Data.Either (fromRight)
import Data.Foldable (find)
import Data.Text (Text)
import StrongPath (File', Path, Rel, System, reldir, relfile, (</>))
import Wasp.Cli.Command.CreateNewProject.StarterTemplates.Remote.Github (starterTemplateGithubRepo)
import Wasp.Cli.Common (WaspProjectDir)
import qualified Wasp.Cli.GithubRepo as GR
import qualified Wasp.Data as Data
import Wasp.Util.IO (readFileStrict)
data StarterTemplateName = RemoteStarterTemplate String | LocalStarterTemplate String
data StarterTemplateName
= RemoteStarterTemplate String
| LocalStarterTemplate String
| AiGeneratedStarterTemplate
deriving (Eq)
instance Show StarterTemplateName where
show (RemoteStarterTemplate templateName) = templateName
show (LocalStarterTemplate templateName) = templateName
show AiGeneratedStarterTemplate = "ai-generated (experimental)"
getStarterTemplateNames :: IO [StarterTemplateName]
getStarterTemplateNames = do
remoteTemplates <- fromRight [] <$> fetchRemoteStarterTemplateNames
return $ localTemplates ++ remoteTemplates
return $ localTemplates ++ remoteTemplates ++ [AiGeneratedStarterTemplate]
fetchRemoteStarterTemplateNames :: IO (Either String [StarterTemplateName])
fetchRemoteStarterTemplateNames = do
@ -39,3 +50,30 @@ defaultStarterTemplateName = LocalStarterTemplate "basic"
findTemplateNameByString :: [StarterTemplateName] -> String -> Maybe StarterTemplateName
findTemplateNameByString templateNames query = find ((== query) . show) templateNames
-- TODO: This is now repeating what is in templates/basic which is not great.
-- TODO: Reorganize Cli/templates/basic into two dirs:
-- 1. templates/core
-- 2. templates/basic
-- Core would contain only the most neccessary files to get started.
-- Other templates, like basic, would build on top of it.
-- So creating new wasp project from local template would first copy files from "core",
-- then from the actual template (e.g. "basic").
-- Then I wouldn't need this list here, I would just list all the files from Cli/templates/core.
coreWaspProjectFiles :: [Path System (Rel WaspProjectDir) File']
coreWaspProjectFiles =
[ [relfile|.gitignore|],
[relfile|.wasproot|],
[relfile|src/.waspignore|],
[relfile|src/client/tsconfig.json|],
[relfile|src/client/vite-env.d.ts|],
[relfile|src/server/tsconfig.json|],
[relfile|src/shared/tsconfig.json|]
]
readCoreWaspProjectFiles :: IO [(Path System (Rel WaspProjectDir) File', Text)]
readCoreWaspProjectFiles = do
dataDir <- Data.getAbsDataDirPath
let templatesNewDir = dataDir </> [reldir|Cli/templates/basic|]
contents <- mapM (readFileStrict . (templatesNewDir </>)) coreWaspProjectFiles
return $ zip coreWaspProjectFiles contents

View File

@ -75,7 +75,7 @@ generateBaseWaspFile newProjectDetails = ((path, content), planRules)
wasp: {
version: "^${waspVersion}"
},
title: ${appTitle},
title: "${appTitle}",
${appAuth}
}

View File

@ -221,8 +221,7 @@ bytestringToHex :: B.ByteString -> Hex
bytestringToHex = Hex . concatMap (printf "%02x") . B.unpack
hexFromString :: String -> Hex
hexFromString
hexFromString = Hex
hexToString :: Hex -> String
hexToString (Hex s) = s