This commit is contained in:
Martin Sosic 2024-01-18 19:12:13 +01:00
parent 79bf3cdfb4
commit 754fb29927
9 changed files with 83 additions and 62 deletions

View File

@ -7,5 +7,15 @@
"devDependencies": {
"@types/react": "^18.0.37",
"prisma": "4.16.2"
}
},
"//": [
"COMMENTS:",
{
"devDependencies.prisma": [
"We on purpose specify exact version for prisma.",
"Check this GH comment for the reasoning behind it:",
"https://github.com/wasp-lang/wasp/pull/634#issuecomment-1158802302 ."
]
}
],
}

View File

@ -14,6 +14,8 @@ import GHC.Generics
data Dependency = Dependency
{ name :: String,
-- | NOTE: By npm docs, this can be semver version range,
-- but it can also be a URL (tarball, git or Github), or a local file path.
version :: String
}
deriving (Show, Eq, Data, Generic)

View File

@ -1,10 +1,8 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TemplateHaskell #-}
module Wasp.AppSpec.PackageJson where
import Control.Applicative (liftA2)
import Data.Aeson.TH
import Data.Aeson (FromJSON)
import Data.Map (Map)
import qualified Data.Map as M
import GHC.Generics (Generic)
@ -12,20 +10,16 @@ import Wasp.AppSpec.App.Dependency (Dependency)
import qualified Wasp.AppSpec.App.Dependency as D
data PackageJson = PackageJson
{ _name :: !String,
-- todo(filip): do this properly once you merge martin's PR
_dependencies :: !(Map String String),
_devDependencies :: !(Map String String)
{ name :: !String,
dependencies :: !(Map String String),
devDependencies :: !(Map String String)
}
deriving (Show, Generic)
$(deriveJSON defaultOptions {fieldLabelModifier = drop 1} ''PackageJson)
instance FromJSON PackageJson
dependencies :: PackageJson -> [Dependency]
dependencies packageJson = D.fromList $ M.toList $ _dependencies packageJson
getDependencies :: PackageJson -> [Dependency]
getDependencies packageJson = D.fromList $ M.toList $ dependencies packageJson
devDependencies :: PackageJson -> [Dependency]
devDependencies packageJson = D.fromList $ M.toList $ _devDependencies packageJson
allDependencies :: PackageJson -> [Dependency]
allDependencies = liftA2 (++) dependencies devDependencies
getDevDependencies :: PackageJson -> [Dependency]
getDevDependencies packageJson = D.fromList $ M.toList $ devDependencies packageJson

View File

@ -172,8 +172,8 @@ makeJobMessagePrefix jobMsg =
(T.pack . buildPrefix . concat)
[ [("[", jobStyles)],
[(jobName, jobStyles)],
[("]", jobStyles)],
styledFlags
styledFlags,
[("]", jobStyles)]
]
where
buildPrefix :: [StyledText] -> String
@ -187,10 +187,10 @@ makeJobMessagePrefix jobMsg =
minPrefixLength = 10
(jobName, jobStyles) = case J._jobType jobMsg of
J.Wasp -> (" Wasp ", [Term.Yellow])
J.Wasp -> ("Wasp", [Term.Yellow])
J.Server -> ("Server", [Term.Magenta])
J.WebApp -> ("Client", [Term.Cyan])
J.Db -> (" Db ", [Term.Blue])
J.Db -> ("Db", [Term.Blue])
styledFlags :: [StyledText]
styledFlags =

View File

@ -107,10 +107,9 @@ buildNpmDepsForFullStack spec forServer forWebApp =
getUserNpmDepsForPackage :: AppSpec -> NpmDepsForUser
getUserNpmDepsForPackage spec =
NpmDepsForUser
{ -- todo(filip): what if package.json has no dependencies field?
userDependencies = AS.PackageJson.dependencies $ AS.packageJson spec,
{ userDependencies = AS.PackageJson.getDependencies $ AS.packageJson spec,
-- Should we allow user devDependencies? https://github.com/wasp-lang/wasp/issues/456
userDevDependencies = AS.PackageJson.devDependencies $ AS.packageJson spec
userDevDependencies = AS.PackageJson.getDevDependencies $ AS.packageJson spec
}
conflictErrorToMessage :: DependencyConflictError -> String

View File

@ -10,6 +10,7 @@ import Control.Monad (when)
import Control.Monad.IO.Class (liftIO)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as B
import Data.Functor ((<&>))
import qualified Data.Text as T
import StrongPath (Abs, Dir, File', Path', Rel, relfile, (</>))
import qualified StrongPath as SP
@ -130,12 +131,14 @@ reportInstallationProgress chan jobType = reportPeriodically allPossibleMessages
]
installNpmDependenciesAndReport :: Job -> Chan JobMessage -> JobType -> IO ExitCode
installNpmDependenciesAndReport installF chan jobType = do
installNpmDependenciesAndReport installJob chan jobType = do
writeChan chan $ J.JobMessage {J._data = J.JobOutput "Starting npm install\n" J.Stdout, J._jobType = jobType}
result <- installF chan `race` reportInstallationProgress chan jobType
result <- installJob chan `race` reportInstallationProgress chan jobType
case result of
Left exitCode -> return exitCode
Right _ -> error "This should be impossible"
Right _ -> error "This should never happen, reporting installation progress should run forever."
{- HLINT ignore installNpmDependencies "Redundant <$>" -}
-- Run the individual `npm install` commands for both server and webapp projects
-- It runs these concurrently, collects the output produced by these commands
@ -143,25 +146,34 @@ installNpmDependenciesAndReport installF chan jobType = do
installNpmDependencies :: Path' Abs (Dir WaspProjectDir) -> Path' Abs (Dir ProjectRootDir) -> IO (Either String ())
installNpmDependencies projectDir dstDir = do
messagesChan <- newChan
(_, exitCode) <-
concurrently
(handleProjectInstallMessage messagesChan)
(installNpmDependenciesAndReport (SdkGenerator.installNpmDependencies projectDir) messagesChan J.Wasp)
case exitCode of
installProjectNpmDependencies messagesChan projectDir >>= \case
ExitFailure code -> return $ Left $ "Project setup failed with exit code " ++ show code ++ "."
_ -> do
let handleMessagesJob = handleJobMessages messagesChan
let runSetupJobs =
concurrently
(installNpmDependenciesAndReport (ServerSetup.installNpmDependencies dstDir) messagesChan J.Server)
(installNpmDependenciesAndReport (WebAppSetup.installNpmDependencies dstDir) messagesChan J.WebApp)
(_, results) <- concurrently handleMessagesJob runSetupJobs
case results of
(ExitSuccess, ExitSuccess) -> return $ Right ()
exitCodes -> return $ Left $ setupFailedMessage exitCodes
_success -> do
installWebAppAndServerNpmDependencies messagesChan dstDir <&> \case
(ExitSuccess, ExitSuccess) -> Right ()
exitCodes -> Left $ setupFailedMessage exitCodes
where
handleProjectInstallMessage :: Chan J.JobMessage -> IO ()
handleProjectInstallMessage = runPrefixedWriter . processMessages
setupFailedMessage (serverExitCode, webAppExitCode) =
let serverErrorMessage = case serverExitCode of
ExitFailure code -> " Server setup failed with exit code " ++ show code ++ "."
_success -> ""
webAppErrorMessage = case webAppExitCode of
ExitFailure code -> " Web app setup failed with exit code " ++ show code ++ "."
_success -> ""
in "Setup failed!" ++ serverErrorMessage ++ webAppErrorMessage
installProjectNpmDependencies ::
Chan JobMessage -> SP.Path SP.System Abs (Dir WaspProjectDir) -> IO ExitCode
installProjectNpmDependencies messagesChan projectDir =
snd <$> handleProjectInstallMessages messagesChan `concurrently` installProjectDepsJob
where
installProjectDepsJob =
installNpmDependenciesAndReport
(SdkGenerator.installNpmDependencies projectDir)
messagesChan
J.Wasp
handleProjectInstallMessages :: Chan J.JobMessage -> IO ()
handleProjectInstallMessages = runPrefixedWriter . processMessages
where
processMessages :: Chan J.JobMessage -> PrefixedWriter ()
processMessages chan = do
@ -169,7 +181,16 @@ installNpmDependencies projectDir dstDir = do
case J._data jobMsg of
J.JobOutput {} -> printJobMessagePrefixed jobMsg >> processMessages chan
J.JobExit {} -> return ()
handleJobMessages = runPrefixedWriter . processMessages (False, False)
installWebAppAndServerNpmDependencies ::
Chan JobMessage -> SP.Path SP.System Abs (Dir ProjectRootDir) -> IO (ExitCode, ExitCode)
installWebAppAndServerNpmDependencies messagesChan dstDir =
snd <$> handleSetupJobsMessages messagesChan `concurrently` (installServerDepsJob `concurrently` installWebAppDepsJob)
where
installServerDepsJob = installNpmDependenciesAndReport (ServerSetup.installNpmDependencies dstDir) messagesChan J.Server
installWebAppDepsJob = installNpmDependenciesAndReport (WebAppSetup.installNpmDependencies dstDir) messagesChan J.WebApp
handleSetupJobsMessages = runPrefixedWriter . processMessages (False, False)
where
processMessages :: (Bool, Bool) -> Chan J.JobMessage -> PrefixedWriter ()
processMessages (True, True) _ = return ()
@ -182,14 +203,5 @@ installNpmDependencies projectDir dstDir = do
J.JobExit {} -> case J._jobType jobMsg of
J.WebApp -> processMessages (True, isServerDone) chan
J.Server -> processMessages (isWebAppDone, True) chan
J.Db -> error "This should never happen. No db job should be active."
J.Wasp -> error "This should never happen. No db job should be active."
setupFailedMessage (serverExitCode, webAppExitCode) =
let serverErrorMessage = case serverExitCode of
ExitFailure code -> " Server setup failed with exit code " ++ show code ++ "."
_ -> ""
webAppErrorMessage = case webAppExitCode of
ExitFailure code -> " Web app setup failed with exit code " ++ show code ++ "."
_ -> ""
in "Setup failed!" ++ serverErrorMessage ++ webAppErrorMessage
J.Db -> error "This should never happen. No Db job should be active."
J.Wasp -> error "This should never happen. No Wasp job should be active."

View File

@ -92,8 +92,8 @@ sdkRootDirInProjectRootDir = [reldir|sdk/wasp|]
sdkTemplatesDirInTemplatesDir :: Path' (Rel TemplatesDir) (Dir SdkTemplatesDir)
sdkTemplatesDirInTemplatesDir = [reldir|sdk|]
-- todo(filip): figure out where this belongs
-- also, fix imports for wasp project
-- TODO(filip): Figure out where this belongs. Check https://github.com/wasp-lang/wasp/pull/1602#discussion_r1437144166 .
-- Also, fix imports for wasp project.
installNpmDependencies :: Path' Abs (Dir WaspProjectDir) -> J.Job
installNpmDependencies projectDir =
runNodeCommandAsJob projectDir "npm" ["install"] J.Wasp

View File

@ -10,7 +10,7 @@ where
import Control.Arrow (ArrowChoice (left))
import qualified Data.Aeson as Aeson
import Data.List (find, isSuffixOf)
import StrongPath (Abs, Dir, File', Path', toFilePath, (</>))
import StrongPath (Abs, Dir, File', Path', Rel, toFilePath, (</>))
import StrongPath.TH (relfile)
import qualified Wasp.Analyzer as Analyzer
import Wasp.Analyzer.AnalyzeError (getErrorMessageAndCtx)
@ -24,7 +24,7 @@ import qualified Wasp.ConfigFile as CF
import Wasp.Error (showCompilerErrorForTerminal)
import qualified Wasp.ExternalCode as ExternalCode
import qualified Wasp.Generator.ConfigFile as G.CF
import Wasp.Project.Common (CompileError, CompileWarning, WaspProjectDir, findFileInWaspProjectDir)
import Wasp.Project.Common (CompileError, CompileWarning, WaspProjectDir, findFileInWaspProjectDir, packageJsonInWaspProjectDir)
import Wasp.Project.Db (makeDevDatabaseUrl)
import Wasp.Project.Db.Migrations (findMigrationsDir)
import Wasp.Project.Deployment (loadUserDockerfileContents)
@ -127,9 +127,9 @@ analyzePackageJsonContent waspProjectDir =
Nothing -> return $ Left [fileNotFoundMessage]
where
fileNotFoundMessage = "couldn't find package.json file in the " ++ toFilePath waspProjectDir ++ " directory"
findPackageJsonFile = findFileInWaspProjectDir waspProjectDir [relfile|package.json|]
findPackageJsonFile = findFileInWaspProjectDir waspProjectDir packageJsonInWaspProjectDir
readPackageJsonFile :: Path' Abs File' -> IO (Either [CompileError] PackageJson)
readPackageJsonFile packageJsonFile = do
byteString <- IOUtil.readFileBytes packageJsonFile
return $ maybeToEither ["Error reading the package.json file"] $ Aeson.decode byteString
return $ maybeToEither ["Error parsing the package.json file"] $ Aeson.decode byteString

View File

@ -3,10 +3,11 @@ module Wasp.Project.Common
CompileError,
CompileWarning,
WaspProjectDir,
packageJsonInWaspProjectDir,
)
where
import StrongPath (Abs, Dir, File', Path', Rel, toFilePath, (</>))
import StrongPath (Abs, Dir, File', Path', Rel, relfile, toFilePath, (</>))
import System.Directory (doesFileExist)
data WaspProjectDir -- Root dir of Wasp project, containing source files.
@ -15,6 +16,9 @@ type CompileError = String
type CompileWarning = String
packageJsonInWaspProjectDir :: Path' (Rel WaspProjectDir) File'
packageJsonInWaspProjectDir = [relfile|package.json|]
findFileInWaspProjectDir ::
Path' Abs (Dir WaspProjectDir) ->
Path' (Rel WaspProjectDir) File' ->