Skip to content

Commit

Permalink
Merge branch 'filip-ts-sdk' into filip-ts-sdk-setup
Browse files Browse the repository at this point in the history
  • Loading branch information
sodic committed Oct 7, 2024
2 parents 2f9981d + 1699df2 commit 96f7bba
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import StrongPath (Abs, Dir, File, Path')
import Wasp.Cli.Command.CreateNewProject.Common (defaultWaspVersionBounds)
import Wasp.Cli.Command.CreateNewProject.ProjectDescription (NewProjectAppName, NewProjectName)
import Wasp.NodePackageFFI (InstallablePackage (WaspConfigPackage), getPackageInstallationPath)
import Wasp.Project.Analyze (WaspFile (..), findWaspFile)
import Wasp.Project.Analyze (WaspFile (..), WaspFilePath (..), findWaspFile)
import Wasp.Project.Common (WaspProjectDir)
import Wasp.Project.ExternalConfig.PackageJson (findPackageJsonFile)
import qualified Wasp.Util.IO as IOUtil
Expand All @@ -27,9 +27,9 @@ replaceTemplatePlaceholdersInWaspFile ::
NewProjectAppName -> NewProjectName -> Path' Abs (Dir WaspProjectDir) -> IO ()
replaceTemplatePlaceholdersInWaspFile appName projectName projectDir =
findWaspFile projectDir >>= \case
Nothing -> return ()
Just (WaspLang absMainWaspFile) -> replaceTemplatePlaceholders absMainWaspFile
Just (WaspTs absMainTsFile) -> replaceTemplatePlaceholders absMainTsFile
Left _error -> return ()
Right (WaspLang absMainWaspFile) -> replaceTemplatePlaceholders absMainWaspFile
Right (WaspTs absMainTsFile) -> replaceTemplatePlaceholders absMainTsFile
where
replaceTemplatePlaceholders = replaceTemplatePlaceholdersInFileOnDisk appName projectName

Expand Down
5 changes: 2 additions & 3 deletions waspc/src/Wasp/Analyzer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,10 @@ analyze prismaSchemaAst =

getEntityDecls :: Psl.Schema.Schema -> Either [AnalyzeError] [Decl]
getEntityDecls schema =
wrapAnalyzerError TypeError (typeCheck stdTypes ast)
wrapAnalyzerError TypeError (typeCheck stdTypes astWithEntitiesOnly)
>>= (wrapAnalyzerError EvaluationError . evaluate stdTypes)
where
entityStatements = parseEntityStatements schema
ast = Parser.AST entityStatements
astWithEntitiesOnly = Parser.AST $ parseEntityStatements schema

wrapAnalyzerError :: (e -> AnalyzeError) -> Either e a -> Either [AnalyzeError] a
wrapAnalyzerError makeError = left ((: []) . makeError)
Original file line number Diff line number Diff line change
Expand Up @@ -156,33 +156,18 @@ tuple4 eval1 eval2 eval3 eval4 = evaluation $ \(typeDefs, bindings) -> withCtx $
extImport :: TypedExprEvaluation AppSpec.ExtImport.ExtImport
extImport = evaluation' . withCtx $ \ctx -> \case
TypedAST.ExtImport name extImportPath ->
-- NOTE(martin): This parsing here could instead be done in Parser.
-- NOTE(martin): This parsing here could instead be done in Parser.
-- I don't have a very good reason for doing it here instead of Parser, except
-- for being somewhat simpler to implement.
-- So we might want to move it to Parser at some point in the future, if we
-- figure out that is better (it sounds/feels like it could be).
case stripImportPrefix extImportPath of
Just relFileFP -> case SP.parseRelFileP relFileFP of
Left err -> mkParseError ctx $ show err
Right relFileSP -> pure $ AppSpec.ExtImport.ExtImport name relFileSP
Nothing ->
mkParseError
ctx
$ "Path in external import must start with \"" ++ extSrcPrefix ++ "\"!"
case AppSpec.ExtImport.parseExtImportPath extImportPath of
Left err -> mkParseError ctx err
Right importPath -> pure $ AppSpec.ExtImport.ExtImport name importPath
expr -> Left $ ER.mkEvaluationError ctx $ ER.ExpectedType T.ExtImportType (TypedAST.exprType expr)
where
mkParseError ctx msg = Left $ ER.mkEvaluationError ctx $ ER.ParseError $ ER.EvaluationParseError msg
stripImportPrefix importPath = stripPrefix extSrcPrefix importPath
-- Filip: We no longer want separation between client and server code
-- todo (filip): Do we still want to know whic is which. We might (because of the reloading).
-- For now, as we'd like (expect):
-- - Nodemon watches all files in the user's source folder (client files
-- included), but tsc only compiles the server files (I think because it
-- knows that the others aren't used). I am not yet sure how it knows this.
-- - Vite also only triggers on client files. I am not sure how it knows
-- about the difference either.
-- todo (filip): investigate
extSrcPrefix = "@src/"

-- | An evaluation that expects a "JSON".
json :: TypedExprEvaluation AppSpec.JSON.JSON
Expand Down
12 changes: 7 additions & 5 deletions waspc/src/Wasp/AppSpec/ExtImport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ module Wasp.AppSpec.ExtImport
( ExtImport (..),
ExtImportName (..),
importIdentifier,
parseExtImportPath,
)
where

import Control.Arrow (left)
import Data.Aeson (FromJSON (parseJSON), withObject, (.:))
import Data.Aeson.Types (ToJSON)
import Data.Data (Data)
import Data.List (stripPrefix)
import GHC.Generics (Generic)
import StrongPath (File', Path, Posix, Rel, parseRelFileP)
import StrongPath (File', Path, Posix, Rel)
import qualified StrongPath as SP
import Wasp.AppSpec.ExternalFiles (SourceExternalCodeDir)

Expand Down Expand Up @@ -58,13 +60,13 @@ importIdentifier (ExtImport importName _) = case importName of
ExtImportModule n -> n
ExtImportField n -> n

-- TODO: Remove duplication
parseExtImportPath :: String -> Either String ExtImportPath
parseExtImportPath extImportPath = case stripImportPrefix extImportPath of
Just relFileFP -> case SP.parseRelFileP relFileFP of
Left err -> Left $ "Failed to parse relative posix path to file: " ++ show err
Right path' -> Right path'
Nothing -> Left $ "Path in external import must start with \"" ++ extSrcPrefix ++ "\"!"
Just relFileFP ->
left
(("Failed to parse relative posix path to file: " ++) . show)
$ SP.parseRelFileP relFileFP
where
stripImportPrefix importPath = stripPrefix extSrcPrefix importPath
-- Filip: We no longer want separation between client and server code
Expand Down
87 changes: 51 additions & 36 deletions waspc/src/Wasp/Project/Analyze.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@ module Wasp.Project.Analyze
analyzeWaspFileContent,
findWaspFile,
analyzePrismaSchema,
WaspFile (..),
WaspFilePath (..),
)
where

import Control.Applicative ((<|>))
import Control.Arrow (ArrowChoice (left))
import Control.Concurrent (newChan)
import Control.Concurrent.Async (concurrently)
import Control.Monad.Except (ExceptT (..), liftEither, runExceptT)
import qualified Data.Aeson as Aeson
import Data.Conduit.Process.Typed (ExitCode (..))
import Data.List (find, isSuffixOf)
import StrongPath (Abs, Dir, File', Path', Rel, basename, toFilePath, (</>))
import qualified StrongPath as SP
import StrongPath.TH (relfile)
import StrongPath.Types (File)
import StrongPath
( Abs,
Dir,
File,
File',
Path',
Rel,
basename,
castFile,
fromAbsDir,
fromAbsFile,
fromRelFile,
relfile,
(</>),
)
import qualified Wasp.Analyzer as Analyzer
import Wasp.Analyzer.AnalyzeError (getErrorMessageAndCtx)
import Wasp.Analyzer.Parser.Ctx (Ctx)
Expand Down Expand Up @@ -52,7 +62,6 @@ import qualified Wasp.Psl.Ast.Schema as Psl.Schema
import qualified Wasp.Psl.Parser.Schema as Psl.Parser
import Wasp.Psl.Valid (getValidDbSystemFromPrismaSchema)
import qualified Wasp.Psl.Valid as PslV
import Wasp.Util (maybeToEither)
import Wasp.Util.Aeson (encodeToString)
import qualified Wasp.Util.IO as IOUtil
import Wasp.Util.StrongPath (replaceRelExtension)
Expand All @@ -64,7 +73,7 @@ analyzeWaspProject ::
CompileOptions ->
IO (Either [CompileError] AS.AppSpec, [CompileWarning])
analyzeWaspProject waspDir options = do
waspFilePathOrError <- maybeToEither [fileNotFoundMessage] <$> findWaspFile waspDir
waspFilePathOrError <- left (: []) <$> findWaspFile waspDir

case waspFilePathOrError of
Left err -> return (Left err, [])
Expand All @@ -79,10 +88,8 @@ analyzeWaspProject waspDir options = do
EC.analyzeExternalConfigs waspDir >>= \case
Left errors -> return (Left errors, [])
Right externalConfigs -> constructAppSpec waspDir options externalConfigs prismaSchemaAst declarations
where
fileNotFoundMessage = "Couldn't find the *.wasp file in the " ++ toFilePath waspDir ++ " directory"

data WaspFile
data WaspFilePath
= WaspLang !(Path' Abs (File WaspLangFile))
| WaspTs !(Path' Abs (File WaspTsFile))

Expand All @@ -92,9 +99,9 @@ data WaspTsFile

data CompiledWaspJsFile

data DeclsJsonFile
data AppSpecDeclsJsonFile

analyzeWaspFile :: Path' Abs (Dir WaspProjectDir) -> Psl.Schema.Schema -> WaspFile -> IO (Either [CompileError] [AS.Decl])
analyzeWaspFile :: Path' Abs (Dir WaspProjectDir) -> Psl.Schema.Schema -> WaspFilePath -> IO (Either [CompileError] [AS.Decl])
analyzeWaspFile waspDir prismaSchemaAst = \case
WaspLang waspFilePath -> analyzeWaspLangFile prismaSchemaAst waspFilePath
WaspTs waspFilePath -> analyzeWaspTsFile waspDir prismaSchemaAst waspFilePath
Expand All @@ -103,8 +110,8 @@ analyzeWaspTsFile :: Path' Abs (Dir WaspProjectDir) -> Psl.Schema.Schema -> Path
analyzeWaspTsFile waspProjectDir prismaSchemaAst waspFilePath = runExceptT $ do
-- TODO: I'm not yet sure where tsconfig.node.json location should come from
-- because we also need that knowledge when generating a TS SDK project.
compiledWaspJsFile <- ExceptT $ compileWaspTsFile waspProjectDir [relfile|tsconfig.node.json|] waspFilePath
declsJsonFile <- ExceptT $ executeMainWaspJsFile waspProjectDir prismaSchemaAst compiledWaspJsFile
compiledWaspJsFile <- ExceptT $ compileWaspTsFile waspProjectDir [relfile|tsconfig.wasp.json|] waspFilePath
declsJsonFile <- ExceptT $ executeMainWaspJsFileAndGetDeclsFile waspProjectDir prismaSchemaAst compiledWaspJsFile
ExceptT $ readDecls prismaSchemaAst declsJsonFile

compileWaspTsFile ::
Expand All @@ -122,31 +129,31 @@ compileWaspTsFile waspProjectDir tsconfigNodeFileInWaspProjectDir waspFilePath =
"npx"
[ "tsc",
"-p",
toFilePath (waspProjectDir </> tsconfigNodeFileInWaspProjectDir),
fromAbsFile (waspProjectDir </> tsconfigNodeFileInWaspProjectDir),
"--noEmit",
"false",
"--outDir",
toFilePath outDir
fromAbsDir outDir
]
J.Wasp
chan
)
case tscExitCode of
ExitFailure _status -> return $ Left ["Got TypeScript compiler errors for " ++ toFilePath waspFilePath ++ "."]
ExitSuccess -> return $ Right absCompiledWaspJsFile
return $ case tscExitCode of
ExitFailure _status -> Left ["Got TypeScript compiler errors for " ++ fromAbsFile waspFilePath ++ "."]
ExitSuccess -> Right absCompiledWaspJsFile
where
outDir = waspProjectDir </> dotWaspDirInWaspProjectDir
absCompiledWaspJsFile = outDir </> compiledWaspJsFileInDotWaspDir
compiledWaspJsFileInDotWaspDir = SP.castFile $ case replaceRelExtension (basename waspFilePath) ".mjs" of
compiledWaspJsFileInDotWaspDir = castFile $ case replaceRelExtension (basename waspFilePath) ".mjs" of
Just path -> path
Nothing -> error $ "Couldn't calculate the compiled JS file path for " ++ toFilePath waspFilePath ++ "."
Nothing -> error $ "Couldn't calculate the compiled JS file path for " ++ fromAbsFile waspFilePath ++ "."

executeMainWaspJsFile ::
executeMainWaspJsFileAndGetDeclsFile ::
Path' Abs (Dir WaspProjectDir) ->
Psl.Schema.Schema ->
Path' Abs (File CompiledWaspJsFile) ->
IO (Either [CompileError] (Path' Abs (File DeclsJsonFile)))
executeMainWaspJsFile waspProjectDir prismaSchemaAst absCompiledMainWaspJsFile = do
IO (Either [CompileError] (Path' Abs (File AppSpecDeclsJsonFile)))
executeMainWaspJsFileAndGetDeclsFile waspProjectDir prismaSchemaAst absCompiledMainWaspJsFile = do
chan <- newChan
(_, runExitCode) <- do
concurrently
Expand All @@ -160,8 +167,8 @@ executeMainWaspJsFile waspProjectDir prismaSchemaAst absCompiledMainWaspJsFile =
-- Before this, I had the entrypoint file hardcoded, which was bad
-- too: waspProjectDir </> [relfile|node_modules/wasp-config/dist/run.js|]
[ "wasp-config",
SP.fromAbsFile absCompiledMainWaspJsFile,
SP.fromAbsFile absDeclsOutputFile,
fromAbsFile absCompiledMainWaspJsFile,
fromAbsFile absDeclsOutputFile,
encodeToString allowedEntityNames
]
J.Wasp
Expand All @@ -174,10 +181,10 @@ executeMainWaspJsFile waspProjectDir prismaSchemaAst absCompiledMainWaspJsFile =
absDeclsOutputFile = waspProjectDir </> dotWaspDirInWaspProjectDir </> [relfile|decls.json|]
allowedEntityNames = Psl.Schema.getModelNames prismaSchemaAst

readDecls :: Psl.Schema.Schema -> Path' Abs (File DeclsJsonFile) -> IO (Either [CompileError] [AS.Decl])
readDecls :: Psl.Schema.Schema -> Path' Abs (File AppSpecDeclsJsonFile) -> IO (Either [CompileError] [AS.Decl])
readDecls prismaSchemaAst declsJsonFile = runExceptT $ do
entityDecls <- liftEither entityDeclsOrErrors
remainingDecls <- ExceptT declsFromJsonOrError
remainingDecls <- ExceptT $ left (: []) <$> declsFromJsonOrError
return $ entityDecls ++ remainingDecls
where
entityDeclsOrErrors =
Expand All @@ -187,9 +194,9 @@ readDecls prismaSchemaAst declsJsonFile = runExceptT $ do

declsFromJsonOrError = do
declsBytestring <- IOUtil.readFileBytes declsJsonFile
return $ case Aeson.eitherDecode declsBytestring of
Left err -> Left ["Error while parsing the declarations from JSON: " ++ err]
Right value -> Right value
return $
left ("Error while reading the declarations from JSON: " ++) $
Aeson.eitherDecode declsBytestring

analyzeWaspLangFile :: Psl.Schema.Schema -> Path' Abs (File WaspLangFile) -> IO (Either [CompileError] [AS.Decl])
analyzeWaspLangFile prismaSchemaAst waspFilePath = do
Expand Down Expand Up @@ -242,14 +249,22 @@ constructAppSpec waspDir options externalConfigs parsedPrismaSchema decls = do

return $ runValidation ASV.validateAppSpec appSpec

findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe WaspFile)
findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either String WaspFilePath)
findWaspFile waspDir = do
files <- fst <$> IOUtil.listDirectory waspDir
return $ findWaspTsFile files <|> findWaspLangFile files
return $ case (findWaspTsFile files, findWaspLangFile files) of
(Just _, Just _) -> Left bothFilesFoundMessage
(Nothing, Nothing) -> Left fileNotFoundMessage
(Just waspTsFile, Nothing) -> Right waspTsFile
(Nothing, Just waspLangFile) -> Right waspLangFile
where
findWaspTsFile files = WaspTs <$> findFileThatEndsWith ".wasp.mts" files
findWaspLangFile files = WaspLang <$> findFileThatEndsWith ".wasp" files
findFileThatEndsWith suffix files = SP.castFile . (waspDir </>) <$> find ((suffix `isSuffixOf`) . toFilePath) files
findFileThatEndsWith suffix files = castFile . (waspDir </>) <$> find ((suffix `isSuffixOf`) . fromRelFile) files
fileNotFoundMessage = "Couldn't find the *.wasp or a *.wasp.mts file in the " ++ fromAbsDir waspDir ++ " directory"
bothFilesFoundMessage =
"Found both *.wasp and *.wasp.mts files in the project directory. "
++ "You must choose how you want to define your app (using Wasp or TypeScript) and only keep one of them."

analyzePrismaSchema :: Path' Abs (Dir WaspProjectDir) -> IO (Either [CompileError] Psl.Schema.Schema, [CompileWarning])
analyzePrismaSchema waspProjectDir = do
Expand All @@ -268,7 +283,7 @@ analyzePrismaSchema waspProjectDir = do
-- NOTE: linking here to migration docs because I think it's the most common reason why schema.prisma file is missing.
-- After people mostly start using 0.14.0+ they will have schema.prisma file, so this message will be less relevant.
-- If we see that this message is still relevant, we can change it to be more general.
couldntFindPrismaSchemaMessage = "Couldn't find the schema.prisma file in the " ++ toFilePath waspProjectDir ++ " directory. \nRead more: https://wasp-lang.dev/docs/migrate-from-0-13-to-0-14#migrate-to-the-new-schemaprisma-file"
couldntFindPrismaSchemaMessage = "Couldn't find the schema.prisma file in the " ++ fromAbsDir waspProjectDir ++ " directory. \nRead more: https://wasp-lang.dev/docs/migrate-from-0-13-to-0-14#migrate-to-the-new-schemaprisma-file"

runValidation :: (result -> [ValidationError]) -> result -> (Either [CompileError] result, [CompileWarning])
runValidation getErrorsAndWarnings result =
Expand Down
4 changes: 2 additions & 2 deletions waspc/test/AppSpec/FromJSONTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ spec_AppSpecFromJSON = do
}
)
where
extNamedImportJson = [trimming| { "kind": "named", "name" : "foo", "path": "folder/file.js" }|]
extDefaultImportJson = [trimming| { "kind": "default", "name" : "foo", "path": "folder/subfolder/file.js" }|]
extNamedImportJson = [trimming| { "kind": "named", "name" : "foo", "path": "@src/folder/file.js" }|]
extDefaultImportJson = [trimming| { "kind": "default", "name" : "foo", "path": "@src/folder/subfolder/file.js" }|]

fooEntityRef = [trimming| { "name": "foo", "declType": "Entity" }|]
barEntityRef = [trimming| { "name": "bar", "declType": "Entity" }|]
Expand Down

0 comments on commit 96f7bba

Please sign in to comment.