npm installation now has nice progress indicator.

This commit is contained in:
Martin Sosic 2022-11-25 21:32:48 +01:00
parent 5da642e8eb
commit d5d2c53d7e
4 changed files with 60 additions and 24 deletions

View File

@ -1,5 +1,6 @@
module Wasp.Generator.NpmInstall
( ensureNpmInstall,
( isNpmInstallNeeded,
installNpmDependenciesWithInstallRecord,
)
where
@ -24,11 +25,12 @@ import qualified Wasp.Generator.ServerGenerator.Setup as ServerSetup
import Wasp.Generator.WebAppGenerator as WG
import qualified Wasp.Generator.WebAppGenerator.Setup as WebAppSetup
-- | Run npm install if needed
-- | Figure out if npm install is needed.
--
-- Redundant npm installs can be avoided if the dependencies specified
-- by the user and wasp have not changed since the last time this ran.
--
-- It only does this if the dependencies described in the user wasp file are
-- Npm instal is needed only if the dependencies described in the user wasp file are
-- different from the dependencies that we just installed. To this end, this
-- code keeps track of the dependencies installed with a metadata file, which
-- it updates after each install.
@ -42,16 +44,18 @@ import qualified Wasp.Generator.WebAppGenerator.Setup as WebAppSetup
-- previous run. This would be more decoupled from the rest of the system.
-- Npm conflict handling could be ignored in that case, because it would work
-- from the record of what's in package.json.
ensureNpmInstall :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO ([GeneratorWarning], [GeneratorError])
ensureNpmInstall spec dstDir = do
isNpmInstallNeeded :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO (Either String (Maybe N.NpmDepsForFullStack))
isNpmInstallNeeded spec dstDir = do
let errorOrNpmDepsForFullStack = N.buildNpmDepsForFullStack spec (SG.npmDepsForWasp spec) (WG.npmDepsForWasp spec)
case errorOrNpmDepsForFullStack of
Left message -> return ([], [GenericGeneratorError ("npm install failed: " ++ message)])
Left message -> return $ Left $ "determining npm deps to install failed: " ++ message
Right npmDepsForFullStack -> do
needed <- isNpmInstallDifferent npmDepsForFullStack dstDir
if needed
then installNpmDependenciesWithInstallRecord npmDepsForFullStack dstDir
else return ([], [])
isInstallNeeded <- isNpmInstallDifferent npmDepsForFullStack dstDir
return $
Right $
if isInstallNeeded
then Just npmDepsForFullStack
else Nothing
-- Run npm install for desired AppSpec dependencies, recording what we installed
-- Installation may fail, in which the installation record is removed.

View File

@ -12,4 +12,4 @@ import qualified Wasp.Generator.ServerGenerator.Common as Common
installNpmDependencies :: Path' Abs (Dir ProjectRootDir) -> J.Job
installNpmDependencies projectDir = do
let serverDir = projectDir </> Common.serverRootDirInProjectRootDir
runNodeCommandAsJob serverDir "npm" ["install", "--loglevel", "info"] J.Server
runNodeCommandAsJob serverDir "npm" ["install"] J.Server

View File

@ -3,19 +3,30 @@ module Wasp.Generator.Setup
)
where
import Control.Concurrent (threadDelay)
import Control.Concurrent.Async (race)
import Control.Monad (when)
import StrongPath (Abs, Dir, Path')
import Wasp.AppSpec (AppSpec)
import Wasp.Generator.Common (ProjectRootDir)
import qualified Wasp.Generator.DbGenerator as DbGenerator
import Wasp.Generator.Monad (GeneratorError (..), GeneratorWarning (..))
import Wasp.Generator.NpmInstall (ensureNpmInstall)
import Wasp.Generator.NpmInstall (installNpmDependenciesWithInstallRecord, isNpmInstallNeeded)
import qualified Wasp.Message as Msg
runSetup :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> Msg.SendMessage -> IO ([GeneratorWarning], [GeneratorError])
runSetup spec dstDir sendMessage = do
sendMessage $ Msg.Start "Starting npm install..."
(npmInstallWarnings, npmInstallErrors) <- ensureNpmInstall spec dstDir
errorOrMaybeFullStackDeps <- isNpmInstallNeeded spec dstDir
case errorOrMaybeFullStackDeps of
Left errorMessage -> return ([], [GenericGeneratorError errorMessage])
Right maybeFullStackDeps -> do
case maybeFullStackDeps of
Nothing -> return ([], [])
Just fullStackDeps -> do
sendMessage $ Msg.Start "Starting npm install... (this may take a couple of minutes the very first time)"
(Left (npmInstallWarnings, npmInstallErrors)) <-
installNpmDependenciesWithInstallRecord fullStackDeps dstDir
`race` reportInstallationProgress reportInstallationProgressMessages
if null npmInstallErrors
then do
sendMessage $ Msg.Success "Successfully completed npm install."
@ -25,3 +36,24 @@ runSetup spec dstDir sendMessage = do
return (npmInstallWarnings ++ dbGeneratorWarnings, dbGeneratorErrors)
else do
return (npmInstallWarnings, npmInstallErrors)
where
reportInstallationProgress :: [String] -> IO ()
reportInstallationProgress messages = do
threadDelay $ secToMicroSec 5
putStrLn $ "\n\n ..." ++ head messages
threadDelay $ secToMicroSec 5
reportInstallationProgress $ if hasLessThan2Elems messages then messages else drop 1 messages
reportInstallationProgressMessages =
[ "Still installing npm dependencies!",
"Installation is taking a bit longer, but we will get there!",
"Yup, still not done installing.",
"Installation going great - we will get there soon!",
"We are getting closer and closer, soon it will all be installed!",
"You still waiting for installation to finish? You should! We got too far to give up now!",
"You waited so patiently, wait just a bit more (for installation to finish)..."
]
secToMicroSec = (* 1000000)
hasLessThan2Elems = null . drop 1

View File

@ -12,4 +12,4 @@ import qualified Wasp.Generator.WebAppGenerator.Common as Common
installNpmDependencies :: Path' Abs (Dir ProjectRootDir) -> J.Job
installNpmDependencies projectDir = do
let webAppDir = projectDir </> Common.webAppRootDirInProjectRootDir
runNodeCommandAsJob webAppDir "npm" ["install", "--loglevel", "info"] J.WebApp
runNodeCommandAsJob webAppDir "npm" ["install"] J.WebApp