Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using TS SDK from waspc #2276

Merged
merged 41 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e228263
Successfully get spec from stdout
sodic Sep 6, 2024
664eafe
Successfully read spec from file
sodic Sep 6, 2024
1b75ab9
Clean up code in Analyze.hs
sodic Sep 10, 2024
0c7b901
Move outDir source of truth into Haskell
sodic Sep 10, 2024
b7c4800
Add extra context to Analyze.hs
sodic Sep 11, 2024
68bfe30
Improve TS SDK analysis
sodic Sep 11, 2024
0758747
Put config dir back in .wasp dir
sodic Sep 11, 2024
b7ecab4
Add more comments
sodic Sep 12, 2024
e2e802e
Update Analyze.hs
sodic Sep 18, 2024
2d27900
Merge branch 'main' into filip-ts-sdk
sodic Sep 25, 2024
987243d
Remove todo for duplication
sodic Sep 25, 2024
15e3e68
Remove the duplication in templating
sodic Sep 25, 2024
7600288
Rearrange analyze.hs
sodic Sep 25, 2024
2b0c0ff
Remove todo after adding it to GitHub
sodic Sep 25, 2024
7ec9356
Remove one more todo
sodic Sep 25, 2024
1597f4b
Remove todo after adding it to GitHub
sodic Sep 25, 2024
b887cd8
Resolve some more todos
sodic Sep 25, 2024
1bdc871
Remove another todo
sodic Sep 25, 2024
52378c4
Send prisma models to JS runtime
sodic Sep 26, 2024
115dce5
Merge branch 'main' into filip-ts-sdk
sodic Sep 26, 2024
2617b6f
Properly parse decls.json
sodic Sep 26, 2024
32275e5
Improve error messages
sodic Sep 26, 2024
b673308
Merge branch 'main' into filip-ts-sdk
sodic Sep 30, 2024
3513b68
Fix ext import parsing and inject entities
sodic Oct 5, 2024
e9ef283
Fail if you find both wasp files
sodic Oct 7, 2024
0dc7435
Change tsconfig file name
sodic Oct 7, 2024
3e9efc8
Clean up imports
sodic Oct 7, 2024
24f65ec
Fix some more imports
sodic Oct 7, 2024
edd7a85
Change type name
sodic Oct 7, 2024
ae76820
Implement review changes
sodic Oct 7, 2024
7eab0bc
Fix failing tests
sodic Oct 7, 2024
8c8cb4f
Remove duplication when parsing ext imports
sodic Oct 7, 2024
d0ba9e5
Remove redundant variable
sodic Oct 7, 2024
4eec3c1
Remove trailling space
sodic Oct 7, 2024
1699df2
Revert Show instance for Decl
sodic Oct 7, 2024
5a6719e
Add module to package.json and remove mts
sodic Oct 8, 2024
3f48b98
Merge branch 'main' into filip-ts-sdk
sodic Oct 8, 2024
67f1f9e
Differentiate between different tsconfigs
sodic Oct 9, 2024
522e131
Fix tsconfig stuff
sodic Oct 9, 2024
8ab0b3f
Update e2e tests
sodic Oct 9, 2024
d90b235
Rename tsconfig constants and refactor
sodic Oct 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import qualified Data.Text as T
import StrongPath (Abs, Dir, File, Path')
import Wasp.Cli.Command.CreateNewProject.Common (defaultWaspVersionBounds)
import Wasp.Cli.Command.CreateNewProject.ProjectDescription (NewProjectAppName, NewProjectName)
import Wasp.Project.Analyze (findPackageJsonFile, findWaspFile)
import Wasp.Project.Analyze (WaspFile (..), findPackageJsonFile, findWaspFile)
import Wasp.Project.Common (WaspProjectDir)
import qualified Wasp.Util.IO as IOUtil

Expand All @@ -26,7 +26,10 @@ replaceTemplatePlaceholdersInWaspFile ::
replaceTemplatePlaceholdersInWaspFile appName projectName projectDir =
findWaspFile projectDir >>= \case
Nothing -> return ()
Just absMainWaspFile -> replaceTemplatePlaceholdersInFileOnDisk appName projectName absMainWaspFile
Just (WaspLang absMainWaspFile) -> replaceTemplatePlaceholders absMainWaspFile
Just (WaspTs absMainTsFile) -> replaceTemplatePlaceholders absMainTsFile
where
replaceTemplatePlaceholders = replaceTemplatePlaceholdersInFileOnDisk appName projectName

-- | Template file for package.json file has placeholders in it that we want to replace
-- in the package.json file we have written to the disk.
Expand Down
5 changes: 3 additions & 2 deletions waspc/src/Wasp/Error.hs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module Wasp.Error (showCompilerErrorForTerminal) where

import Data.List (intercalate)
import StrongPath (Abs, File', Path')
import StrongPath (Abs, Path')
import qualified StrongPath as SP
import StrongPath.Types (File)
import Wasp.Analyzer.Parser.Ctx (Ctx, getCtxRgn)
import Wasp.Analyzer.Parser.SourcePosition (SourcePosition (..))
import Wasp.Analyzer.Parser.SourceRegion (SourceRegion (..))
Expand All @@ -12,7 +13,7 @@ import qualified Wasp.Util.Terminal as T
-- | Transforms compiler error (error with parse context) into an informative, pretty String that
-- can be printed directly into the terminal. It uses terminal features like escape codes
-- (colors, styling, ...).
showCompilerErrorForTerminal :: (Path' Abs File', String) -> (String, Ctx) -> String
showCompilerErrorForTerminal :: (Path' Abs (File f), String) -> (String, Ctx) -> String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have something like data WaspFile, so that we could do Path' Abs (File WaspFile)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. We didn't before, but we'll have it now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take care of this later. It requires dealing with cyclic dependencies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved in #2407

showCompilerErrorForTerminal (waspFilePath, waspFileContent) (errMsg, errCtx) =
let srcRegion = getCtxRgn errCtx
in intercalate
Expand Down
135 changes: 124 additions & 11 deletions waspc/src/Wasp/Project/Analyze.hs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has a lot of diverse imports, indicating that it might want to be split into multiple files. I think we should split it probably -> we could certainly separate the logic that deals with WaspFile, and maybe then further split that into WaspLang and WaspTs logic, but not yet sure about that.

Copy link
Contributor Author

@sodic sodic Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about extracting a Project.WaspFile module but wasn't sure if it's necessary (thought I mentioned it in a comment somewhere, but looks like I didn't).

But yeah, if you like it too, I'll do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this one for later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #2407

Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
module Wasp.Project.Analyze
( analyzeWaspProject,
readPackageJsonFile,
analyzeWaspFileContent,
findWaspFile,
findPackageJsonFile,
analyzePrismaSchema,
WaspFile (..),
)
where

import Control.Applicative ((<|>))
import Control.Arrow (ArrowChoice (left))
import Control.Concurrent (newChan)
import Control.Concurrent.Async (concurrently)
import Control.Monad.Except (ExceptT (..), runExceptT)
import Control.Monad.IO.Class (liftIO)
import qualified Data.Aeson as Aeson
import Data.Conduit.Process.Typed (ExitCode (..))
import Data.List (find, isSuffixOf)
import StrongPath (Abs, Dir, File', Path', toFilePath, (</>))
import StrongPath (Abs, Dir, File', Path', Rel, toFilePath, (</>))
import qualified StrongPath as SP
import StrongPath.TH (relfile)
import StrongPath.Types (File)
sodic marked this conversation as resolved.
Show resolved Hide resolved
import qualified Wasp.Analyzer as Analyzer
import Wasp.Analyzer.AnalyzeError (getErrorMessageAndCtx)
import Wasp.Analyzer.Parser.Ctx (Ctx)
Expand All @@ -23,10 +32,14 @@ import qualified Wasp.CompileOptions as CompileOptions
import qualified Wasp.ConfigFile as CF
import Wasp.Error (showCompilerErrorForTerminal)
import qualified Wasp.Generator.ConfigFile as G.CF
import qualified Wasp.Generator.Job as J
import Wasp.Generator.Job.IO (readJobMessagesAndPrintThemPrefixed)
import Wasp.Generator.Job.Process (runNodeCommandAsJob)
Comment on lines +43 to +45
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is used here, then it sounds like it maybe shouldn't be in the Generator hm. What do you think? I didn't check the code though to see if there is anything Generator specific about it really or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, nice catch. I didn't notice it was there.

And there's nothing generator-specific about it. I think it's just there because the generator used to be the only one running jobs. I'll move it somewhere more appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this one for later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #2407

import Wasp.Project.Common
( CompileError,
CompileWarning,
WaspProjectDir,
dotWaspDirInWaspProjectDir,
findFileInWaspProjectDir,
packageJsonInWaspProjectDir,
prismaSchemaFileInWaspProjectDir,
Expand Down Expand Up @@ -60,7 +73,7 @@ analyzeWaspProject waspDir options = do
(Left prismaSchemaErrors, prismaSchemaWarnings) -> return (Left prismaSchemaErrors, prismaSchemaWarnings)
-- NOTE: we are ignoring prismaSchemaWarnings if the schema was parsed successfully
(Right prismaSchemaAst, _) ->
analyzeWaspFile prismaSchemaAst waspFilePath >>= \case
analyzeWaspFile waspDir prismaSchemaAst waspFilePath >>= \case
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waspFilePath -> waspFile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I prefer keeping the Path. I think it's our general convention too.

Left errors -> return (Left errors, [])
Right declarations ->
analyzePackageJsonContent waspDir >>= \case
Expand All @@ -69,8 +82,109 @@ analyzeWaspProject waspDir options = do
where
fileNotFoundMessage = "Couldn't find the *.wasp file in the " ++ toFilePath waspDir ++ " directory"

analyzeWaspFile :: Psl.Schema.Schema -> Path' Abs File' -> IO (Either [CompileError] [AS.Decl])
analyzeWaspFile prismaSchemaAst waspFilePath = do
data WaspFile
sodic marked this conversation as resolved.
Show resolved Hide resolved
= WaspLang !(Path' Abs (File WaspLangFile))
| WaspTs !(Path' Abs (File WaspTsFile))

data WaspLangFile

data WaspTsFile
sodic marked this conversation as resolved.
Show resolved Hide resolved

data CompiledWaspJsFile

data SpecJsonFile

analyzeWaspFile :: Path' Abs (Dir WaspProjectDir) -> Psl.Schema.Schema -> WaspFile -> IO (Either [CompileError] [AS.Decl])
analyzeWaspFile waspDir prismaSchemaAst = \case
WaspLang waspFilePath -> analyzeWaspLangFile prismaSchemaAst waspFilePath
WaspTs waspFilePath -> analyzeWaspTsFile waspDir prismaSchemaAst waspFilePath

analyzeWaspTsFile :: Path' Abs (Dir WaspProjectDir) -> Psl.Schema.Schema -> Path' Abs (File WaspTsFile) -> IO (Either [CompileError] [AS.Decl])
analyzeWaspTsFile waspProjectDir _prismaSchemaAst _waspFilePath = runExceptT $ do
-- TODO: I'm not yet sure where tsconfig.node.json location will come from
-- because we also need that knowledge to generate a TS SDK project.
compiledWaspJsFile <- ExceptT $ compileWaspTsFile waspProjectDir [relfile|tsconfig.node.json|]
specJsonFile <- ExceptT $ executeMainWaspJsFile waspProjectDir compiledWaspJsFile
contents <- ExceptT $ readDeclsJsonFile specJsonFile
liftIO $ putStrLn "Here are the contents of the spec file:"
liftIO $ print contents
return []

-- TODO: Reconsider the return value. Can I write the function in such a way
-- that it's impossible to get the absolute path to the compiled file without
-- calling the function that compiles it?
-- To do that, I'd have to craete a private module that knows where the file is
-- and not expose the constant for creating the absoltue path (like I did with config/spec.json).
-- Normally, I could just put the constant in the where clause like I did there, but I'm hesitant
-- to do that since the path comes from the tsconfig.
-- That is what I did currently, but I'll have to figure out the long-term solution.
-- The ideal solution is reading the TS file, and passing its config to tsc
-- manually (and getting the output file path in the process).
compileWaspTsFile ::
Path' Abs (Dir WaspProjectDir) ->
Path' (Rel WaspProjectDir) File' ->
IO (Either [CompileError] (Path' Abs (File CompiledWaspJsFile)))
sodic marked this conversation as resolved.
Show resolved Hide resolved
compileWaspTsFile waspProjectDir tsconfigNodeFileInWaspProjectDir = do
infomiho marked this conversation as resolved.
Show resolved Hide resolved
chan <- newChan
(_, tscExitCode) <-
concurrently
(readJobMessagesAndPrintThemPrefixed chan)
( runNodeCommandAsJob
sodic marked this conversation as resolved.
Show resolved Hide resolved
sodic marked this conversation as resolved.
Show resolved Hide resolved
waspProjectDir
"npx"
[ "tsc",
"-p",
toFilePath (waspProjectDir </> tsconfigNodeFileInWaspProjectDir),
sodic marked this conversation as resolved.
Show resolved Hide resolved
"--noEmit",
"false",
"--outDir",
toFilePath $ SP.parent absCompiledWaspJsFile
]
J.Wasp
chan
)
case tscExitCode of
ExitFailure _status -> return $ Left ["Error while running TypeScript compiler on the *.wasp.mts file."]
ExitSuccess -> return $ Right absCompiledWaspJsFile
where
-- TODO: I should be getting the compiled file path from the tsconfig.node.file
absCompiledWaspJsFile = waspProjectDir </> dotWaspDirInWaspProjectDir </> [relfile|config/main.wasp.mjs|]

executeMainWaspJsFile :: Path' Abs (Dir WaspProjectDir) -> Path' Abs (File CompiledWaspJsFile) -> IO (Either [CompileError] (Path' Abs (File SpecJsonFile)))
executeMainWaspJsFile waspProjectDir absCompiledMainWaspJsFile = do
chan <- newChan
(_, runExitCode) <- do
concurrently
(readJobMessagesAndPrintThemPrefixed chan)
( runNodeCommandAsJob
waspProjectDir
"npx"
-- TODO: Figure out how to keep running instructions in a single place
-- (e.g., this is the same as the package name, but it's repeated in two places).
-- 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,
sodic marked this conversation as resolved.
Show resolved Hide resolved
SP.fromAbsFile absSpecOutputFile
]
J.Wasp
chan
)
case runExitCode of
ExitFailure _status -> return $ Left ["Error while running the compiled *.wasp.mts file."]
ExitSuccess -> return $ Right absSpecOutputFile
where
-- TODO: The config part of the path is problematic because it relies on TSC to create it during compilation,
-- see notes in compileWaspFile.
absSpecOutputFile = waspProjectDir </> dotWaspDirInWaspProjectDir </> [relfile|config/spec.json|]

readDeclsJsonFile :: Path' Abs (File SpecJsonFile) -> IO (Either [CompileError] Aeson.Value)
readDeclsJsonFile declsJsonFile = do
byteString <- IOUtil.readFile declsJsonFile
return $ Right $ Aeson.toJSON byteString

analyzeWaspLangFile :: Psl.Schema.Schema -> Path' Abs (File WaspLangFile) -> IO (Either [CompileError] [AS.Decl])
analyzeWaspLangFile prismaSchemaAst waspFilePath = do
waspFileContent <- IOUtil.readFile waspFilePath
left (map $ showCompilerErrorForTerminal (waspFilePath, waspFileContent))
<$> analyzeWaspFileContent prismaSchemaAst waspFileContent
Expand Down Expand Up @@ -118,15 +232,14 @@ constructAppSpec waspDir options packageJson parsedPrismaSchema decls = do

return $ runValidation ASV.validateAppSpec appSpec

findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs File'))
findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe WaspFile)
findWaspFile waspDir = do
files <- fst <$> IOUtil.listDirectory waspDir
return $ (waspDir </>) <$> find isWaspFile files
return $ findWaspTsFile files <|> findWaspLangFile files
sodic marked this conversation as resolved.
Show resolved Hide resolved
where
isWaspFile path =
".wasp"
`isSuffixOf` toFilePath path
&& (length (toFilePath path) > length (".wasp" :: String))
findWaspTsFile files = WaspTs <$> findFileThatEndsWith ".wasp.mts" files
sodic marked this conversation as resolved.
Show resolved Hide resolved
findWaspLangFile files = WaspLang <$> findFileThatEndsWith ".wasp" files
findFileThatEndsWith suffix files = SP.castFile . (waspDir </>) <$> find ((suffix `isSuffixOf`) . toFilePath) files
infomiho marked this conversation as resolved.
Show resolved Hide resolved

analyzePackageJsonContent :: Path' Abs (Dir WaspProjectDir) -> IO (Either [CompileError] PackageJson)
analyzePackageJsonContent waspProjectDir =
Expand Down
5 changes: 3 additions & 2 deletions waspc/src/Wasp/Project/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module Wasp.Project.Common
where

import StrongPath (Abs, Dir, File', Path', Rel, reldir, relfile, toFilePath, (</>))
import StrongPath.Types (File)
import System.Directory (doesFileExist)
import Wasp.AppSpec.ExternalFiles (SourceExternalCodeDir, SourceExternalPublicDir)
import qualified Wasp.Generator.Common
Expand Down Expand Up @@ -86,8 +87,8 @@ tsconfigInWaspProjectDir = [relfile|tsconfig.json|]

findFileInWaspProjectDir ::
Path' Abs (Dir WaspProjectDir) ->
Path' (Rel WaspProjectDir) File' ->
IO (Maybe (Path' Abs File'))
Path' (Rel WaspProjectDir) (File f) ->
Martinsos marked this conversation as resolved.
Show resolved Hide resolved
IO (Maybe (Path' Abs (File f)))
findFileInWaspProjectDir waspDir file = do
let fileAbsFp = waspDir </> file
fileExists <- doesFileExist $ toFilePath fileAbsFp
Expand Down
Loading