mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-08-16 18:50:44 +03:00
Add leftovers
This commit is contained in:
parent
d2e5007a0a
commit
bcfc6fe297
@ -150,6 +150,8 @@ export async function generateApp(
|
||||
data: { status: "success" },
|
||||
});
|
||||
} else {
|
||||
console.log('closeano je', code)
|
||||
console.log(JSON.stringify(appGenerationResults[appId]));
|
||||
await Project.update({
|
||||
where: { id: appId },
|
||||
data: { status: "failure" },
|
||||
|
@ -15,7 +15,7 @@ import Wasp.AI.GenerateNewProject.Entity (writeEntitiesToWaspFile)
|
||||
import Wasp.AI.GenerateNewProject.Operation (OperationType (..), generateAndWriteOperation, getOperationJsFilePath)
|
||||
import Wasp.AI.GenerateNewProject.OperationsJsFile (fixOperationsJsFile)
|
||||
import Wasp.AI.GenerateNewProject.Page (generateAndWritePage, getPageComponentPath)
|
||||
import Wasp.AI.GenerateNewProject.PageComponentFile (fixPageComponent)
|
||||
import Wasp.AI.GenerateNewProject.PageComponentFile (fixPageComponent, fixImportsInPageComponent, fixImports)
|
||||
import Wasp.AI.GenerateNewProject.Plan (generatePlan)
|
||||
import qualified Wasp.AI.GenerateNewProject.Plan as Plan
|
||||
import Wasp.AI.GenerateNewProject.Skeleton (generateAndWriteProjectSkeletonAndPresetFiles)
|
||||
@ -31,7 +31,7 @@ generateNewProject ::
|
||||
CodeAgent ()
|
||||
generateNewProject newProjectDetails waspProjectSkeletonFiles = do
|
||||
writeToLog . T.pack $
|
||||
"Generating new wasp project named " <> _projectAppName newProjectDetails <> "!"
|
||||
"Generating a new Wasp project named " <> _projectAppName newProjectDetails <> "!"
|
||||
|
||||
writeToLog "Generating project skeleton..."
|
||||
(waspFilePath, planRules) <-
|
||||
@ -41,7 +41,7 @@ generateNewProject newProjectDetails waspProjectSkeletonFiles = do
|
||||
plan <- generatePlan newProjectDetails planRules
|
||||
|
||||
writeEntitiesToWaspFile waspFilePath (Plan.entities plan)
|
||||
writeToLog "Updated wasp file with entities."
|
||||
writeToLog "Updated the Wasp file with entities."
|
||||
|
||||
writeToLog "Generating actions..."
|
||||
actions <-
|
||||
@ -61,17 +61,23 @@ generateNewProject newProjectDetails waspProjectSkeletonFiles = do
|
||||
-- TODO: Pass plan rules into fixWaspFile, as extra guidance what to keep an eye on? We can't just
|
||||
-- do it blindly though, some of them are relevant only to plan (e.g. not generating login /
|
||||
-- signup page), we would have to do some adapting.
|
||||
writeToLog "Fixing any mistakes in Wasp file..."
|
||||
writeToLog "Fixing mistakes in the Wasp file..."
|
||||
fixWaspFile newProjectDetails waspFilePath plan
|
||||
writeToLog "Wasp file fixed."
|
||||
|
||||
writeToLog "Fixing any mistakes in NodeJS operations files..."
|
||||
writeToLog "Fixing mistakes in NodeJS operation files..."
|
||||
forM_ (nub $ getOperationJsFilePath <$> (queries <> actions)) $ \opFp -> do
|
||||
fixOperationsJsFile newProjectDetails waspFilePath opFp
|
||||
writeToLog $ T.pack $ "Fixed NodeJS operations file '" <> opFp <> "'."
|
||||
writeToLog "NodeJS operations files fixed."
|
||||
writeToLog $ T.pack $ "Fixed NodeJS operation file '" <> opFp <> "'."
|
||||
writeToLog "NodeJS operation files fixed."
|
||||
|
||||
writeToLog "Fixing any mistakes in pages..."
|
||||
writeToLog "Fixing import mistakes in pages..."
|
||||
forM_ (getPageComponentPath <$> pages) $ \pageFp -> do
|
||||
fixImports pageFp queries actions
|
||||
writeToLog $ T.pack $ "Fixed '" <> pageFp <> "' page."
|
||||
writeToLog "Imports in pages fixed."
|
||||
|
||||
writeToLog "Fixing common mistakes in pages..."
|
||||
forM_ (getPageComponentPath <$> pages) $ \pageFp -> do
|
||||
fixPageComponent newProjectDetails waspFilePath pageFp queries actions
|
||||
writeToLog $ T.pack $ "Fixed '" <> pageFp <> "' page."
|
||||
@ -84,4 +90,3 @@ generateNewProject newProjectDetails waspProjectSkeletonFiles = do
|
||||
fromIntegral (promptTokensUsed + completionTokensUsed) / (1000 :: Double)
|
||||
|
||||
writeToLog "Done!"
|
||||
where
|
||||
|
@ -1,14 +1,26 @@
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
||||
|
||||
{-# HLINT ignore "Use fewer imports" #-}
|
||||
|
||||
module Wasp.AI.GenerateNewProject.PageComponentFile
|
||||
( fixPageComponent,
|
||||
fixImportsInPageComponent,
|
||||
operations,
|
||||
queries,
|
||||
actions,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson (FromJSON)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.List (intercalate, isInfixOf, isPrefixOf, partition, stripPrefix)
|
||||
import Data.List.Extra (nub)
|
||||
import Data.Map (Map)
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (fromJust, fromMaybe)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Debug.Trace (trace)
|
||||
import GHC.Generics (Generic)
|
||||
import NeatInterpolation (trimming)
|
||||
import Wasp.AI.CodeAgent (CodeAgent, getFile, writeToFile)
|
||||
@ -19,9 +31,237 @@ import Wasp.AI.GenerateNewProject.Common
|
||||
)
|
||||
import Wasp.AI.GenerateNewProject.Common.Prompts (appDescriptionBlock)
|
||||
import qualified Wasp.AI.GenerateNewProject.Common.Prompts as Prompts
|
||||
import Wasp.AI.GenerateNewProject.Operation (Operation)
|
||||
import Wasp.AI.GenerateNewProject.Operation (Operation (opPlan), OperationImpl (OperationImpl))
|
||||
import qualified Wasp.AI.GenerateNewProject.Operation as Operation
|
||||
import Wasp.AI.GenerateNewProject.Page (getAllPossibleImports, makePageDocPrompt)
|
||||
import Wasp.AI.OpenAI.ChatGPT (ChatMessage (..), ChatRole (..))
|
||||
import qualified Wasp.AI.GenerateNewProject.Plan as Plan
|
||||
import Wasp.AI.OpenAI.ChatGPT (ChatMessage (..), ChatRole (..), Model (GPT_4), _model)
|
||||
import Wasp.Util (trim)
|
||||
import qualified Wasp.AI.CodeAgent as CA
|
||||
import qualified Data.Text.IO as T.IO
|
||||
import System.Directory (createDirectoryIfMissing)
|
||||
import System.FilePath (takeDirectory)
|
||||
import GHC.IO.Handle (hFlush)
|
||||
import System.IO (stdout)
|
||||
|
||||
deletePost :: Operation
|
||||
deletePost = Operation.Operation
|
||||
{ opImpl =
|
||||
OperationImpl
|
||||
{ opWaspDecl = "",
|
||||
opJsImpl = "",
|
||||
opJsImports = Just ""
|
||||
},
|
||||
opPlan =
|
||||
Plan.Operation
|
||||
{ opName = "deletePost",
|
||||
opFnPath = "",
|
||||
opDesc = ""
|
||||
},
|
||||
opType = Operation.Action
|
||||
}
|
||||
|
||||
editPost :: Operation
|
||||
editPost = Operation.Operation
|
||||
{ opImpl =
|
||||
OperationImpl
|
||||
{ opWaspDecl = "",
|
||||
opJsImpl = "",
|
||||
opJsImports = Just ""
|
||||
},
|
||||
opPlan =
|
||||
Plan.Operation
|
||||
{ opName = "editPost",
|
||||
opFnPath = "",
|
||||
opDesc = ""
|
||||
},
|
||||
opType = Operation.Action
|
||||
}
|
||||
|
||||
createPost :: Operation
|
||||
createPost = Operation.Operation
|
||||
{ opImpl =
|
||||
OperationImpl
|
||||
{ opWaspDecl = "",
|
||||
opJsImpl = "",
|
||||
opJsImports = Just ""
|
||||
},
|
||||
opPlan =
|
||||
Plan.Operation
|
||||
{ opName = "createPost",
|
||||
opFnPath = "",
|
||||
opDesc = ""
|
||||
},
|
||||
opType = Operation.Action
|
||||
}
|
||||
|
||||
createComment :: Operation
|
||||
createComment = Operation.Operation
|
||||
{ opImpl =
|
||||
OperationImpl
|
||||
{ opWaspDecl = "",
|
||||
opJsImpl = "",
|
||||
opJsImports = Just ""
|
||||
},
|
||||
opPlan =
|
||||
Plan.Operation
|
||||
{ opName = "createComment",
|
||||
opFnPath = "",
|
||||
opDesc = ""
|
||||
},
|
||||
opType = Operation.Action
|
||||
}
|
||||
|
||||
deleteComment :: Operation
|
||||
deleteComment = Operation.Operation
|
||||
{ opImpl =
|
||||
OperationImpl
|
||||
{ opWaspDecl = "",
|
||||
opJsImpl = "",
|
||||
opJsImports = Just ""
|
||||
},
|
||||
opPlan =
|
||||
Plan.Operation
|
||||
{ opName = "deleteComment",
|
||||
opFnPath = "",
|
||||
opDesc = ""
|
||||
},
|
||||
opType = Operation.Action
|
||||
}
|
||||
|
||||
getPost :: Operation
|
||||
getPost =
|
||||
Operation.Operation
|
||||
{ opImpl =
|
||||
OperationImpl
|
||||
{ opWaspDecl = "",
|
||||
opJsImpl = "",
|
||||
opJsImports = Just ""
|
||||
},
|
||||
opPlan =
|
||||
Plan.Operation
|
||||
{ opName = "getPost",
|
||||
opFnPath = "",
|
||||
opDesc = ""
|
||||
},
|
||||
opType = Operation.Query
|
||||
}
|
||||
|
||||
operations :: [Operation]
|
||||
operations = [deletePost, editPost, createPost, createComment, deleteComment, getPost]
|
||||
|
||||
queries :: [Operation]
|
||||
queries = [getPost]
|
||||
|
||||
actions :: [Operation]
|
||||
actions = [deletePost, editPost, createPost, createComment, deleteComment]
|
||||
|
||||
fixImports :: FilePath -> [Operation] -> [Operation] -> CodeAgent ()
|
||||
fixImports pageComponentPath queries actions = do
|
||||
currentPageComponentContent <- fromMaybe (error "couldn't find page file to fix") <$> getFile pageComponentPath
|
||||
let fixedComponentContent = getComponentWithFixedImports currentPageComponentContent importsForNames
|
||||
writeToFile pageComponentPath (const fixedComponentContent)
|
||||
where
|
||||
importsForNames = getWaspImportsForNames $ queries ++ actions
|
||||
|
||||
getComponentWithFixedImports :: Text -> Map String String -> Text
|
||||
getComponentWithFixedImports pageComponentContent importsForNames = fixedComponentContent
|
||||
where
|
||||
fixedComponentContent = T.intercalate "\n" [nonWaspImportsText, fixedWaspImportsText, remainingCodeText]
|
||||
fixedWaspImportsText = T.pack $ intercalate "\n" fixedWaspImports
|
||||
nonWaspImportsText = T.pack $ intercalate "\n" nonWaspImports
|
||||
remainingCodeText = T.pack $ intercalate "\n" remainingCode
|
||||
fixedWaspImports = map (importsForNames M.!) importedNames
|
||||
importedNames = nub $ concatMap getImportedNames waspImports
|
||||
(waspImports, nonWaspImports, remainingCode) = partitionComponentFile pageComponentContent
|
||||
|
||||
getImportedNames :: String -> [String]
|
||||
getImportedNames importLine = nub $ words $ map removeSpecialCharacters middleOfImportLine
|
||||
where
|
||||
middleOfImportLine =
|
||||
trim $
|
||||
removeSuffix "from" $
|
||||
trim $
|
||||
removePrefix "import " $
|
||||
trim importLineWithoutPath
|
||||
importLineWithoutPath = takeWhile (not . (`elem` ['"', '\''])) importLine
|
||||
removeSpecialCharacters char
|
||||
| char `elem` [',', '}', '{'] = ' '
|
||||
| otherwise = char
|
||||
removePrefix prefix = fromJust . stripPrefix prefix
|
||||
removeSuffix suffix = reverse . removePrefix (reverse suffix) . reverse
|
||||
|
||||
partitionComponentFile :: Text -> ([String], [String], [String])
|
||||
partitionComponentFile componentContent = (waspImportLines, nonWaspImportLines, remainingCodeLines)
|
||||
where
|
||||
(waspImportLines, nonWaspImportLines) = partition ("@wasp" `isInfixOf`) importLines
|
||||
(importLines, remainingCodeLines) = splitContentIntoImportsAndRest $ T.unpack $ T.strip componentContent
|
||||
splitContentIntoImportsAndRest = span (isPrefixOf "import" . trim) . lines
|
||||
|
||||
getWaspImportsForNames :: [Operation] -> M.Map String String
|
||||
getWaspImportsForNames operations = M.fromList $ possibleUnchangingImports ++ map makeOperationImport operations
|
||||
where
|
||||
possibleUnchangingImports :: [(String, String)]
|
||||
possibleUnchangingImports =
|
||||
[ ("logout", "import logout from '@wasp/auth/logout';"),
|
||||
("useAuth", "import useAuth from '@wasp/auth/useAuth';"),
|
||||
("useQuery", "import { useQuery } from '@wasp/queries';"),
|
||||
("useAction", "import { useAction } from '@wasp/actions';")
|
||||
]
|
||||
|
||||
makeOperationImport :: Operation -> (String, String)
|
||||
makeOperationImport operation = (T.unpack opName, T.unpack opImport)
|
||||
where
|
||||
opImport = [trimming|import ${opName} from '@wasp/${opType}/${opName}';|]
|
||||
opName = T.pack $ Plan.opName $ opPlan operation
|
||||
opType = case Operation.opType operation of
|
||||
Operation.Action -> "actions"
|
||||
Operation.Query -> "queries"
|
||||
|
||||
fixImportsOld :: FilePath -> [Operation] -> [Operation] -> CodeAgent ()
|
||||
fixImportsOld pageComponentPath queries actions = do
|
||||
currentPageComponentContent <- fromMaybe (error "couldn't find page file to fix") <$> getFile pageComponentPath
|
||||
trace ("input:" ++ T.unpack currentPageComponentContent) $ return ()
|
||||
let x@(waspImportsText, nonWaspImportsText, remainingCodeText) = partitionComponentFileText currentPageComponentContent
|
||||
trace ("output:" ++ show x) $ return ()
|
||||
fixedWaspImportsText <-
|
||||
queryChatGPTForJSON
|
||||
(defaultChatGPTParamsForFixing {_model = importFixGptModel})
|
||||
[ ChatMessage {role = System, content = Prompts.systemPrompt},
|
||||
ChatMessage {role = User, content = importsPrompt waspImportsText}
|
||||
]
|
||||
let fixedComponentContent = T.intercalate "\n" [nonWaspImportsText, imports fixedWaspImportsText, remainingCodeText]
|
||||
writeToFile pageComponentPath (const fixedComponentContent)
|
||||
where
|
||||
importsPrompt waspImportsText =
|
||||
[trimming|
|
||||
We want to write a list of JavaScript imports. These are the imports we are allowed to choose from:
|
||||
${possibleWaspImports}
|
||||
|
||||
A junior developer wrote these imports:
|
||||
|
||||
```js
|
||||
${waspImportsText}
|
||||
```
|
||||
|
||||
Let's fix the imports by choosing the ones we need from the initial list of valid imports.
|
||||
Only include imports for the symbols the junior developer wanted to use, don't add any redundant imports.
|
||||
|
||||
Please respond ONLY with a valid JSON of the format { imports: string }.
|
||||
There should be no other text in your response. Don't wrap content with the "```" code delimiters.
|
||||
|]
|
||||
possibleWaspImports = T.intercalate "\n" $ map ("- " <>) $ getAllPossibleImports $ queries ++ actions
|
||||
importFixGptModel = GPT_4
|
||||
|
||||
partitionComponentFileText :: Text -> (Text, Text, Text)
|
||||
partitionComponentFileText componentContent = (waspImportsText, nonWaspImportsText, remainingCodeText)
|
||||
where
|
||||
waspImportsText = T.pack $ intercalate "\n" waspImportLines
|
||||
nonWaspImportsText = T.pack $ intercalate "\n" nonWaspImportLines
|
||||
remainingCodeText = T.pack $ intercalate "\n" remainingCodeLines
|
||||
(waspImportLines, nonWaspImportLines) = partition ("@wasp" `isInfixOf`) importLines
|
||||
(importLines, remainingCodeLines) = splitContentIntoImportsAndRest $ T.unpack $ T.strip componentContent
|
||||
splitContentIntoImportsAndRest = span (isPrefixOf "import" . trim) . lines
|
||||
|
||||
fixPageComponent :: NewProjectDetails -> FilePath -> FilePath -> [Operation] -> [Operation] -> CodeAgent ()
|
||||
fixPageComponent newProjectDetails waspFilePath pageComponentPath queries actions = do
|
||||
@ -70,8 +310,7 @@ fixPageComponent newProjectDetails waspFilePath pageComponentPath queries action
|
||||
Fix these by replacing them with actual implementation.
|
||||
- If there are any duplicate imports, make sure to remove them.
|
||||
- There might be some invalid JS or JSX syntax -> fix it if there is any.
|
||||
- All @wasp imports must come from the list below:
|
||||
${possibleImportsList}
|
||||
- Remove redundant imports, but don't change any of the remaining ones.
|
||||
|
||||
With this in mind, generate a new, fixed React component (${pageComponentPathText}).
|
||||
Do actual fixes, don't leave comments with "TODO"!
|
||||
@ -84,8 +323,43 @@ fixPageComponent newProjectDetails waspFilePath pageComponentPath queries action
|
||||
basicWaspLangInfoPrompt = Prompts.basicWaspLangInfo
|
||||
pageComponentPathText = T.pack pageComponentPath
|
||||
pageDocPrompt = makePageDocPrompt $ queries ++ actions
|
||||
|
||||
fixImportsInPageComponent :: FilePath -> [Operation] -> [Operation] -> CodeAgent ()
|
||||
fixImportsInPageComponent pageComponentPath queries actions = do
|
||||
currentPageComponentContent <- fromMaybe (error "couldn't find page file to fix") <$> getFile pageComponentPath
|
||||
fixedPageComponent <-
|
||||
queryChatGPTForJSON
|
||||
defaultChatGPTParamsForFixing
|
||||
[ ChatMessage {role = System, content = Prompts.systemPrompt},
|
||||
ChatMessage {role = User, content = fixImportsInPageComponentPrompt currentPageComponentContent}
|
||||
]
|
||||
writeToFile pageComponentPath (const $ pageComponentImpl fixedPageComponent)
|
||||
where
|
||||
fixImportsInPageComponentPrompt currentPageContent =
|
||||
[trimming|
|
||||
Here is a React component (${pageComponentPathText}) containing some frontend code
|
||||
that we generated together earlier:
|
||||
```js
|
||||
${currentPageContent}
|
||||
```
|
||||
|
||||
Some imports are probably incorrect. Let's fix them.
|
||||
|
||||
Make sure that every Wasp imports (i.e., imports from '@wasp') is one of the following:
|
||||
${possibleImportsList}
|
||||
This is an exhaustive list of all valid Wasp imports.
|
||||
Any Wasp import not found in the list is invalid, fix it by replacing it with one or more appropriate imports from the list above.
|
||||
|]
|
||||
pageComponentPathText = T.pack pageComponentPath
|
||||
possibleImportsList = T.unlines $ getAllPossibleImports $ queries ++ actions
|
||||
|
||||
data Imports = Imports
|
||||
{ imports :: Text
|
||||
}
|
||||
deriving (Generic, Show)
|
||||
|
||||
instance FromJSON Imports
|
||||
|
||||
data PageComponent = PageComponent
|
||||
{ pageComponentImpl :: Text
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user