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:
Martijn Faassen 2022-02-08 12:39:32 +01:00
parent 9546dcf200
commit 27b3c83f7a
6 changed files with 455 additions and 221 deletions

View File

@ -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 =

View File

@ -4,6 +4,7 @@
"version": "0.0.0",
"private": true,
{=& depsChunk =},
{=& devDepsChunk =},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",

View File

@ -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}"

View File

@ -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 =

View File

@ -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 =

View File

@ -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"