mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-23 19:29:17 +03:00
Introduce combinePackageDependencies
This is the base operation used now to combine package dependencies. It knows about dev dependencies too. I've adjusted the code for both web app and server to use this. I've factored out commonalities in error handling into PackageJsonGenerator We now support devDependencies for web app and in the deps command We also have nicer functions to generate the package json entries directly from the PackageJsonDependencies structure
This commit is contained in:
parent
9546dcf200
commit
27b3c83f7a
@ -7,6 +7,7 @@ import Control.Monad.IO.Class (liftIO)
|
||||
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
|
||||
import Wasp.Cli.Command (Command)
|
||||
import Wasp.Cli.Terminal (title)
|
||||
import qualified Wasp.Generator.PackageJsonGenerator as PJG
|
||||
import qualified Wasp.Generator.ServerGenerator as ServerGenerator
|
||||
import qualified Wasp.Generator.WebAppGenerator as WebAppGenerator
|
||||
import qualified Wasp.Util.Terminal as Term
|
||||
@ -18,14 +19,31 @@ deps =
|
||||
unlines $
|
||||
[ "",
|
||||
title "Below are listed the dependencies that Wasp uses in your project. You can import and use these directly in the code as if you specified them yourself, but you can't change their versions.",
|
||||
"",
|
||||
title "Server dependencies:"
|
||||
""
|
||||
]
|
||||
++ map printDep ServerGenerator.waspNpmDeps
|
||||
++ [ "",
|
||||
title "Webapp dependencies:"
|
||||
]
|
||||
++ map printDep WebAppGenerator.waspNpmDeps
|
||||
++ printDeps
|
||||
"Server dependencies:"
|
||||
( PJG.dependencies ServerGenerator.waspPackageJsonDependencies
|
||||
)
|
||||
++ [""]
|
||||
++ printDeps
|
||||
"Server devDependencies:"
|
||||
( PJG.devDependencies ServerGenerator.waspPackageJsonDependencies
|
||||
)
|
||||
++ [""]
|
||||
++ printDeps
|
||||
"Webapp dependencies:"
|
||||
( PJG.dependencies WebAppGenerator.waspPackageJsonDependencies
|
||||
)
|
||||
++ [""]
|
||||
++ printDeps
|
||||
"Webapp devDependencies:"
|
||||
( PJG.devDependencies WebAppGenerator.waspPackageJsonDependencies
|
||||
)
|
||||
|
||||
printDeps :: String -> [AS.Dependency.Dependency] -> [String]
|
||||
printDeps dependenciesTitle dependencies =
|
||||
title dependenciesTitle : map printDep dependencies
|
||||
|
||||
printDep :: AS.Dependency.Dependency -> String
|
||||
printDep dep =
|
||||
|
@ -4,6 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
{=& depsChunk =},
|
||||
{=& devDepsChunk =},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
|
@ -1,104 +1,146 @@
|
||||
module Wasp.Generator.PackageJsonGenerator
|
||||
( resolveNpmDeps,
|
||||
resolveDependencies,
|
||||
DependencyConflictError (DependencyConflictError),
|
||||
npmDepsToPackageJsonEntry,
|
||||
npmDevDepsToPackageJsonEntry,
|
||||
( DependencyConflictError (..),
|
||||
getDependenciesPackageJsonEntry,
|
||||
getDevDependenciesPackageJsonEntry,
|
||||
combinePackageJsonDependencies,
|
||||
PackageJsonDependencies (..),
|
||||
PackageJsonDependenciesError (..),
|
||||
conflictErrorToMessage,
|
||||
genPackageJsonDependencies,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Arrow (ArrowChoice (left))
|
||||
import Data.List (intercalate)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Maybe (fromMaybe)
|
||||
import qualified Data.Maybe as Maybe
|
||||
import Wasp.AppSpec (AppSpec)
|
||||
import qualified Wasp.AppSpec as AS
|
||||
import qualified Wasp.AppSpec.App as AS.App
|
||||
import qualified Wasp.AppSpec.App.Dependency as D
|
||||
import Wasp.Generator.Monad (Generator, GeneratorError (..), logAndThrowGeneratorError)
|
||||
|
||||
type NpmDependenciesConflictError = String
|
||||
|
||||
-- First entry is Wasp dependency, the second is the user Dependency that is
|
||||
-- in conflict with it.
|
||||
data DependencyConflictError = DependencyConflictError D.Dependency D.Dependency
|
||||
data PackageJsonDependencies = PackageJsonDependencies
|
||||
{ dependencies :: [D.Dependency],
|
||||
devDependencies :: [D.Dependency]
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
data PackageJsonDependenciesError = PackageJsonDependenciesError
|
||||
{ dependenciesConflictErrors :: [DependencyConflictError],
|
||||
devDependenciesConflictErrors :: [DependencyConflictError]
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
data DependencyConflictError = DependencyConflictError
|
||||
{ waspDependency :: D.Dependency,
|
||||
userDependency :: D.Dependency
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- | Generate a PackageJsonDependencies by combining wask dependencies with user dependencies
|
||||
-- derived from AppSpec, or if there are conflicts, fail with error messages.
|
||||
genPackageJsonDependencies :: AppSpec -> PackageJsonDependencies -> Generator PackageJsonDependencies
|
||||
genPackageJsonDependencies spec waspDependencies =
|
||||
case combinePackageJsonDependencies waspDependencies userDependencies of
|
||||
Right deps -> return deps
|
||||
Left conflictErrorDeps ->
|
||||
logAndThrowGeneratorError $
|
||||
GenericGeneratorError $
|
||||
intercalate " ; " $
|
||||
map
|
||||
conflictErrorToMessage
|
||||
( dependenciesConflictErrors conflictErrorDeps
|
||||
++ devDependenciesConflictErrors conflictErrorDeps
|
||||
)
|
||||
where
|
||||
userDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = fromMaybe [] $ AS.App.dependencies $ snd $ AS.getApp spec,
|
||||
-- Should we allow user devDependencies? https://github.com/wasp-lang/wasp/issues/456
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
conflictErrorToMessage :: DependencyConflictError -> String
|
||||
conflictErrorToMessage DependencyConflictError {waspDependency = waspDep, userDependency = userDep} =
|
||||
"Error: Dependency conflict for user dependency ("
|
||||
++ D.name userDep
|
||||
++ ", "
|
||||
++ D.version userDep
|
||||
++ "): "
|
||||
++ "Version must be set to the exactly the same version as"
|
||||
++ " the one wasp is using: "
|
||||
++ D.version waspDep
|
||||
|
||||
-- | Takes wasp npm dependencies and user npm dependencies and figures out how
|
||||
-- to combine them together, returning (Right) a list of npm dependencies to
|
||||
-- be used on behalf of wasp and then also a list of npm dependencies to be
|
||||
-- used on behalf of user. These lists might be the same as the initial ones,
|
||||
-- but might also be different.
|
||||
|
||||
-- On error (Left), returns a list of conflicting wasp dep, user dep pairs.
|
||||
resolveDependencies ::
|
||||
[D.Dependency] ->
|
||||
[D.Dependency] ->
|
||||
Either
|
||||
[DependencyConflictError]
|
||||
([D.Dependency], [D.Dependency])
|
||||
resolveDependencies waspDeps userDeps =
|
||||
if null conflictErrors
|
||||
then Right (waspDeps, Map.elems userDepsNotInWaspDeps)
|
||||
else Left conflictErrors
|
||||
-- to combine them together, returning (Right) a new PackageJsonDependencies
|
||||
-- that combines them, and on error (Left), returns a PackageJsonDependenciesError
|
||||
-- which describes which dependencies are in conflict.
|
||||
combinePackageJsonDependencies :: PackageJsonDependencies -> PackageJsonDependencies -> Either PackageJsonDependenciesError PackageJsonDependencies
|
||||
combinePackageJsonDependencies waspDependencies userDependencies =
|
||||
if null conflictErrors && null devConflictErrors
|
||||
then
|
||||
Right $
|
||||
PackageJsonDependencies
|
||||
{ dependencies = dependencies waspDependencies ++ remainingUserDeps,
|
||||
devDependencies = devDependencies waspDependencies ++ remainingUserDevDeps
|
||||
}
|
||||
else
|
||||
Left $
|
||||
PackageJsonDependenciesError
|
||||
{ dependenciesConflictErrors = conflictErrors,
|
||||
devDependenciesConflictErrors = devConflictErrors
|
||||
}
|
||||
where
|
||||
waspDepsByName = makeDepsByName $ dependencies waspDependencies
|
||||
waspDevDepsByName = makeDepsByName $ devDependencies waspDependencies
|
||||
userDepsByName = makeDepsByName $ dependencies userDependencies
|
||||
userDevDepsByName = makeDepsByName $ devDependencies userDependencies
|
||||
allWaspDepsByName = waspDepsByName `Map.union` waspDevDepsByName
|
||||
conflictErrors = determineConflictErrors allWaspDepsByName userDepsByName
|
||||
devConflictErrors = determineConflictErrors allWaspDepsByName userDevDepsByName
|
||||
remainingUserDeps = getRemainingUserDeps allWaspDepsByName userDepsByName
|
||||
remainingUserDevDeps = getRemainingUserDeps allWaspDepsByName userDevDepsByName
|
||||
|
||||
type DepsByName = Map.Map String D.Dependency
|
||||
|
||||
-- Given a map of wasp dependencies and a map of user dependencies, construct a
|
||||
-- list of conflict errors for any dependencies that have a matching name but
|
||||
-- different version.
|
||||
determineConflictErrors :: DepsByName -> DepsByName -> [DependencyConflictError]
|
||||
determineConflictErrors waspDepsByName userDepsByName =
|
||||
Maybe.mapMaybe makeConflictErrorIfMismatchedVersion (Map.toAscList overlappingDeps)
|
||||
where
|
||||
waspDepsByName = makeDepsByName waspDeps
|
||||
userDepsByName = makeDepsByName userDeps
|
||||
overlappingDeps = waspDepsByName `Map.intersection` userDepsByName
|
||||
userDepsNotInWaspDeps = userDepsByName `Map.difference` waspDepsByName
|
||||
makeDepsByName :: [D.Dependency] -> Map.Map String D.Dependency
|
||||
makeDepsByName = Map.fromList . fmap (\d -> (D.name d, d))
|
||||
makeConflictErrorIfMismatchedVersion :: (String, D.Dependency) -> Maybe DependencyConflictError
|
||||
makeConflictErrorIfMismatchedVersion (waspDepName, waspDep) = do
|
||||
userDep <- Map.lookup waspDepName userDepsByName
|
||||
if D.version waspDep /= D.version userDep
|
||||
then Just $ DependencyConflictError waspDep userDep
|
||||
else Nothing
|
||||
|
||||
-- get all items in overlappingDeps, for each check whether there's
|
||||
-- a conflicting version in userDepsMap, and if so report a conflict
|
||||
conflictErrors :: [DependencyConflictError]
|
||||
conflictErrors = Maybe.mapMaybe makeConflictErrorIfMismatchedVersion (Map.toAscList overlappingDeps)
|
||||
where
|
||||
makeConflictErrorIfMismatchedVersion :: (String, D.Dependency) -> Maybe DependencyConflictError
|
||||
makeConflictErrorIfMismatchedVersion (waspDepName, waspDep) = do
|
||||
userDep <- Map.lookup waspDepName userDepsByName
|
||||
if D.version waspDep /= D.version userDep
|
||||
then
|
||||
Just $
|
||||
DependencyConflictError
|
||||
waspDep
|
||||
userDep
|
||||
else Nothing
|
||||
-- Given a map of wasp dependencies and a map of user dependencies, construct a
|
||||
-- a list of user dependencies that remain once any overlapping wasp dependencies
|
||||
-- have been removed. This assumes conflict detection was already passed.
|
||||
getRemainingUserDeps :: DepsByName -> DepsByName -> [D.Dependency]
|
||||
getRemainingUserDeps waspDepsByName userDepsByName = Map.elems $ userDepsByName `Map.difference` waspDepsByName
|
||||
|
||||
-- | Takes wasp npm dependencies and user npm dependencies and figures out how to
|
||||
-- combine them together, returning (Right) a list of npm dependencies to be used on
|
||||
-- behalf of wasp and then also a list of npm dependencies to be used on behalf
|
||||
-- of user. These lists might be the same as the initial ones, but might also
|
||||
-- be different.
|
||||
-- On error (Left), returns list of conflicting user deps together with the error message
|
||||
-- explaining what the error is.
|
||||
resolveNpmDeps ::
|
||||
[D.Dependency] ->
|
||||
[D.Dependency] ->
|
||||
Either
|
||||
[(D.Dependency, NpmDependenciesConflictError)]
|
||||
([D.Dependency], [D.Dependency])
|
||||
resolveNpmDeps waspDeps userDeps = left (map convertConflict) $ resolveDependencies waspDeps userDeps
|
||||
where
|
||||
convertConflict :: DependencyConflictError -> (D.Dependency, NpmDependenciesConflictError)
|
||||
convertConflict (DependencyConflictError waspDep userDep) =
|
||||
( userDep,
|
||||
"Error: Dependency conflict for user dependency ("
|
||||
++ D.name userDep
|
||||
++ ", "
|
||||
++ D.version userDep
|
||||
++ "): "
|
||||
++ "Version must be set to the exactly the same version as"
|
||||
++ " the one wasp is using: "
|
||||
++ D.version waspDep
|
||||
)
|
||||
-- Construct a map of dependency keyed by dependency name.
|
||||
makeDepsByName :: [D.Dependency] -> DepsByName
|
||||
makeDepsByName = Map.fromList . fmap (\d -> (D.name d, d))
|
||||
|
||||
npmDepsToPackageJsonEntryWithKey :: [D.Dependency] -> String -> String
|
||||
npmDepsToPackageJsonEntryWithKey deps key =
|
||||
-- | Construct dependencies entry in package.json
|
||||
getDependenciesPackageJsonEntry :: PackageJsonDependencies -> String
|
||||
getDependenciesPackageJsonEntry = dependenciesToPackageJsonEntryWithKey "dependencies" . dependencies
|
||||
|
||||
-- | Construct devDependencies entry in package.json
|
||||
getDevDependenciesPackageJsonEntry :: PackageJsonDependencies -> String
|
||||
getDevDependenciesPackageJsonEntry = dependenciesToPackageJsonEntryWithKey "devDependencies" . devDependencies
|
||||
|
||||
dependenciesToPackageJsonEntryWithKey :: String -> [D.Dependency] -> String
|
||||
dependenciesToPackageJsonEntryWithKey key deps =
|
||||
"\""
|
||||
++ key
|
||||
++ "\": {"
|
||||
++ intercalate ",\n " (map (\dep -> "\"" ++ D.name dep ++ "\": \"" ++ D.version dep ++ "\"") deps)
|
||||
++ "\n}"
|
||||
|
||||
npmDepsToPackageJsonEntry :: [D.Dependency] -> String
|
||||
npmDepsToPackageJsonEntry deps = npmDepsToPackageJsonEntryWithKey deps "dependencies"
|
||||
|
||||
npmDevDepsToPackageJsonEntry :: [D.Dependency] -> String
|
||||
npmDevDepsToPackageJsonEntry deps = npmDepsToPackageJsonEntryWithKey deps "devDependencies"
|
||||
++ "\n}"
|
@ -4,14 +4,12 @@ module Wasp.Generator.ServerGenerator
|
||||
( genServer,
|
||||
preCleanup,
|
||||
operationsRouteInRootRouter,
|
||||
waspNpmDeps,
|
||||
waspNpmDevDeps,
|
||||
waspPackageJsonDependencies,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad (unless)
|
||||
import Data.Aeson (object, (.=))
|
||||
import Data.List (intercalate)
|
||||
import Data.Maybe
|
||||
( fromJust,
|
||||
fromMaybe,
|
||||
@ -34,12 +32,8 @@ import Wasp.Generator.ExternalCodeGenerator (generateExternalCodeDir)
|
||||
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
||||
import Wasp.Generator.FileDraft (FileDraft, createCopyFileDraft)
|
||||
import Wasp.Generator.JsImport (getJsImportDetailsForExtFnImport)
|
||||
import Wasp.Generator.Monad (Generator, GeneratorError (..), logAndThrowGeneratorError)
|
||||
import Wasp.Generator.PackageJsonGenerator
|
||||
( npmDepsToPackageJsonEntry,
|
||||
npmDevDepsToPackageJsonEntry,
|
||||
resolveNpmDeps,
|
||||
)
|
||||
import Wasp.Generator.Monad (Generator)
|
||||
import qualified Wasp.Generator.PackageJsonGenerator as PJG
|
||||
import Wasp.Generator.ServerGenerator.AuthG (genAuth)
|
||||
import Wasp.Generator.ServerGenerator.Common
|
||||
( ServerSrcDir,
|
||||
@ -57,7 +51,7 @@ genServer :: AppSpec -> Generator [FileDraft]
|
||||
genServer spec =
|
||||
sequence
|
||||
[ genReadme,
|
||||
genPackageJson spec waspNpmDeps waspNpmDevDeps,
|
||||
genPackageJson spec waspPackageJsonDependencies,
|
||||
genNpmrc,
|
||||
genNvmrc,
|
||||
genGitignore
|
||||
@ -97,21 +91,17 @@ dotEnvInServerRootDir = [relfile|.env|]
|
||||
genReadme :: Generator FileDraft
|
||||
genReadme = return $ C.mkTmplFd (asTmplFile [relfile|README.md|])
|
||||
|
||||
genPackageJson :: AppSpec -> [AS.Dependency.Dependency] -> [AS.Dependency.Dependency] -> Generator FileDraft
|
||||
genPackageJson spec waspDeps waspDevDeps = do
|
||||
(resolvedWaspDeps, resolvedUserDeps) <-
|
||||
case resolveNpmDeps waspDeps userDeps of
|
||||
Right deps -> return deps
|
||||
Left depsAndErrors -> logAndThrowGeneratorError $ GenericGeneratorError $ intercalate " ; " $ map snd depsAndErrors
|
||||
|
||||
genPackageJson :: AppSpec -> PJG.PackageJsonDependencies -> Generator FileDraft
|
||||
genPackageJson spec waspDependencies = do
|
||||
combinedDependencies <- PJG.genPackageJsonDependencies spec waspDependencies
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|package.json|])
|
||||
(asServerFile [relfile|package.json|])
|
||||
( Just $
|
||||
object
|
||||
[ "depsChunk" .= npmDepsToPackageJsonEntry (resolvedWaspDeps ++ resolvedUserDeps),
|
||||
"devDepsChunk" .= npmDevDepsToPackageJsonEntry waspDevDeps,
|
||||
[ "depsChunk" .= PJG.getDependenciesPackageJsonEntry combinedDependencies,
|
||||
"devDepsChunk" .= PJG.getDevDependenciesPackageJsonEntry combinedDependencies,
|
||||
"nodeVersion" .= nodeVersionAsText,
|
||||
"startProductionScript"
|
||||
.= ( (if not (null $ AS.getDecls @AS.Entity.Entity spec) then "npm run db-migrate-prod && " else "")
|
||||
@ -119,32 +109,30 @@ genPackageJson spec waspDeps waspDevDeps = do
|
||||
)
|
||||
]
|
||||
)
|
||||
where
|
||||
userDeps :: [AS.Dependency.Dependency]
|
||||
userDeps = fromMaybe [] $ AS.App.dependencies $ snd $ AS.getApp spec
|
||||
|
||||
waspNpmDeps :: [AS.Dependency.Dependency]
|
||||
waspNpmDeps =
|
||||
AS.Dependency.fromList
|
||||
[ ("cookie-parser", "~1.4.4"),
|
||||
("cors", "^2.8.5"),
|
||||
("debug", "~2.6.9"),
|
||||
("express", "~4.16.1"),
|
||||
("morgan", "~1.9.1"),
|
||||
("@prisma/client", prismaVersion),
|
||||
("jsonwebtoken", "^8.5.1"),
|
||||
("secure-password", "^4.0.0"),
|
||||
("dotenv", "8.2.0"),
|
||||
("helmet", "^4.6.0")
|
||||
]
|
||||
|
||||
waspNpmDevDeps :: [AS.Dependency.Dependency]
|
||||
waspNpmDevDeps =
|
||||
AS.Dependency.fromList
|
||||
[ ("nodemon", "^2.0.4"),
|
||||
("standard", "^14.3.4"),
|
||||
("prisma", prismaVersion)
|
||||
]
|
||||
waspPackageJsonDependencies :: PJG.PackageJsonDependencies
|
||||
waspPackageJsonDependencies =
|
||||
PJG.PackageJsonDependencies
|
||||
{ PJG.dependencies =
|
||||
AS.Dependency.fromList
|
||||
[ ("cookie-parser", "~1.4.4"),
|
||||
("cors", "^2.8.5"),
|
||||
("debug", "~2.6.9"),
|
||||
("express", "~4.16.1"),
|
||||
("morgan", "~1.9.1"),
|
||||
("@prisma/client", prismaVersion),
|
||||
("jsonwebtoken", "^8.5.1"),
|
||||
("secure-password", "^4.0.0"),
|
||||
("dotenv", "8.2.0"),
|
||||
("helmet", "^4.6.0")
|
||||
],
|
||||
PJG.devDependencies =
|
||||
AS.Dependency.fromList
|
||||
[ ("nodemon", "^2.0.4"),
|
||||
("standard", "^14.3.4"),
|
||||
("prisma", prismaVersion)
|
||||
]
|
||||
}
|
||||
|
||||
genNpmrc :: Generator FileDraft
|
||||
genNpmrc =
|
||||
|
@ -1,12 +1,11 @@
|
||||
module Wasp.Generator.WebAppGenerator
|
||||
( generateWebApp,
|
||||
waspNpmDeps,
|
||||
waspPackageJsonDependencies,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson (object, (.=))
|
||||
import Data.List (intercalate)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import StrongPath
|
||||
( Dir,
|
||||
Path',
|
||||
@ -21,11 +20,8 @@ import qualified Wasp.AppSpec.App as AS.App
|
||||
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
|
||||
import Wasp.Generator.ExternalCodeGenerator (generateExternalCodeDir)
|
||||
import Wasp.Generator.FileDraft
|
||||
import Wasp.Generator.Monad (Generator, GeneratorError (..), logAndThrowGeneratorError)
|
||||
import Wasp.Generator.PackageJsonGenerator
|
||||
( npmDepsToPackageJsonEntry,
|
||||
resolveNpmDeps,
|
||||
)
|
||||
import Wasp.Generator.Monad (Generator)
|
||||
import qualified Wasp.Generator.PackageJsonGenerator as PJG
|
||||
import qualified Wasp.Generator.WebAppGenerator.AuthG as AuthG
|
||||
import Wasp.Generator.WebAppGenerator.Common
|
||||
( asTmplFile,
|
||||
@ -42,7 +38,7 @@ generateWebApp :: AppSpec -> Generator [FileDraft]
|
||||
generateWebApp spec = do
|
||||
sequence
|
||||
[ generateReadme,
|
||||
genPackageJson spec waspNpmDeps,
|
||||
genPackageJson spec waspPackageJsonDependencies,
|
||||
generateGitignore,
|
||||
return $ C.mkTmplFd $ asTmplFile [relfile|netlify.toml|]
|
||||
]
|
||||
@ -53,13 +49,9 @@ generateWebApp spec = do
|
||||
generateReadme :: Generator FileDraft
|
||||
generateReadme = return $ C.mkTmplFd $ asTmplFile [relfile|README.md|]
|
||||
|
||||
genPackageJson :: AppSpec -> [AS.Dependency.Dependency] -> Generator FileDraft
|
||||
genPackageJson spec waspDeps = do
|
||||
(resolvedWaspDeps, resolvedUserDeps) <-
|
||||
case resolveNpmDeps waspDeps userDeps of
|
||||
Right deps -> return deps
|
||||
Left depsAndErrors -> logAndThrowGeneratorError $ GenericGeneratorError $ intercalate " ; " $ map snd depsAndErrors
|
||||
|
||||
genPackageJson :: AppSpec -> PJG.PackageJsonDependencies -> Generator FileDraft
|
||||
genPackageJson spec waspDependencies = do
|
||||
combinedDependencies <- PJG.genPackageJsonDependencies spec waspDependencies
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(C.asTmplFile [relfile|package.json|])
|
||||
@ -67,27 +59,29 @@ genPackageJson spec waspDeps = do
|
||||
( Just $
|
||||
object
|
||||
[ "appName" .= (fst (getApp spec) :: String),
|
||||
"depsChunk" .= npmDepsToPackageJsonEntry (resolvedWaspDeps ++ resolvedUserDeps)
|
||||
"depsChunk" .= PJG.getDependenciesPackageJsonEntry combinedDependencies,
|
||||
"devDepsChunk" .= PJG.getDevDependenciesPackageJsonEntry combinedDependencies
|
||||
]
|
||||
)
|
||||
where
|
||||
userDeps :: [AS.Dependency.Dependency]
|
||||
userDeps = fromMaybe [] $ AS.App.dependencies $ snd $ getApp spec
|
||||
|
||||
waspNpmDeps :: [AS.Dependency.Dependency]
|
||||
waspNpmDeps =
|
||||
AS.Dependency.fromList
|
||||
[ ("axios", "^0.21.1"),
|
||||
("lodash", "^4.17.15"),
|
||||
("react", "^16.12.0"),
|
||||
("react-dom", "^16.12.0"),
|
||||
("react-query", "^2.14.1"),
|
||||
("react-router-dom", "^5.1.2"),
|
||||
("react-scripts", "4.0.3"),
|
||||
("uuid", "^3.4.0")
|
||||
]
|
||||
|
||||
-- TODO: Also extract devDependencies like we did dependencies (waspNpmDeps).
|
||||
waspPackageJsonDependencies :: PJG.PackageJsonDependencies
|
||||
waspPackageJsonDependencies =
|
||||
PJG.PackageJsonDependencies
|
||||
{ PJG.dependencies =
|
||||
AS.Dependency.fromList
|
||||
[ ("axios", "^0.21.1"),
|
||||
("lodash", "^4.17.15"),
|
||||
("react", "^16.12.0"),
|
||||
("react-dom", "^16.12.0"),
|
||||
("react-query", "^2.14.1"),
|
||||
("react-router-dom", "^5.1.2"),
|
||||
("react-scripts", "4.0.3"),
|
||||
("uuid", "^3.4.0")
|
||||
],
|
||||
PJG.devDependencies =
|
||||
AS.Dependency.fromList
|
||||
[]
|
||||
}
|
||||
|
||||
generateGitignore :: Generator FileDraft
|
||||
generateGitignore =
|
||||
|
@ -3,69 +3,260 @@ module Generator.PackageJsonGeneratorTest where
|
||||
import Test.Tasty.Hspec
|
||||
import qualified Wasp.AppSpec.App.Dependency as D
|
||||
import Wasp.Generator.PackageJsonGenerator
|
||||
( DependencyConflictError (DependencyConflictError),
|
||||
resolveDependencies,
|
||||
resolveNpmDeps,
|
||||
)
|
||||
|
||||
spec_resolveNpmDeps :: Spec
|
||||
spec_resolveNpmDeps = do
|
||||
spec_combinePackageJsonDependencies :: Spec
|
||||
spec_combinePackageJsonDependencies = do
|
||||
let waspDeps =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
]
|
||||
|
||||
let waspDevDeps =
|
||||
D.fromList
|
||||
[ ("alpha", "10"),
|
||||
("beta", "20")
|
||||
]
|
||||
|
||||
it "a conflicting version number is detected" $ do
|
||||
let userDeps =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "3")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = []
|
||||
}
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "3")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Left
|
||||
[ DependencyConflictError
|
||||
(D.make ("b", "2"))
|
||||
(D.make ("b", "3"))
|
||||
]
|
||||
it "wasp deps completely overlap with user deps, so no user deps required" $ do
|
||||
let userDeps =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Right (waspDeps, [])
|
||||
PackageJsonDependenciesError
|
||||
{ dependenciesConflictErrors =
|
||||
[ DependencyConflictError
|
||||
(D.make ("b", "2"))
|
||||
(D.make ("b", "3"))
|
||||
],
|
||||
devDependenciesConflictErrors = []
|
||||
}
|
||||
|
||||
it "no dependency name overlap so no conflict" $ do
|
||||
let userDeps =
|
||||
D.fromList
|
||||
[ ("c", "1"),
|
||||
("d", "2")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Right (waspDeps, userDeps)
|
||||
it "wasp deps completely overlap with user deps, no duplication" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = []
|
||||
}
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
it "some dependencies names overlap, with the same version so dependency is not in user dep" $ do
|
||||
let userDeps =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("d", "2")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Right (waspDeps, [D.make ("d", "2")])
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Right
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
it "Reports error if user dep version does not match wasp dep version" $ do
|
||||
let userDeps =
|
||||
D.fromList
|
||||
[ ("a", "2"),
|
||||
("foo", "bar")
|
||||
]
|
||||
let Left conflicts = resolveNpmDeps waspDeps userDeps
|
||||
conflicts
|
||||
`shouldBe` [ ( D.make ("a", "2"),
|
||||
"Error: Dependency conflict for user dependency (a, 2): "
|
||||
++ "Version must be set to the exactly "
|
||||
++ "the same version as the one wasp is using: 1"
|
||||
)
|
||||
]
|
||||
it "user dependencies supplement wasp dependencies" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = []
|
||||
}
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("c", "3"),
|
||||
("d", "4")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Right
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2"),
|
||||
("c", "3"),
|
||||
("d", "4")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
it "user dependencies partially overlap wasp dependencies, so only non-overlapping supplement" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = []
|
||||
}
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("d", "4")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Right
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2"),
|
||||
("d", "4")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
it "report error if user dependency overlaps wasp dependency, different version" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = []
|
||||
}
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "2"),
|
||||
("foo", "bar")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Left
|
||||
PackageJsonDependenciesError
|
||||
{ dependenciesConflictErrors =
|
||||
[ DependencyConflictError
|
||||
(D.make ("a", "1"))
|
||||
(D.make ("a", "2"))
|
||||
],
|
||||
devDependenciesConflictErrors = []
|
||||
}
|
||||
|
||||
it "a conflicting version number is detected with wasp devDependencies" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = waspDevDeps
|
||||
}
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("alpha", "70")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Left
|
||||
PackageJsonDependenciesError
|
||||
{ dependenciesConflictErrors =
|
||||
[ DependencyConflictError
|
||||
(D.make ("alpha", "10"))
|
||||
(D.make ("alpha", "70"))
|
||||
],
|
||||
devDependenciesConflictErrors = []
|
||||
}
|
||||
|
||||
it "dev dependencies are also combined" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = waspDevDeps
|
||||
}
|
||||
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("d", "4")
|
||||
],
|
||||
devDependencies =
|
||||
D.fromList
|
||||
[ ("alpha", "10"),
|
||||
("gamma", "30")
|
||||
]
|
||||
}
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Right
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2"),
|
||||
("d", "4")
|
||||
],
|
||||
devDependencies =
|
||||
D.fromList
|
||||
[ ("alpha", "10"),
|
||||
("beta", "20"),
|
||||
("gamma", "30")
|
||||
]
|
||||
}
|
||||
|
||||
it "wasp dev dependency overlaps with user dependency, should remain devDependency" $ do
|
||||
let waspPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies = waspDeps,
|
||||
devDependencies = waspDevDeps
|
||||
}
|
||||
|
||||
let userPackageJsonDependencies =
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("alpha", "10")
|
||||
],
|
||||
devDependencies = []
|
||||
}
|
||||
combinePackageJsonDependencies waspPackageJsonDependencies userPackageJsonDependencies
|
||||
`shouldBe` Right
|
||||
PackageJsonDependencies
|
||||
{ dependencies =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
],
|
||||
devDependencies =
|
||||
D.fromList
|
||||
[ ("alpha", "10"),
|
||||
("beta", "20")
|
||||
]
|
||||
}
|
||||
|
||||
it "conflictErrorToMessage" $ do
|
||||
conflictErrorToMessage
|
||||
( DependencyConflictError
|
||||
{ waspDependency = D.make ("a", "1"),
|
||||
userDependency = D.make ("a", "2")
|
||||
}
|
||||
)
|
||||
`shouldBe` "Error: Dependency conflict for user dependency (a, 2): "
|
||||
++ "Version must be set to the exactly "
|
||||
++ "the same version as the one wasp is using: 1"
|
Loading…
Reference in New Issue
Block a user