diff --git a/waspc/cli/exe/Main.hs b/waspc/cli/exe/Main.hs index 961a12a735..b9df493424 100644 --- a/waspc/cli/exe/Main.hs +++ b/waspc/cli/exe/Main.hs @@ -130,7 +130,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do handleInternalErrors :: E.ErrorCall -> IO () handleInternalErrors e = do - putStrLn $ "\nInternal Wasp error (bug in compiler):\n" ++ indent 2 (show e) + putStrLn $ "\nInternal Wasp error (bug in the compiler):\n" ++ indent 2 (show e) exitFailure -- | Sets env variables that are visible to the commands run by the CLI. diff --git a/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates/Templating.hs b/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates/Templating.hs index 7c3cb58570..2be2430864 100644 --- a/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates/Templating.hs +++ b/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates/Templating.hs @@ -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 (findWaspFile) +import Wasp.Project.Analyze (WaspFilePath (..), findWaspFile) import Wasp.Project.Common (WaspProjectDir) import Wasp.Project.ExternalConfig.PackageJson (findPackageJsonFile) import qualified Wasp.Util.IO as IOUtil @@ -26,8 +26,11 @@ replaceTemplatePlaceholdersInWaspFile :: NewProjectAppName -> NewProjectName -> Path' Abs (Dir WaspProjectDir) -> IO () replaceTemplatePlaceholdersInWaspFile appName projectName projectDir = findWaspFile projectDir >>= \case - Nothing -> return () - Just absMainWaspFile -> replaceTemplatePlaceholdersInFileOnDisk appName projectName absMainWaspFile + Left _error -> return () + Right (WaspLang absMainWaspFile) -> replaceTemplatePlaceholders absMainWaspFile + Right (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. diff --git a/waspc/data/Cli/templates/basic/package.json b/waspc/data/Cli/templates/basic/package.json index d533706c1a..47102a526e 100644 --- a/waspc/data/Cli/templates/basic/package.json +++ b/waspc/data/Cli/templates/basic/package.json @@ -1,5 +1,6 @@ { "name": "__waspAppName__", + "type": "module", "dependencies": { "wasp": "file:.wasp/out/sdk/wasp", "react": "^18.2.0" diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json index a4557082c4..7b5eed4125 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/package.json @@ -9,5 +9,6 @@ "typescript": "^5.1.0", "vite": "^4.3.9" }, - "name": "waspBuild" + "name": "waspBuild", + "type": "module" } diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json index a4557082c4..7b5eed4125 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/package.json @@ -9,5 +9,6 @@ "typescript": "^5.1.0", "vite": "^4.3.9" }, - "name": "waspBuild" + "name": "waspBuild", + "type": "module" } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json index cae5e83e8d..1ffaf97439 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/package.json @@ -9,5 +9,6 @@ "typescript": "^5.1.0", "vite": "^4.3.9" }, - "name": "waspCompile" + "name": "waspCompile", + "type": "module" } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json index d659437d0d..61062ad976 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/package.json @@ -11,5 +11,6 @@ }, "name": "waspComplexTest", "react-redux": "^7.1.3", - "redux": "^4.0.5" + "redux": "^4.0.5", + "type": "module" } diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json index 9644c5e9d1..deb4a4baca 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/package.json @@ -9,5 +9,6 @@ "typescript": "^5.1.0", "vite": "^4.3.9" }, - "name": "waspJob" + "name": "waspJob", + "type": "module" } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json index 987acc7d89..98da5e7388 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/package.json @@ -9,5 +9,6 @@ "typescript": "^5.1.0", "vite": "^4.3.9" }, - "name": "waspMigrate" + "name": "waspMigrate", + "type": "module" } diff --git a/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json b/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json index 52321cde8e..5a4bb9c6ee 100644 --- a/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json +++ b/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/package.json @@ -9,5 +9,6 @@ "typescript": "^5.1.0", "vite": "^4.3.9" }, - "name": "waspNew" + "name": "waspNew", + "type": "module" } diff --git a/waspc/src/Wasp/Analyzer.hs b/waspc/src/Wasp/Analyzer.hs index 3f86d3407c..8c4c98a68a 100644 --- a/waspc/src/Wasp/Analyzer.hs +++ b/waspc/src/Wasp/Analyzer.hs @@ -111,6 +111,7 @@ module Wasp.Analyzer -- * API analyze, + getEntityDecls, takeDecls, AnalyzeError (..), getErrorMessageAndCtx, @@ -127,8 +128,9 @@ import Wasp.Analyzer.AnalyzeError ) import Wasp.Analyzer.Evaluator (Decl, evaluate, takeDecls) import Wasp.Analyzer.Parser (parseStatements) +import qualified Wasp.Analyzer.Parser as Parser import Wasp.Analyzer.Parser.Valid (validateAst) -import Wasp.Analyzer.Prisma (injectEntitiesFromPrismaSchema) +import Wasp.Analyzer.Prisma (injectEntitiesFromPrismaSchema, parseEntityStatements) import Wasp.Analyzer.StdTypeDefinitions (stdTypes) import Wasp.Analyzer.TypeChecker (typeCheck) import qualified Wasp.Psl.Ast.Schema as Psl.Schema @@ -157,7 +159,17 @@ analyze prismaSchemaAst = ^ disallow entities here ^ inject entities here --} - >=> (left ((: []) . ValidationError) . validateAst) + >=> wrapAnalyzerError ValidationError . validateAst >=> injectEntitiesFromPrismaSchema prismaSchemaAst - >=> (left ((: []) . TypeError) . typeCheck stdTypes) - >=> (left ((: []) . EvaluationError) . evaluate stdTypes) + >=> (wrapAnalyzerError TypeError . typeCheck stdTypes) + >=> (wrapAnalyzerError EvaluationError . evaluate stdTypes) + +getEntityDecls :: Psl.Schema.Schema -> Either [AnalyzeError] [Decl] +getEntityDecls schema = + wrapAnalyzerError TypeError (typeCheck stdTypes astWithEntitiesOnly) + >>= (wrapAnalyzerError EvaluationError . evaluate stdTypes) + where + astWithEntitiesOnly = Parser.AST $ parseEntityStatements schema + +wrapAnalyzerError :: (e -> AnalyzeError) -> Either e a -> Either [AnalyzeError] a +wrapAnalyzerError makeError = left ((: []) . makeError) diff --git a/waspc/src/Wasp/Analyzer/Evaluator/Evaluation/TypedExpr/Combinators.hs b/waspc/src/Wasp/Analyzer/Evaluator/Evaluation/TypedExpr/Combinators.hs index 26598d58e2..8e6c70c7bd 100644 --- a/waspc/src/Wasp/Analyzer/Evaluator/Evaluation/TypedExpr/Combinators.hs +++ b/waspc/src/Wasp/Analyzer/Evaluator/Evaluation/TypedExpr/Combinators.hs @@ -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 diff --git a/waspc/src/Wasp/Analyzer/Prisma.hs b/waspc/src/Wasp/Analyzer/Prisma.hs index ba070ebf49..161222fbec 100644 --- a/waspc/src/Wasp/Analyzer/Prisma.hs +++ b/waspc/src/Wasp/Analyzer/Prisma.hs @@ -1,5 +1,6 @@ module Wasp.Analyzer.Prisma ( injectEntitiesFromPrismaSchema, + parseEntityStatements, ) where @@ -11,9 +12,12 @@ import qualified Wasp.Psl.Generator.Model as Psl.Model.Generator injectEntitiesFromPrismaSchema :: Psl.Schema.Schema -> Parser.AST -> Either a Parser.AST injectEntitiesFromPrismaSchema schema ast = Right $ ast {Parser.astStmts = stmts ++ entityStmts} where - entityStmts = makeEntityStmt <$> generatePrismaModelSources schema + entityStmts = parseEntityStatements schema stmts = Parser.astStmts ast +parseEntityStatements :: Psl.Schema.Schema -> [WithCtx Parser.Stmt] +parseEntityStatements schema = makeEntityStmt <$> generatePrismaModelSources schema + type ModelName = String type ModelBody = String diff --git a/waspc/src/Wasp/AppSpec/ExtImport.hs b/waspc/src/Wasp/AppSpec/ExtImport.hs index 5de24f10da..fee7606a78 100644 --- a/waspc/src/Wasp/AppSpec/ExtImport.hs +++ b/waspc/src/Wasp/AppSpec/ExtImport.hs @@ -6,14 +6,18 @@ 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) data ExtImport = ExtImport @@ -30,7 +34,9 @@ instance FromJSON ExtImport where nameStr <- o .: "name" pathStr <- o .: "path" extImportName <- parseExtImportName kindStr nameStr - extImportPath <- parseExtImportPath pathStr + extImportPath <- case parseExtImportPath pathStr of + Right path' -> pure path' + Left err -> fail err return $ ExtImport extImportName extImportPath where parseExtImportName kindStr nameStr = case kindStr of @@ -38,10 +44,6 @@ instance FromJSON ExtImport where "named" -> pure $ ExtImportField nameStr _ -> fail $ "Failed to parse import kind: " <> kindStr - parseExtImportPath pathStr = case parseRelFileP pathStr of - Just path' -> pure path' - Nothing -> fail $ "Failed to parse relative posix path to file: " <> pathStr - type ExtImportPath = Path Posix (Rel SourceExternalCodeDir) File' type Identifier = String @@ -57,3 +59,23 @@ importIdentifier :: ExtImport -> Identifier importIdentifier (ExtImport importName _) = case importName of ExtImportModule n -> n ExtImportField n -> n + +parseExtImportPath :: String -> Either String ExtImportPath +parseExtImportPath extImportPath = case stripImportPrefix extImportPath of + 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 + -- todo (filip): Do we still want to know which 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/" diff --git a/waspc/src/Wasp/Error.hs b/waspc/src/Wasp/Error.hs index 947d82fb33..aa4ef8474a 100644 --- a/waspc/src/Wasp/Error.hs +++ b/waspc/src/Wasp/Error.hs @@ -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 (..)) @@ -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 showCompilerErrorForTerminal (waspFilePath, waspFileContent) (errMsg, errCtx) = let srcRegion = getCtxRgn errCtx in intercalate diff --git a/waspc/src/Wasp/Generator/ExternalConfig/TsConfig.hs b/waspc/src/Wasp/Generator/ExternalConfig/TsConfig.hs index ab3d3e4001..8d3612f807 100644 --- a/waspc/src/Wasp/Generator/ExternalConfig/TsConfig.hs +++ b/waspc/src/Wasp/Generator/ExternalConfig/TsConfig.hs @@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleInstances #-} module Wasp.Generator.ExternalConfig.TsConfig - ( validateTsConfig, + ( validateSrcTsConfig, ) where @@ -23,8 +23,8 @@ instance IsJavascriptValue Bool where type FieldName = String -validateTsConfig :: T.TsConfig -> [ErrorMsg] -validateTsConfig tsConfig = +validateSrcTsConfig :: T.TsConfig -> [ErrorMsg] +validateSrcTsConfig tsConfig = concat [ validateRequiredFieldInCompilerOptions "module" "esnext" T._module, validateRequiredFieldInCompilerOptions "target" "esnext" T.target, diff --git a/waspc/src/Wasp/Project/Analyze.hs b/waspc/src/Wasp/Project/Analyze.hs index 5cd19cd96a..0004fad4c9 100644 --- a/waspc/src/Wasp/Project/Analyze.hs +++ b/waspc/src/Wasp/Project/Analyze.hs @@ -3,27 +3,56 @@ module Wasp.Project.Analyze analyzeWaspFileContent, findWaspFile, analyzePrismaSchema, + WaspFilePath (..), ) where 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.List (find, isSuffixOf) -import StrongPath (Abs, Dir, File', Path', toFilePath, ()) +import StrongPath + ( Abs, + Dir, + File, + File', + Path', + Rel, + basename, + castFile, + fromAbsDir, + fromAbsFile, + fromRelFile, + relfile, + (), + ) +import System.Exit (ExitCode (..)) import qualified Wasp.Analyzer as Analyzer import Wasp.Analyzer.AnalyzeError (getErrorMessageAndCtx) import Wasp.Analyzer.Parser.Ctx (Ctx) import qualified Wasp.AppSpec as AS +import Wasp.AppSpec.Core.Decl.JSON () import qualified Wasp.AppSpec.Valid as ASV import Wasp.CompileOptions (CompileOptions) 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) import Wasp.Project.Common ( CompileError, CompileWarning, + WaspFilePath (..), + WaspLangFile, WaspProjectDir, + WaspTsFile, + dotWaspDirInWaspProjectDir, findFileInWaspProjectDir, + getSrcTsConfigInWaspProjectDir, prismaSchemaFileInWaspProjectDir, ) import Wasp.Project.Db (makeDevDatabaseUrl) @@ -37,8 +66,9 @@ 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) import Wasp.Valid (ValidationError) import qualified Wasp.Valid as Valid @@ -47,7 +77,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, []) @@ -56,17 +86,116 @@ 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 Left errors -> return (Left errors, []) Right declarations -> - EC.analyzeExternalConfigs waspDir >>= \case + EC.analyzeExternalConfigs waspDir (getSrcTsConfigInWaspProjectDir waspFilePath) >>= \case Left errors -> return (Left errors, []) Right externalConfigs -> constructAppSpec waspDir options externalConfigs prismaSchemaAst declarations + +data CompiledWaspJsFile + +data AppSpecDeclsJsonFile + +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 + +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 should come from + -- because we also need that knowledge when generating a TS SDK project. + compiledWaspJsFile <- ExceptT $ compileWaspTsFile waspProjectDir [relfile|tsconfig.wasp.json|] waspFilePath + declsJsonFile <- ExceptT $ executeMainWaspJsFileAndGetDeclsFile waspProjectDir prismaSchemaAst compiledWaspJsFile + ExceptT $ readDecls prismaSchemaAst declsJsonFile + +compileWaspTsFile :: + Path' Abs (Dir WaspProjectDir) -> + Path' (Rel WaspProjectDir) File' -> + Path' Abs (File WaspTsFile) -> + IO (Either [CompileError] (Path' Abs (File CompiledWaspJsFile))) +compileWaspTsFile waspProjectDir tsconfigNodeFileInWaspProjectDir waspFilePath = do + chan <- newChan + (_, tscExitCode) <- + concurrently + (readJobMessagesAndPrintThemPrefixed chan) + ( runNodeCommandAsJob + waspProjectDir + "npx" + [ "tsc", + "-p", + fromAbsFile (waspProjectDir tsconfigNodeFileInWaspProjectDir), + "--noEmit", + "false", + "--outDir", + fromAbsDir outDir + ] + J.Wasp + chan + ) + return $ case tscExitCode of + ExitFailure _status -> Left ["Got TypeScript compiler errors for " ++ fromAbsFile waspFilePath ++ "."] + ExitSuccess -> Right absCompiledWaspJsFile where - fileNotFoundMessage = "Couldn't find the *.wasp file in the " ++ toFilePath waspDir ++ " directory" + outDir = waspProjectDir dotWaspDirInWaspProjectDir + absCompiledWaspJsFile = outDir compiledWaspJsFileInDotWaspDir + compiledWaspJsFileInDotWaspDir = castFile $ case replaceRelExtension (basename waspFilePath) ".js" of + Just path -> path + Nothing -> error $ "Couldn't calculate the compiled JS file path for " ++ fromAbsFile waspFilePath ++ "." + +executeMainWaspJsFileAndGetDeclsFile :: + Path' Abs (Dir WaspProjectDir) -> + Psl.Schema.Schema -> + Path' Abs (File CompiledWaspJsFile) -> + IO (Either [CompileError] (Path' Abs (File AppSpecDeclsJsonFile))) +executeMainWaspJsFileAndGetDeclsFile waspProjectDir prismaSchemaAst 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 string 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", + fromAbsFile absCompiledMainWaspJsFile, + fromAbsFile absDeclsOutputFile, + encodeToString allowedEntityNames + ] + J.Wasp + chan + ) + case runExitCode of + ExitFailure _status -> return $ Left ["Error while running the compiled *.wasp.ts file."] + ExitSuccess -> return $ Right absDeclsOutputFile + where + absDeclsOutputFile = waspProjectDir dotWaspDirInWaspProjectDir [relfile|decls.json|] + allowedEntityNames = Psl.Schema.getModelNames prismaSchemaAst + +readDecls :: Psl.Schema.Schema -> Path' Abs (File AppSpecDeclsJsonFile) -> IO (Either [CompileError] [AS.Decl]) +readDecls prismaSchemaAst declsJsonFile = runExceptT $ do + entityDecls <- liftEither entityDeclsOrErrors + remainingDecls <- ExceptT $ left (: []) <$> declsFromJsonOrError + return $ entityDecls ++ remainingDecls + where + entityDeclsOrErrors = + left (map fst) $ + left (map getErrorMessageAndCtx) $ + Analyzer.getEntityDecls prismaSchemaAst + + declsFromJsonOrError = do + declsBytestring <- IOUtil.readFileBytes declsJsonFile + return $ + left ("Error while reading the declarations from JSON: " ++) $ + Aeson.eitherDecode declsBytestring -analyzeWaspFile :: Psl.Schema.Schema -> Path' Abs File' -> IO (Either [CompileError] [AS.Decl]) -analyzeWaspFile prismaSchemaAst waspFilePath = do +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 @@ -116,15 +245,22 @@ constructAppSpec waspDir options externalConfigs parsedPrismaSchema decls = do return $ runValidation ASV.validateAppSpec appSpec -findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs File')) +findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either String WaspFilePath) findWaspFile waspDir = do files <- fst <$> IOUtil.listDirectory waspDir - return $ (waspDir ) <$> find isWaspFile 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 - isWaspFile path = - ".wasp" - `isSuffixOf` toFilePath path - && (length (toFilePath path) > length (".wasp" :: String)) + findWaspTsFile files = WaspTs <$> findFileThatEndsWith ".wasp.ts" files + findWaspLangFile files = WaspLang <$> findFileThatEndsWith ".wasp" files + findFileThatEndsWith suffix files = castFile . (waspDir ) <$> find ((suffix `isSuffixOf`) . fromRelFile) files + fileNotFoundMessage = "Couldn't find the *.wasp or a *.wasp.ts file in the " ++ fromAbsDir waspDir ++ " directory" + bothFilesFoundMessage = + "Found both *.wasp and *.wasp.ts 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 @@ -143,7 +279,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 = diff --git a/waspc/src/Wasp/Project/Common.hs b/waspc/src/Wasp/Project/Common.hs index f6541cd725..9fb166e924 100644 --- a/waspc/src/Wasp/Project/Common.hs +++ b/waspc/src/Wasp/Project/Common.hs @@ -5,7 +5,10 @@ module Wasp.Project.Common CompileError, CompileWarning, PackageJsonFile, - TsConfigFile, + SrcTsConfigFile, + WaspFilePath (..), + WaspLangFile, + WaspTsFile, findFileInWaspProjectDir, dotWaspDirInWaspProjectDir, generatedCodeDirInDotWaspDir, @@ -18,9 +21,10 @@ module Wasp.Project.Common nodeModulesDirInWaspProjectDir, srcDirInWaspProjectDir, extPublicDirInWaspProjectDir, - tsconfigInWaspProjectDir, prismaSchemaFileInWaspProjectDir, - tsConfigInWaspProjectDir, + getSrcTsConfigInWaspProjectDir, + srcTsConfigInWaspLangProject, + srcTsConfigInWaspTsProject, ) where @@ -41,7 +45,15 @@ data DotWaspDir -- Here we put everything that wasp generates. data PackageJsonFile -data TsConfigFile +data SrcTsConfigFile + +data WaspFilePath + = WaspLang !(Path' Abs (File WaspLangFile)) + | WaspTs !(Path' Abs (File WaspTsFile)) + +data WaspLangFile + +data WaspTsFile -- | NOTE: If you change the depth of this path, also update @waspProjectDirFromProjectRootDir@ below. -- TODO: SHould this be renamed to include word "root"? @@ -76,8 +88,17 @@ dotWaspInfoFileInGeneratedCodeDir = [relfile|.waspinfo|] packageJsonInWaspProjectDir :: Path' (Rel WaspProjectDir) (File PackageJsonFile) packageJsonInWaspProjectDir = [relfile|package.json|] -tsConfigInWaspProjectDir :: Path' (Rel WaspProjectDir) (File TsConfigFile) -tsConfigInWaspProjectDir = [relfile|tsconfig.json|] +-- TODO: The entire tsconfig story is very fragile +getSrcTsConfigInWaspProjectDir :: WaspFilePath -> Path' (Rel WaspProjectDir) (File SrcTsConfigFile) +getSrcTsConfigInWaspProjectDir = \case + WaspTs _ -> srcTsConfigInWaspTsProject + WaspLang _ -> srcTsConfigInWaspLangProject + +srcTsConfigInWaspLangProject :: Path' (Rel WaspProjectDir) (File SrcTsConfigFile) +srcTsConfigInWaspLangProject = [relfile|tsconfig.json|] + +srcTsConfigInWaspTsProject :: Path' (Rel WaspProjectDir) (File SrcTsConfigFile) +srcTsConfigInWaspTsProject = [relfile|tsconfig.src.json|] packageLockJsonInWaspProjectDir :: Path' (Rel WaspProjectDir) File' packageLockJsonInWaspProjectDir = [relfile|package-lock.json|] @@ -91,13 +112,10 @@ srcDirInWaspProjectDir = [reldir|src|] extPublicDirInWaspProjectDir :: Path' (Rel WaspProjectDir) (Dir SourceExternalPublicDir) extPublicDirInWaspProjectDir = [reldir|public|] -tsconfigInWaspProjectDir :: Path' (Rel WaspProjectDir) File' -tsconfigInWaspProjectDir = [relfile|tsconfig.json|] - findFileInWaspProjectDir :: Path' Abs (Dir WaspProjectDir) -> - Path' (Rel WaspProjectDir) (File file) -> - IO (Maybe (Path' Abs (File file))) + Path' (Rel WaspProjectDir) (File f) -> + IO (Maybe (Path' Abs (File f))) findFileInWaspProjectDir waspDir file = do let fileAbsFp = waspDir file fileExists <- doesFileExist $ toFilePath fileAbsFp diff --git a/waspc/src/Wasp/Project/ExternalConfig.hs b/waspc/src/Wasp/Project/ExternalConfig.hs index c03a2732de..742d67285d 100644 --- a/waspc/src/Wasp/Project/ExternalConfig.hs +++ b/waspc/src/Wasp/Project/ExternalConfig.hs @@ -5,15 +5,16 @@ module Wasp.Project.ExternalConfig where import Control.Monad.Except (ExceptT (ExceptT), runExceptT) -import StrongPath (Abs, Dir, Path') +import StrongPath (Abs, Dir, File, Path', Rel) import qualified Wasp.ExternalConfig.PackageJson as P import qualified Wasp.ExternalConfig.TsConfig as T import Wasp.Project.Common ( CompileError, + SrcTsConfigFile, WaspProjectDir, ) import Wasp.Project.ExternalConfig.PackageJson (analyzePackageJsonFile) -import Wasp.Project.ExternalConfig.TsConfig (analyzeTsConfigFile) +import Wasp.Project.ExternalConfig.TsConfig (analyzeSrcTsConfigFile) data ExternalConfigs = ExternalConfigs { _packageJson :: P.PackageJson, @@ -21,10 +22,13 @@ data ExternalConfigs = ExternalConfigs } deriving (Show) -analyzeExternalConfigs :: Path' Abs (Dir WaspProjectDir) -> IO (Either [CompileError] ExternalConfigs) -analyzeExternalConfigs waspDir = runExceptT $ do +analyzeExternalConfigs :: + Path' Abs (Dir WaspProjectDir) -> + Path' (Rel WaspProjectDir) (File SrcTsConfigFile) -> + IO (Either [CompileError] ExternalConfigs) +analyzeExternalConfigs waspDir srcTsConfigFile = runExceptT $ do packageJsonContent <- ExceptT $ analyzePackageJsonFile waspDir - tsConfigContent <- ExceptT $ analyzeTsConfigFile waspDir + tsConfigContent <- ExceptT $ analyzeSrcTsConfigFile waspDir srcTsConfigFile return $ ExternalConfigs diff --git a/waspc/src/Wasp/Project/ExternalConfig/TsConfig.hs b/waspc/src/Wasp/Project/ExternalConfig/TsConfig.hs index 8693241a9b..e12ca1e591 100644 --- a/waspc/src/Wasp/Project/ExternalConfig/TsConfig.hs +++ b/waspc/src/Wasp/Project/ExternalConfig/TsConfig.hs @@ -1,43 +1,40 @@ module Wasp.Project.ExternalConfig.TsConfig - ( analyzeTsConfigFile, + ( analyzeSrcTsConfigFile, ) where +import Control.Arrow (left) import Control.Monad.Except (ExceptT (ExceptT), runExceptT, throwError) import qualified Data.ByteString.Lazy.UTF8 as BS import Data.Either.Extra (maybeToEither) -import StrongPath (Abs, Dir, File, Path', toFilePath) +import StrongPath (Abs, Dir, File, Path', Rel, toFilePath) import qualified Wasp.ExternalConfig.TsConfig as T -import Wasp.Generator.ExternalConfig.TsConfig (validateTsConfig) +import Wasp.Generator.ExternalConfig.TsConfig (validateSrcTsConfig) import Wasp.Project.Common - ( TsConfigFile, + ( SrcTsConfigFile, WaspProjectDir, findFileInWaspProjectDir, - tsConfigInWaspProjectDir, ) import qualified Wasp.Util.IO as IOUtil import Wasp.Util.Json (parseJsonWithComments) -analyzeTsConfigFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either [String] T.TsConfig) -analyzeTsConfigFile waspDir = runExceptT $ do - tsConfigFile <- ExceptT findTsConfigOrError - tsConfig <- ExceptT $ readTsConfigFile tsConfigFile - case validateTsConfig tsConfig of - [] -> return tsConfig +analyzeSrcTsConfigFile :: + Path' Abs (Dir WaspProjectDir) -> + Path' (Rel WaspProjectDir) (File SrcTsConfigFile) -> + IO (Either [String] T.TsConfig) +analyzeSrcTsConfigFile waspDir srcTsConfigFile = runExceptT $ do + tsConfigFileContents <- ExceptT findTsConfigOrError + srcTsConfigContents <- ExceptT $ left (: []) <$> readTsConfigFile tsConfigFileContents + case validateSrcTsConfig srcTsConfigContents of + [] -> return srcTsConfigContents errors -> throwError errors where - findTsConfigOrError = maybeToEither [fileNotFoundMessage] <$> findTsConfigFile waspDir + findTsConfigOrError = maybeToEither [fileNotFoundMessage] <$> findFileInWaspProjectDir waspDir srcTsConfigFile fileNotFoundMessage = "Couldn't find the tsconfig.json file in the " ++ toFilePath waspDir ++ " directory" -findTsConfigFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs (File TsConfigFile))) -findTsConfigFile waspProjectDir = findFileInWaspProjectDir waspProjectDir tsConfigInWaspProjectDir - -readTsConfigFile :: Path' Abs (File TsConfigFile) -> IO (Either [String] T.TsConfig) +-- TODO: Reduce polymorphism, should only work with TsConfig files +readTsConfigFile :: Path' Abs (File f) -> IO (Either String T.TsConfig) readTsConfigFile tsConfigFile = do tsConfigContent <- IOUtil.readFileBytes tsConfigFile - parseResult <- parseJsonWithComments . BS.toString $ tsConfigContent - - case parseResult of - Right tsConfig -> return $ Right tsConfig - Left err -> return $ Left ["Failed to parse tsconfig.json file: " ++ err] + return $ left ("Failed to parse tsconfig file" ++) parseResult diff --git a/waspc/src/Wasp/Psl/Ast/Model.hs b/waspc/src/Wasp/Psl/Ast/Model.hs index 7eb9cc82d9..e9c6e336a8 100644 --- a/waspc/src/Wasp/Psl/Ast/Model.hs +++ b/waspc/src/Wasp/Psl/Ast/Model.hs @@ -7,7 +7,7 @@ module Wasp.Psl.Ast.Model Field (..), FieldType (..), FieldTypeModifier (..), - getFields, + getName, ) where @@ -60,5 +60,5 @@ data FieldTypeModifier | Optional deriving (Show, Eq, Data) -getFields :: Model -> [Field] -getFields (Model _ (Body elements)) = [field | ElementField field <- elements] +getName :: Model -> Name +getName (Model name _) = name diff --git a/waspc/src/Wasp/Psl/Ast/Schema.hs b/waspc/src/Wasp/Psl/Ast/Schema.hs index df551c9ea8..403b4452dc 100644 --- a/waspc/src/Wasp/Psl/Ast/Schema.hs +++ b/waspc/src/Wasp/Psl/Ast/Schema.hs @@ -7,6 +7,7 @@ module Wasp.Psl.Ast.Schema getEnums, getDatasources, getGenerators, + getModelNames, ) where @@ -14,6 +15,7 @@ import Wasp.Psl.Ast.ConfigBlock (ConfigBlock) import qualified Wasp.Psl.Ast.ConfigBlock as Psl.ConfigBlock import Wasp.Psl.Ast.Enum (Enum) import Wasp.Psl.Ast.Model (Model) +import qualified Wasp.Psl.Ast.Model as Model import Wasp.Psl.Ast.Type (Type) import Wasp.Psl.Ast.View (View) import Prelude hiding (Enum) @@ -49,3 +51,6 @@ getGenerators schema = [generator | generator@((Psl.ConfigBlock.ConfigBlock Psl. getConfigBlocks :: Schema -> [ConfigBlock] getConfigBlocks (Schema blocks) = [configBlock | ConfigBlock configBlock <- blocks] + +getModelNames :: Schema -> [String] +getModelNames schema = map Model.getName $ getModels schema diff --git a/waspc/src/Wasp/Util/Aeson.hs b/waspc/src/Wasp/Util/Aeson.hs index e243467809..488417528d 100644 --- a/waspc/src/Wasp/Util/Aeson.hs +++ b/waspc/src/Wasp/Util/Aeson.hs @@ -1,10 +1,11 @@ module Wasp.Util.Aeson ( encodeToText, decodeFromString, + encodeToString, ) where -import Data.Aeson (FromJSON, ToJSON, eitherDecode) +import Data.Aeson (FromJSON, ToJSON, eitherDecode, encode) import Data.Aeson.Text (encodeToTextBuilder) import qualified Data.ByteString.Lazy.UTF8 as BS import Data.Text (Text) @@ -16,3 +17,6 @@ encodeToText = toStrict . toLazyText . encodeToTextBuilder decodeFromString :: FromJSON a => String -> Either String a decodeFromString = eitherDecode . BS.fromString + +encodeToString :: ToJSON a => a -> String +encodeToString = BS.toString . encode diff --git a/waspc/test/AppSpec/FromJSONTest.hs b/waspc/test/AppSpec/FromJSONTest.hs index a9f3247135..1d0d2d8e7b 100644 --- a/waspc/test/AppSpec/FromJSONTest.hs +++ b/waspc/test/AppSpec/FromJSONTest.hs @@ -94,16 +94,6 @@ spec_AppSpecFromJSON = do } |] `shouldDecodeTo` Just (Ref.Ref "foo" :: Ref Query) - it "fails to parse an invalid declType" $ do - [trimming| - { - "name": "foo", - "declType": "IMadeThisUp" - } - |] - -- NOTE: We are using `Ref Entity` here, because the Show instance in - -- shouldDecodeTo demands a proper type. - `shouldDecodeTo` (Nothing :: Maybe (Ref Entity)) describe "Query" $ do it "parses a valid Query JSON with auth and entities" $ do [trimming| @@ -288,8 +278,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" }|] diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal index 058aca0162..ecb0237ac1 100644 --- a/waspc/waspc.cabal +++ b/waspc/waspc.cabal @@ -464,7 +464,6 @@ library waspls Wasp.LSP.TypeInference Wasp.LSP.Util Wasp.LSP.Prisma.Analyze - Wasp.LSP.Prisma.Util build-depends: base , aeson diff --git a/waspc/waspls/src/Wasp/LSP/ExtImport/ExportsCache.hs b/waspc/waspls/src/Wasp/LSP/ExtImport/ExportsCache.hs index dba605eca6..369346be39 100644 --- a/waspc/waspls/src/Wasp/LSP/ExtImport/ExportsCache.hs +++ b/waspc/waspls/src/Wasp/LSP/ExtImport/ExportsCache.hs @@ -22,7 +22,7 @@ import Wasp.LSP.ExtImport.Path (WaspStyleExtFilePath, absPathToCachePath, cacheP import Wasp.LSP.ExtImport.Syntax (ExtImportNode (einName, einPath), getAllExtImports) import Wasp.LSP.ServerMonads (HandlerM, ServerM, getProjectRootDir, handler, modify) import qualified Wasp.LSP.ServerState as State -import Wasp.Project.Common (tsconfigInWaspProjectDir) +import Wasp.Project.Common (srcTsConfigInWaspLangProject) import qualified Wasp.TypeScript.Inspect.Exports as TS -- | Based on the files imported in the external imports of the current concrete @@ -63,7 +63,7 @@ refreshExportsOfFiles files = do getExportRequestForFile projectRootDir file = TS.TsExportsRequest { TS.filepaths = [SP.fromAbsFile file], - TS.tsconfig = Just $ SP.fromAbsFile $ projectRootDir tsconfigInWaspProjectDir + TS.tsconfig = Just $ SP.fromAbsFile $ projectRootDir srcTsConfigInWaspLangProject } -- Replaces entries in the exports cache with the exports lists in the diff --git a/waspc/waspls/src/Wasp/LSP/Prisma/Analyze.hs b/waspc/waspls/src/Wasp/LSP/Prisma/Analyze.hs index d8ec5caf46..0ec4b7565d 100644 --- a/waspc/waspls/src/Wasp/LSP/Prisma/Analyze.hs +++ b/waspc/waspls/src/Wasp/LSP/Prisma/Analyze.hs @@ -4,11 +4,11 @@ import Control.Lens ((.~)) import Control.Monad.Cont (liftIO) import Control.Monad.Log.Class (logM) import StrongPath (Abs, Dir, Path') -import Wasp.LSP.Prisma.Util (showModelNames) import Wasp.LSP.ServerMonads (ServerM, modify) import qualified Wasp.LSP.ServerState as State import Wasp.Project (WaspProjectDir) import Wasp.Project.Analyze (analyzePrismaSchema) +import Wasp.Psl.Ast.Schema (getModelNames) analyzeAndSetPrismaSchema :: Path' Abs (Dir WaspProjectDir) -> ServerM () analyzeAndSetPrismaSchema waspDir = do @@ -18,7 +18,7 @@ analyzeAndSetPrismaSchema waspDir = do logOutput "warnings" $ show warnings (Right prismaSchemaAst, warnings) -> do logOutput "warnings" $ show warnings - logOutput "models" $ showModelNames prismaSchemaAst + logOutput "models" $ show $ getModelNames prismaSchemaAst modify (State.prismaSchemaAst .~ prismaSchemaAst) where logOutput :: String -> String -> ServerM () diff --git a/waspc/waspls/src/Wasp/LSP/Prisma/Util.hs b/waspc/waspls/src/Wasp/LSP/Prisma/Util.hs deleted file mode 100644 index 6dbe983765..0000000000 --- a/waspc/waspls/src/Wasp/LSP/Prisma/Util.hs +++ /dev/null @@ -1,13 +0,0 @@ -module Wasp.LSP.Prisma.Util - ( showModelNames, - ) -where - -import qualified Wasp.Psl.Ast.Model as Psl.Model -import qualified Wasp.Psl.Ast.Schema as Psl.Schema - -showModelNames :: Psl.Schema.Schema -> String -showModelNames = unwords . getModelNames - -getModelNames :: Psl.Schema.Schema -> [String] -getModelNames = fmap (\(Psl.Model.Model name _) -> name) . Psl.Schema.getModels