mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-23 19:29:17 +03:00
Resolve dependencies refactor (#449)
Create a new resolveDependencies Use Map to do differences between Wasp and user package.json dependencies. This is now used internally in resolveNpmDeps. Also fixes a bug in the conflict error message, where the wasp and user dependency were previously not properly distinguished. Add .vscode and move everything that appears text editor related to a section
This commit is contained in:
parent
8a97732c71
commit
55d9e1bbd8
11
waspc/.gitignore
vendored
11
waspc/.gitignore
vendored
@ -2,16 +2,19 @@
|
||||
stack*.yaml.lock
|
||||
dist-newstyle
|
||||
waspc.cabal
|
||||
.vscode
|
||||
|
||||
|
||||
/out
|
||||
|
||||
*~
|
||||
.dir-locals.el
|
||||
.projectile
|
||||
|
||||
.hie/
|
||||
.bin/
|
||||
stan.html
|
||||
|
||||
|
||||
# editor related
|
||||
*.orig
|
||||
*~
|
||||
.dir-locals.el
|
||||
.projectile
|
||||
.vscode
|
||||
|
@ -80,6 +80,7 @@ library:
|
||||
- template-haskell
|
||||
- path-io
|
||||
- cryptohash-sha256
|
||||
- containers
|
||||
|
||||
executables:
|
||||
wasp-cli:
|
||||
|
@ -3,6 +3,7 @@
|
||||
module Wasp.AppSpec.App.Dependency
|
||||
( Dependency (..),
|
||||
fromList,
|
||||
make,
|
||||
)
|
||||
where
|
||||
|
||||
@ -15,4 +16,7 @@ data Dependency = Dependency
|
||||
deriving (Show, Eq, Data)
|
||||
|
||||
fromList :: [(String, String)] -> [Dependency]
|
||||
fromList = map (\(n, v) -> Dependency {name = n, version = v})
|
||||
fromList = map make
|
||||
|
||||
make :: (String, String) -> Dependency
|
||||
make (n, v) = Dependency {name = n, version = v}
|
@ -1,17 +1,66 @@
|
||||
module Wasp.Generator.PackageJsonGenerator
|
||||
( resolveNpmDeps,
|
||||
resolveDependencies,
|
||||
DependencyConflictError (DependencyConflictError),
|
||||
npmDepsToPackageJsonEntry,
|
||||
npmDevDepsToPackageJsonEntry,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Bifunctor (second)
|
||||
import Data.List (find, intercalate)
|
||||
import Data.Maybe (fromJust, isJust)
|
||||
import Control.Arrow (ArrowChoice (left))
|
||||
import Data.List (intercalate)
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Maybe as Maybe
|
||||
import qualified Wasp.AppSpec.App.Dependency as D
|
||||
|
||||
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
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- | 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
|
||||
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))
|
||||
|
||||
-- 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
|
||||
|
||||
-- | 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
|
||||
@ -25,40 +74,20 @@ resolveNpmDeps ::
|
||||
Either
|
||||
[(D.Dependency, NpmDependenciesConflictError)]
|
||||
([D.Dependency], [D.Dependency])
|
||||
resolveNpmDeps waspDeps userDeps =
|
||||
if null conflictingUserDeps
|
||||
then Right (waspDeps, userDepsNotInWaspDeps)
|
||||
else Left conflictingUserDeps
|
||||
resolveNpmDeps waspDeps userDeps = left (map convertConflict) $ resolveDependencies waspDeps userDeps
|
||||
where
|
||||
conflictingUserDeps :: [(D.Dependency, NpmDependenciesConflictError)]
|
||||
conflictingUserDeps =
|
||||
map (second fromJust) $
|
||||
filter (isJust . snd) $
|
||||
map (\dep -> (dep, checkIfConflictingUserDep dep)) userDeps
|
||||
|
||||
checkIfConflictingUserDep :: D.Dependency -> Maybe NpmDependenciesConflictError
|
||||
checkIfConflictingUserDep userDep =
|
||||
let attachErrorMessage dep =
|
||||
"Error: Dependency conflict for user dependency ("
|
||||
++ D.name dep
|
||||
++ ", "
|
||||
++ D.version dep
|
||||
++ "): "
|
||||
++ "Version must be set to the exactly the same version as"
|
||||
++ " the one wasp is using: "
|
||||
++ D.version dep
|
||||
in attachErrorMessage <$> find (areTwoDepsInConflict userDep) waspDeps
|
||||
|
||||
areTwoDepsInConflict :: D.Dependency -> D.Dependency -> Bool
|
||||
areTwoDepsInConflict d1 d2 =
|
||||
D.name d1 == D.name d2
|
||||
&& D.version d1 /= D.version d2
|
||||
|
||||
userDepsNotInWaspDeps :: [D.Dependency]
|
||||
userDepsNotInWaspDeps = filter (not . isDepWithNameInWaspDeps . D.name) userDeps
|
||||
|
||||
isDepWithNameInWaspDeps :: String -> Bool
|
||||
isDepWithNameInWaspDeps name = any ((name ==) . D.name) waspDeps
|
||||
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
|
||||
)
|
||||
|
||||
npmDepsToPackageJsonEntryWithKey :: [D.Dependency] -> String -> String
|
||||
npmDepsToPackageJsonEntryWithKey deps key =
|
||||
|
@ -2,35 +2,70 @@ module Generator.PackageJsonGeneratorTest where
|
||||
|
||||
import Test.Tasty.Hspec
|
||||
import qualified Wasp.AppSpec.App.Dependency as D
|
||||
import Wasp.Generator.PackageJsonGenerator (resolveNpmDeps)
|
||||
import Wasp.Generator.PackageJsonGenerator
|
||||
( DependencyConflictError (DependencyConflictError),
|
||||
resolveDependencies,
|
||||
resolveNpmDeps,
|
||||
)
|
||||
|
||||
spec_resolveNpmDeps :: Spec
|
||||
spec_resolveNpmDeps = do
|
||||
let waspDeps =
|
||||
[ ("axios", "^0.20.0"),
|
||||
("lodash", "^4.17.15")
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
]
|
||||
|
||||
it "a conflicting version number is detected" $ do
|
||||
let userDeps =
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "3")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Left
|
||||
[ DependencyConflictError
|
||||
(D.make ("b", "2"))
|
||||
(D.make ("b", "3"))
|
||||
]
|
||||
|
||||
it "Concatenates two distincts lists of deps." $ do
|
||||
it "wasp deps completely overlap with user deps, so no user deps required" $ do
|
||||
let userDeps =
|
||||
[ ("foo", "bar"),
|
||||
("foo2", "bar2")
|
||||
]
|
||||
resolveNpmDeps (D.fromList waspDeps) (D.fromList userDeps)
|
||||
`shouldBe` Right (D.fromList waspDeps, D.fromList userDeps)
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("b", "2")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Right (waspDeps, [])
|
||||
|
||||
it "Does not repeat dep if it is both user and wasp dep." $ do
|
||||
it "no dependency name overlap so no conflict" $ do
|
||||
let userDeps =
|
||||
[ ("axios", "^0.20.0"),
|
||||
("foo", "bar")
|
||||
]
|
||||
resolveNpmDeps (D.fromList waspDeps) (D.fromList userDeps)
|
||||
`shouldBe` Right (D.fromList waspDeps, D.fromList [("foo", "bar")])
|
||||
D.fromList
|
||||
[ ("c", "1"),
|
||||
("d", "2")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Right (waspDeps, userDeps)
|
||||
|
||||
it "Reports error if user dep version does not match wasp dep version." $ do
|
||||
it "some dependencies names overlap, with the same version so dependency is not in user dep" $ do
|
||||
let userDeps =
|
||||
[ ("axios", "^1.20.0"),
|
||||
("foo", "bar")
|
||||
]
|
||||
let Left conflicts = resolveNpmDeps (D.fromList waspDeps) (D.fromList userDeps)
|
||||
map fst conflicts `shouldBe` D.fromList [("axios", "^1.20.0")]
|
||||
D.fromList
|
||||
[ ("a", "1"),
|
||||
("d", "2")
|
||||
]
|
||||
resolveDependencies waspDeps userDeps
|
||||
`shouldBe` Right (waspDeps, [D.make ("d", "2")])
|
||||
|
||||
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"
|
||||
)
|
||||
]
|
Loading…
Reference in New Issue
Block a user