Change in model

- version split off module
- not trying to merge dependencies having the same module (by merging
  exclusions, optional flag, etc.)

*needs tests and comments* (there were corner cases exclusions + optional were buggy if together)
This commit is contained in:
Alexandre Archambault 2015-06-18 03:01:49 +02:00
parent b1a3f578ac
commit 524ed3320b
15 changed files with 223 additions and 219 deletions

View File

@ -75,12 +75,12 @@ case class Coursier(scope: List[String],
val modules = splitArtifacts.map{
case Seq(org, name, version) =>
Module(org, name, version)
(Module(org, name), version)
}
val deps = modules.map(mod =>
Dependency(mod, scope = Scope.Runtime)
)
val deps = modules.map{case (mod, ver) =>
Dependency(mod, ver, scope = Scope.Runtime)
}
val res = resolve(
deps.toSet,
@ -89,7 +89,7 @@ case class Coursier(scope: List[String],
).run
def repr(dep: Dependency) =
s"${dep.module.organization}:${dep.module.name}:${dep.`type`}:${Some(dep.classifier).filter(_.nonEmpty).map(_+":").mkString}${dep.module.version}"
s"${dep.module.organization}:${dep.module.name}:${dep.`type`}:${Some(dep.classifier).filter(_.nonEmpty).map(_+":").mkString}${dep.version}"
val trDeps = res.dependencies.toList.sortBy(repr)
@ -100,11 +100,11 @@ case class Coursier(scope: List[String],
println(s"${res.conflicts.size} conflict(s):\n ${res.conflicts.toList.map(repr).sorted.mkString(" \n")}")
}
val errDeps = trDeps.filter(dep => res.errors.contains(dep.module))
val errDeps = trDeps.filter(dep => res.errors.contains(dep.moduleVersion))
if (errDeps.nonEmpty) {
println(s"${errDeps.size} error(s):")
for (dep <- errDeps) {
println(s" ${dep.module}:\n ${res.errors(dep.module).mkString("\n").replace("\n", " \n")}")
println(s" ${dep.module}:\n ${res.errors(dep.moduleVersion).mkString("\n").replace("\n", " \n")}")
}
}
@ -113,7 +113,7 @@ case class Coursier(scope: List[String],
val cachePolicy: CachePolicy = CachePolicy.Default
val m = res.dependencies.groupBy(dep => res.projectsCache.get(dep.module).map(_._1))
val m = res.dependencies.groupBy(dep => res.projectsCache.get(dep.moduleVersion).map(_._1))
val (notFound, remaining0) = m.partition(_._1.isEmpty)
if (notFound.nonEmpty) {
val notFound0 = notFound.values.flatten.toList.map(repr).sorted

View File

@ -73,13 +73,14 @@ trait Logger {
case class Remote(base: String, logger: Option[Logger] = None) extends Repository {
def find(module: Module,
version: String,
cachePolicy: CachePolicy): EitherT[Task, String, Project] = {
val relPath =
module.organization.split('.').toSeq ++ Seq(
module.name,
module.version,
s"${module.name}-${module.version}.pom"
version,
s"${module.name}-$version.pom"
)
val url = base + relPath.mkString("/")

View File

@ -26,9 +26,9 @@ object JsTests extends TestSuite {
'getProj{
repository.mavenCentral
.find(Module("ch.qos.logback", "logback-classic", "1.1.3"))
.find(Module("ch.qos.logback", "logback-classic"), "1.1.3")
.map{ proj =>
assert(proj.parent == Some(Module("ch.qos.logback", "logback-parent", "1.1.3")))
assert(proj.parent == Some(Module("ch.qos.logback", "logback-parent"), "1.1.3"))
}
.run
.runF

View File

@ -20,6 +20,7 @@ case class ArtifactDownloader(root: String, cache: File, logger: Option[Artifact
var bufferSize = 1024*1024
def artifact(module: Module,
version: String,
classifier: String,
`type`: String,
cachePolicy: CachePolicy): EitherT[Task, String, File] = {
@ -27,8 +28,8 @@ case class ArtifactDownloader(root: String, cache: File, logger: Option[Artifact
val relPath =
module.organization.split('.').toSeq ++ Seq(
module.name,
module.version,
s"${module.name}-${module.version}${Some(classifier).filter(_.nonEmpty).map("-"+_).mkString}.${`type`}"
version,
s"${module.name}-$version${Some(classifier).filter(_.nonEmpty).map("-"+_).mkString}.${`type`}"
)
val file = (cache /: relPath)(new File(_, _))
@ -93,7 +94,7 @@ case class ArtifactDownloader(root: String, cache: File, logger: Option[Artifact
def artifact(dependency: Dependency,
cachePolicy: CachePolicy = CachePolicy.Default): EitherT[Task, String, File] =
artifact(dependency.module, dependency.classifier, dependency.`type`, cachePolicy = cachePolicy)
artifact(dependency.module, dependency.version, dependency.classifier, dependency.`type`, cachePolicy = cachePolicy)
}
@ -140,13 +141,14 @@ case class Remote(root: String,
logger: Option[RemoteLogger] = None) extends Repository {
def find(module: Module,
version: String,
cachePolicy: CachePolicy): EitherT[Task, String, Project] = {
val relPath =
module.organization.split('.').toSeq ++ Seq(
module.name,
module.version,
s"${module.name}-${module.version}.pom"
version,
s"${module.name}-$version.pom"
)
def localFile = {

View File

@ -1,12 +1,14 @@
package coursier
import coursier.core.Resolver.ModuleVersion
import scalaz.EitherT
import scalaz.concurrent.Task
package object core {
def resolution(dependencies: Set[Dependency],
fetch: Module => EitherT[Task, List[String], (Repository, Project)],
fetch: ModuleVersion => EitherT[Task, List[String], (Repository, Project)],
filter: Option[Dependency => Boolean],
profileActivation: Option[(String, Activation, Map[String, String]) => Boolean]): Stream[Resolution] = {

View File

@ -1,32 +1,36 @@
package coursier.core
case class Module(organization: String,
name: String,
version: String) {
name: String) {
def trim: Module = copy(
organization = organization.trim,
name = name.trim,
version = version.trim
name = name.trim
)
override def toString = s"$organization:$name:$version"
override def toString = s"$organization:$name"
}
sealed abstract class Scope(val name: String)
case class Dependency(module: Module,
version: String,
scope: Scope,
`type`: String,
classifier: String,
exclusions: Set[(String, String)],
optional: Boolean)
optional: Boolean) {
def moduleVersion = (module, version)
}
case class Project(module: Module,
version: String,
dependencies: Seq[Dependency],
parent: Option[Module],
parent: Option[(Module, String)],
dependencyManagement: Seq[Dependency],
properties: Map[String, String],
profiles: Seq[Profile])
profiles: Seq[Profile]) {
def moduleVersion = (module, version)
}
object Scope {
case object Compile extends Scope("compile")

View File

@ -4,7 +4,7 @@ import scalaz.{\/, EitherT}
import scalaz.concurrent.Task
trait Repository {
def find(module: Module, cachePolicy: CachePolicy = CachePolicy.Default): EitherT[Task, String, Project]
def find(module: Module, version: String, cachePolicy: CachePolicy = CachePolicy.Default): EitherT[Task, String, Project]
def versions(organization: String, name: String, cachePolicy: CachePolicy = CachePolicy.Default): EitherT[Task, String, Versions]
}

View File

@ -8,6 +8,8 @@ import scalaz.{EitherT, \/-, \/, -\/}
object Resolver {
type ModuleVersion = (Module, String)
/**
* Try to find `module` among `repositories`.
*
@ -20,9 +22,10 @@ object Resolver {
* the repository implementation.
*/
def find(repositories: Seq[Repository],
module: Module): EitherT[Task, List[String], (Repository, Project)] = {
module: Module,
version: String): EitherT[Task, List[String], (Repository, Project)] = {
val lookups = repositories.map(repo => repo -> repo.find(module).run)
val lookups = repositories.map(repo => repo -> repo.find(module, version).run)
val task = lookups.foldLeft(Task.now(-\/(Nil)): Task[List[String] \/ (Repository, Project)]) {
case (acc, (repo, t)) =>
acc.flatMap {
@ -112,9 +115,9 @@ object Resolver {
dep.copy(
module = dep.module.copy(
organization = substituteProps(dep.module.organization),
name = substituteProps(dep.module.name),
version = substituteProps(dep.module.version)
name = substituteProps(dep.module.name)
),
version = substituteProps(dep.version),
`type` = substituteProps(dep.`type`),
scope = Parse.scope(substituteProps(dep.scope.name)),
exclusions = dep.exclusions
@ -153,42 +156,22 @@ object Resolver {
def merge(dependencies: TraversableOnce[Dependency]): (Seq[Dependency], Seq[Dependency]) = {
val m = dependencies
.toList
.groupBy(dep => (dep.module.organization, dep.module.name))
.groupBy(dep => dep.module)
.mapValues{ deps =>
if (deps.lengthCompare(1) == 0) List(\/-(deps.head))
if (deps.lengthCompare(1) == 0) \/-(deps)
else {
val scopeTypeClassifiers = (Set.empty[(Scope, String, String)] /: deps)((acc, dep) => acc + ((dep.scope, dep.`type`, dep.classifier)))
val versions = deps.map(_.module.version).distinct
val versions = deps.map(_.version).distinct
val versionOpt = mergeVersions(versions)
scopeTypeClassifiers.toList.flatMap{case (scope, type0, classifier) =>
val scopeTypeClassifierDeps =
deps.filter(dep =>
dep.scope == scope && dep.`type` == type0 && dep.classifier == classifier
)
def dep(version: String) = {
Dependency(
deps.head.module.copy(version = version),
scope,
type0,
classifier,
scopeTypeClassifierDeps.map(_.exclusions.toSet).reduce(exclusionsIntersect),
scopeTypeClassifierDeps.forall(_.optional)
)
}
versionOpt match {
case Some(version) => List(\/-(dep(version)))
case None => versions.map(version => -\/(dep(version)))
}
versionOpt match {
case Some(version) => \/-(deps.map(dep => dep.copy(version = version)))
case None => -\/(deps)
}
}
}
val l = m.values.toList.flatten
(l.collect{case -\/(dep) => dep}, l.collect{case \/-(dep) => dep})
val l = m.values.toList
(l.collect{case -\/(dep) => dep}.flatten, l.collect{case \/-(dep) => dep}.flatten)
}
/**
@ -223,8 +206,8 @@ object Resolver {
var dep = dep0
for (mgmtDep <- m.get(dependencyManagementKey(dep0))) {
if (dep.module.version.isEmpty)
dep = dep.copy(module = dep.module.copy(version = mgmtDep.module.version))
if (dep.version.isEmpty)
dep = dep.copy(version = mgmtDep.version)
if (dep.scope.name.isEmpty)
dep = dep.copy(scope = mgmtDep.scope)
@ -355,7 +338,7 @@ object Resolver {
Map(
"project.groupId" -> project.module.organization,
"project.artifactId" -> project.module.name,
"project.version" -> project.module.version
"project.version" -> project.version
)
)
@ -390,8 +373,8 @@ object Resolver {
case class Resolution(rootDependencies: Set[Dependency],
dependencies: Set[Dependency],
conflicts: Set[Dependency],
projectsCache: Map[Module, (Repository, Project)],
errors: Map[Module, Seq[String]],
projectsCache: Map[ModuleVersion, (Repository, Project)],
errors: Map[ModuleVersion, Seq[String]],
filter: Option[Dependency => Boolean],
profileActivation: Option[(String, Activation, Map[String, String]) => Boolean]) {
@ -402,7 +385,7 @@ object Resolver {
def transitiveDependencies =
for {
dep <- (dependencies -- conflicts).toList
(_, proj) <- projectsCache.get(dep.module).toSeq
(_, proj) <- projectsCache.get((dep.moduleVersion)).toSeq
trDep <- finalDependencies(dep, proj).filter(filter getOrElse defaultFilter)
} yield trDep
@ -419,9 +402,9 @@ object Resolver {
/**
* The modules we miss some info about.
*/
def missingFromCache: Set[Module] = {
val modules = dependencies.map(_.module)
val nextModules = nextDependenciesAndConflicts._2.map(_.module)
def missingFromCache: Set[ModuleVersion] = {
val modules = dependencies.map(dep => (dep.moduleVersion))
val nextModules = nextDependenciesAndConflicts._2.map(dep => (dep.moduleVersion))
(modules ++ nextModules)
.filterNot(mod => projectsCache.contains(mod) || errors.contains(mod))
@ -453,7 +436,7 @@ object Resolver {
val trDepsSeq =
for {
dep <- updatedDeps
(_, proj) <- projectsCache.get(dep.module).toList
(_, proj) <- projectsCache.get((dep.moduleVersion)).toList
trDep <- finalDependencies(dep, proj).filter(filter getOrElse defaultFilter)
} yield key(trDep) -> (key(dep), trDep.exclusions)
@ -526,7 +509,7 @@ object Resolver {
/**
* Do a new iteration, fetching the missing modules along the way.
*/
def next(fetchModule: Module => EitherT[Task, List[String], (Repository, Project)]): Task[Resolution] = {
def next(fetchModule: ModuleVersion => EitherT[Task, List[String], (Repository, Project)]): Task[Resolution] = {
val missing = missingFromCache
if (missing.isEmpty) Task.now(nextNoMissingUnsafe())
else fetch(missing.toList, fetchModule).map(_.nextIfNoMissing())
@ -535,7 +518,7 @@ object Resolver {
/**
* Required modules for the dependency management of `project`.
*/
def dependencyManagementRequirements(project: Project): Set[Module] = {
def dependencyManagementRequirements(project: Project): Set[ModuleVersion] = {
val approxProperties =
project.parent
.flatMap(projectsCache.get)
@ -548,7 +531,7 @@ object Resolver {
val modules =
(project.dependencies ++ profileDependencies)
.collect{ case dep if dep.scope == Scope.Import => dep.module } ++
.collect{ case dep if dep.scope == Scope.Import => (dep.moduleVersion) } ++
project.parent
modules.toSet
@ -562,12 +545,12 @@ object Resolver {
* these modules should be added to the cache, and `dependencyManagementMissing` checked again
* for new missing modules.
*/
def dependencyManagementMissing(project: Project): Set[Module] = {
def dependencyManagementMissing(project: Project): Set[ModuleVersion] = {
@tailrec
def helper(toCheck: Set[Module],
done: Set[Module],
missing: Set[Module]): Set[Module] = {
def helper(toCheck: Set[ModuleVersion],
done: Set[ModuleVersion],
missing: Set[ModuleVersion]): Set[ModuleVersion] = {
if (toCheck.isEmpty) missing
else if (toCheck.exists(done)) helper(toCheck -- done, done, missing)
@ -584,7 +567,7 @@ object Resolver {
helper(Set.empty, done, missing ++ toCheck)
}
helper(dependencyManagementRequirements(project), Set(project.module), Set.empty)
helper(dependencyManagementRequirements(project), Set(project.moduleVersion), Set.empty)
}
/**
@ -607,7 +590,7 @@ object Resolver {
val deps =
dependencies0
.collect{ case dep if dep.scope == Scope.Import && projectsCache.contains(dep.module) => dep.module } ++
.collect{ case dep if dep.scope == Scope.Import && projectsCache.contains(dep.moduleVersion) => dep.moduleVersion } ++
project.parent.filter(projectsCache.contains)
val projs = deps.map(projectsCache(_)._2)
@ -619,7 +602,7 @@ object Resolver {
project.copy(
dependencies = dependencies0
.filterNot(dep => dep.scope == Scope.Import && depsSet(dep.module)) ++
.filterNot(dep => dep.scope == Scope.Import && depsSet(dep.moduleVersion)) ++
project.parent
.filter(projectsCache.contains)
.toSeq
@ -635,8 +618,8 @@ object Resolver {
/**
* Fetch `modules` with `fetchModules`, and add the resulting errors and projects to the cache.
*/
def fetch(modules: Seq[Module],
fetchModule: Module => EitherT[Task, List[String], (Repository, Project)]): Task[Resolution] = {
def fetch(modules: Seq[ModuleVersion],
fetchModule: ModuleVersion => EitherT[Task, List[String], (Repository, Project)]): Task[Resolution] = {
val lookups = modules.map(dep => fetchModule(dep).run.map(dep -> _))
val gatheredLookups = Task.gatherUnordered(lookups, exceptionCancels = true)
@ -654,7 +637,7 @@ object Resolver {
current <- accTask
updated <- current.fetch(current.dependencyManagementMissing(proj).toList, fetchModule)
proj0 = updated.withDependencyManagement(proj)
} yield updated.copy(projectsCache = updated.projectsCache + (proj0.module -> (repo, proj0)))
} yield updated.copy(projectsCache = updated.projectsCache + (proj0.moduleVersion -> (repo, proj0)))
}
}
}
@ -700,7 +683,7 @@ object Resolver {
*
*/
def resolve(dependencies: Set[Dependency],
fetch: Module => EitherT[Task, List[String], (Repository, Project)],
fetch: ModuleVersion => EitherT[Task, List[String], (Repository, Project)],
maxIterations: Option[Int],
filter: Option[Dependency => Boolean],
profileActivation: Option[(String, Activation, Map[String, String]) => Boolean]): Task[Resolution] = {

View File

@ -56,10 +56,12 @@ object Xml {
else e
}
name <- text(node, "artifactId", "Name")
version = text(node, "version", "Version").getOrElse("")
} yield Module(organization, name, version).trim
} yield Module(organization, name).trim
}
private def readVersion(node: Node) =
text(node, "version", "Version").getOrElse("").trim
private val defaultScope = Scope.Other("")
private val defaultType = "jar"
private val defaultClassifier = ""
@ -67,6 +69,7 @@ object Xml {
def dependency(node: Node): String \/ Dependency = {
for {
mod <- module(node)
version0 = readVersion(node)
scopeOpt = text(node, "scope", "").toOption
.map(Parse.scope)
typeOpt = text(node, "type", "").toOption
@ -82,6 +85,7 @@ object Xml {
optional = text(node, "optional", "").toOption.toSeq.contains("true")
} yield Dependency(
mod,
version0,
scopeOpt getOrElse defaultScope,
typeOpt getOrElse defaultType,
classifierOpt getOrElse defaultClassifier,
@ -150,12 +154,15 @@ object Xml {
for {
projModule <- module(pom, groupIdIsOptional = true)
projVersion = readVersion(pom)
parentOpt = pom.child
.find(_.label == "parent")
parentModuleOpt <- parentOpt
.map(module(_).map(Some(_)))
.getOrElse(\/-(None))
parentVersionOpt = parentOpt
.map(readVersion)
xmlDeps = pom.child
.find(_.label == "dependencies")
@ -173,12 +180,12 @@ object Xml {
groupId <- Some(projModule.organization).filter(_.nonEmpty)
.orElse(parentModuleOpt.map(_.organization).filter(_.nonEmpty))
.toRightDisjunction("No organization found")
version <- Some(projModule.version).filter(_.nonEmpty)
.orElse(parentModuleOpt.map(_.version).filter(_.nonEmpty))
version <- Some(projVersion).filter(_.nonEmpty)
.orElse(parentVersionOpt.filter(_.nonEmpty))
.toRightDisjunction("No version found")
_ <- parentModuleOpt
.map(mod => if (mod.version.isEmpty) -\/("Parent version missing") else \/-(()))
_ <- parentVersionOpt
.map(v => if (v.isEmpty) -\/("Parent version missing") else \/-(()))
.getOrElse(\/-(()))
_ <- parentModuleOpt
.map(mod => if (mod.organization.isEmpty) -\/("Parent organization missing") else \/-(()))
@ -197,9 +204,10 @@ object Xml {
profiles <- xmlProfiles.toList.traverseU(profile)
} yield Project(
projModule.copy(organization = groupId, version = version),
projModule.copy(organization = groupId),
version,
deps,
parentModuleOpt,
parentModuleOpt.map((_, parentVersionOpt.getOrElse(""))),
depMgmts,
properties.toMap,
profiles

View File

@ -6,23 +6,25 @@ package object coursier {
type Dependency = core.Dependency
object Dependency {
def apply(module: Module,
version: String,
scope: Scope = Scope.Other(""), // Subsituted by Resolver with its own default scope (compile)
`type`: String = "jar",
classifier: String = "",
exclusions: Set[(String, String)] = Set.empty,
optional: Boolean = false): Dependency =
core.Dependency(module, scope, `type`, classifier, exclusions, optional)
core.Dependency(module, version, scope, `type`, classifier, exclusions, optional)
}
type Project = core.Project
object Project {
def apply(module: Module,
version: String,
dependencies: Seq[Dependency] = Seq.empty,
parent: Option[Module] = None,
parent: Option[ModuleVersion] = None,
dependencyManagement: Seq[Dependency] = Seq.empty,
properties: Map[String, String] = Map.empty,
profiles: Seq[Profile] = Seq.empty): Project =
core.Project(module, dependencies, parent, dependencyManagement, properties, profiles)
core.Project(module, version, dependencies, parent, dependencyManagement, properties, profiles)
}
type Profile = core.Profile
@ -44,17 +46,19 @@ package object coursier {
type Module = core.Module
object Module {
def apply(organization: String, name: String, version: String): Module =
core.Module(organization, name, version)
def apply(organization: String, name: String): Module =
core.Module(organization, name)
}
type ModuleVersion = (core.Module, String)
type Scope = core.Scope
val Scope: core.Scope.type = core.Scope
type Repository = core.Repository
def fetchFrom(repositories: Seq[Repository]): Module => EitherT[Task, List[String], (Repository, Project)] =
core.Resolver.find(repositories, _)
def fetchFrom(repositories: Seq[Repository]): ModuleVersion => EitherT[Task, List[String], (Repository, Project)] =
modVersion => core.Resolver.find(repositories, modVersion._1, modVersion._2)
type Resolution = core.Resolver.Resolution
object Resolution {
@ -62,15 +66,15 @@ package object coursier {
def apply(rootDependencies: Set[Dependency] = Set.empty,
dependencies: Set[Dependency] = Set.empty,
conflicts: Set[Dependency] = Set.empty,
projectsCache: Map[Module, (Repository, Project)] = Map.empty,
errors: Map[Module, Seq[String]] = Map.empty,
projectsCache: Map[ModuleVersion, (Repository, Project)] = Map.empty,
errors: Map[ModuleVersion, Seq[String]] = Map.empty,
filter: Option[Dependency => Boolean] = None,
profileActivation: Option[(String, Profile.Activation, Map[String, String]) => Boolean] = None): Resolution =
core.Resolver.Resolution(rootDependencies, dependencies, conflicts, projectsCache, errors, filter, profileActivation)
}
def resolve(dependencies: Set[Dependency],
fetch: Module => EitherT[Task, List[String], (Repository, Project)],
fetch: ModuleVersion => EitherT[Task, List[String], (Repository, Project)],
maxIterations: Option[Int] = Some(200),
filter: Option[Dependency => Boolean] = None,
profileActivation: Option[(String, Profile.Activation, Map[String, String]) => Boolean] = None): Task[Resolution] = {

View File

@ -15,7 +15,7 @@ object CentralTests extends TestSuite {
val tests = TestSuite {
'logback{
async {
val dep = Dependency(Module("ch.qos.logback", "logback-classic", "1.1.3"))
val dep = Dependency(Module("ch.qos.logback", "logback-classic"), "1.1.3")
val res0 =
await(resolve(Set(dep), fetchFrom(repositories))
.runF)
@ -28,8 +28,8 @@ object CentralTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(
dep.withCompileScope,
Dependency(Module("ch.qos.logback", "logback-core", "1.1.3")).withCompileScope,
Dependency(Module("org.slf4j", "slf4j-api", "1.7.7")).withCompileScope
Dependency(Module("ch.qos.logback", "logback-core"), "1.1.3").withCompileScope,
Dependency(Module("org.slf4j", "slf4j-api"), "1.7.7").withCompileScope
)
)
@ -38,7 +38,7 @@ object CentralTests extends TestSuite {
}
'asm{
async {
val dep = Dependency(Module("org.ow2.asm", "asm-commons", "5.0.2"))
val dep = Dependency(Module("org.ow2.asm", "asm-commons"), "5.0.2")
val res0 =
await(resolve(Set(dep), fetchFrom(repositories))
.runF)
@ -51,8 +51,8 @@ object CentralTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(
dep.withCompileScope,
Dependency(Module("org.ow2.asm", "asm-tree", "5.0.2")).withCompileScope,
Dependency(Module("org.ow2.asm", "asm", "5.0.2")).withCompileScope
Dependency(Module("org.ow2.asm", "asm-tree"), "5.0.2").withCompileScope,
Dependency(Module("org.ow2.asm", "asm"), "5.0.2").withCompileScope
)
)

View File

@ -21,7 +21,7 @@ object PomParsingTests extends TestSuite {
</dependency>
"""
val expected = \/-(Dependency(Module("comp", "lib", "2.1"), classifier = "extra"))
val expected = \/-(Dependency(Module("comp", "lib"), "2.1", classifier = "extra"))
val result = Xml.dependency(xmlParse(depNode).right.get)
@ -75,7 +75,7 @@ object PomParsingTests extends TestSuite {
None,
Activation(Nil),
Seq(
Dependency(Module("comp", "lib", "0.2"))),
Dependency(Module("comp", "lib"), "0.2")),
Nil,
Map.empty
))
@ -107,7 +107,7 @@ object PomParsingTests extends TestSuite {
Activation(Nil),
Nil,
Seq(
Dependency(Module("comp", "lib", "0.2"), scope = Scope.Test)),
Dependency(Module("comp", "lib"), "0.2", scope = Scope.Test)),
Map.empty
))

View File

@ -10,137 +10,137 @@ import coursier.test.compatibility._
object ResolverTests extends TestSuite {
implicit class ProjectOps(val p: Project) extends AnyVal {
def kv: (Module, (Repository, Project)) = p.module -> (testRepository, p)
def kv: (ModuleVersion, (Repository, Project)) = p.moduleVersion -> (testRepository, p)
}
val projects = Seq(
Project(Module("acme", "config", "1.3.0")),
Project(Module("acme", "config"), "1.3.0"),
Project(Module("acme", "play", "2.4.0"), Seq(
Dependency(Module("acme", "play-json", "2.4.0")))),
Project(Module("acme", "play"), "2.4.0", Seq(
Dependency(Module("acme", "play-json"), "2.4.0"))),
Project(Module("acme", "play-json", "2.4.0")),
Project(Module("acme", "play-json"), "2.4.0"),
Project(Module("acme", "play", "2.4.1"),
Project(Module("acme", "play"), "2.4.1",
Seq(
Dependency(Module("acme", "play-json", "${playJsonVersion}")),
Dependency(Module("${project.groupId}", "${configName}", "1.3.0"))),
Dependency(Module("acme", "play-json"), "${playJsonVersion}"),
Dependency(Module("${project.groupId}", "${configName}"), "1.3.0")),
properties = Map(
"playJsonVersion" -> "2.4.0",
"configName" -> "config")),
Project(Module("acme", "play-extra-no-config", "2.4.1"),
Project(Module("acme", "play-extra-no-config"), "2.4.1",
Seq(
Dependency(Module("acme", "play", "2.4.1"),
Dependency(Module("acme", "play"), "2.4.1",
exclusions = Set(("acme", "config"))))),
Project(Module("acme", "play-extra-no-config-no", "2.4.1"),
Project(Module("acme", "play-extra-no-config-no"), "2.4.1",
Seq(
Dependency(Module("acme", "play", "2.4.1"),
Dependency(Module("acme", "play"), "2.4.1",
exclusions = Set(("*", "config"))))),
Project(Module("hudsucker", "mail", "10.0"),
Project(Module("hudsucker", "mail"), "10.0",
Seq(
Dependency(Module("${project.groupId}", "test-util", "${project.version}"),
Dependency(Module("${project.groupId}", "test-util"), "${project.version}",
scope = Scope.Test))),
Project(Module("hudsucker", "test-util", "10.0")),
Project(Module("hudsucker", "test-util"), "10.0"),
Project(Module("se.ikea", "parent", "18.0"),
Project(Module("se.ikea", "parent"), "18.0",
dependencyManagement = Seq(
Dependency(Module("acme", "play", "2.4.0"),
Dependency(Module("acme", "play"), "2.4.0",
exclusions = Set(("acme", "play-json"))))),
Project(Module("se.ikea", "billy", "18.0"),
Project(Module("se.ikea", "billy"), "18.0",
Seq(
Dependency(Module("acme", "play", ""))
Dependency(Module("acme", "play"), "")
),
parent = Some(Module("se.ikea", "parent", "18.0"))),
parent = Some(Module("se.ikea", "parent"), "18.0")),
Project(Module("org.gnome", "parent", "7.0"),
Project(Module("org.gnome", "parent"), "7.0",
Seq(
Dependency(Module("org.gnu", "glib", "13.4")))),
Dependency(Module("org.gnu", "glib"), "13.4"))),
Project(Module("org.gnome", "panel-legacy", "7.0"),
Project(Module("org.gnome", "panel-legacy"), "7.0",
Seq(
Dependency(Module("org.gnome", "desktop", "${project.version}"))),
parent = Some(Module("org.gnome", "parent", "7.0"))),
Dependency(Module("org.gnome", "desktop"), "${project.version}")),
parent = Some(Module("org.gnome", "parent"), "7.0")),
Project(Module("gov.nsa", "secure-pgp", "10.0"),
Project(Module("gov.nsa", "secure-pgp"), "10.0",
Seq(
Dependency(Module("gov.nsa", "crypto", "536.89")))),
Dependency(Module("gov.nsa", "crypto"), "536.89"))),
Project(Module("com.mailapp", "mail-client", "2.1"),
Project(Module("com.mailapp", "mail-client"), "2.1",
Seq(
Dependency(Module("gov.nsa", "secure-pgp", "10.0"),
Dependency(Module("gov.nsa", "secure-pgp"), "10.0",
exclusions = Set(("*", "${crypto.name}")))),
properties = Map("crypto.name" -> "crypto", "dummy" -> "2")),
Project(Module("com.thoughtworks.paranamer", "paranamer-parent", "2.6"),
Project(Module("com.thoughtworks.paranamer", "paranamer-parent"), "2.6",
Seq(
Dependency(Module("junit", "junit", ""))),
Dependency(Module("junit", "junit"), "")),
dependencyManagement = Seq(
Dependency(Module("junit", "junit", "4.11"), scope = Scope.Test))),
Dependency(Module("junit", "junit"), "4.11", scope = Scope.Test))),
Project(Module("com.thoughtworks.paranamer", "paranamer", "2.6"),
parent = Some(Module("com.thoughtworks.paranamer", "paranamer-parent", "2.6"))),
Project(Module("com.thoughtworks.paranamer", "paranamer"), "2.6",
parent = Some(Module("com.thoughtworks.paranamer", "paranamer-parent"), "2.6")),
Project(Module("com.github.dummy", "libb", "0.3.3"),
Project(Module("com.github.dummy", "libb"), "0.3.3",
profiles = Seq(
Profile("default", activeByDefault = Some(true), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6"))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")
))
)),
Project(Module("com.github.dummy", "libb", "0.4.2"),
Project(Module("com.github.dummy", "libb"), "0.4.2",
Seq(
Dependency(Module("org.scalaverification", "scala-verification", "1.12.4"))
Dependency(Module("org.scalaverification", "scala-verification"), "1.12.4")
),
profiles = Seq(
Profile("default", activeByDefault = Some(true), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6")),
Dependency(Module("org.scalaverification", "scala-verification", "1.12.4"), scope = Scope.Test)
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"),
Dependency(Module("org.scalaverification", "scala-verification"), "1.12.4", scope = Scope.Test)
))
)),
Project(Module("com.github.dummy", "libb", "0.5.3"),
Project(Module("com.github.dummy", "libb"), "0.5.3",
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> None)), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6"))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")
))
)),
Project(Module("com.github.dummy", "libb", "0.5.4"),
Project(Module("com.github.dummy", "libb"), "0.5.4",
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> Some("true"))), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6"))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")
))
)),
Project(Module("com.github.dummy", "libb", "0.5.5"),
Project(Module("com.github.dummy", "libb"), "0.5.5",
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> Some("!false"))), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6"))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")
))
)),
Project(Module("com.github.dummy", "libb-parent", "0.5.6"),
Project(Module("com.github.dummy", "libb-parent"), "0.5.6",
properties = Map("special" -> "true")),
Project(Module("com.github.dummy", "libb", "0.5.6"),
parent = Some(Module("com.github.dummy", "libb-parent", "0.5.6")),
Project(Module("com.github.dummy", "libb"), "0.5.6",
parent = Some(Module("com.github.dummy", "libb-parent"), "0.5.6"),
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> Some("!false"))), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6"))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")
))
))
)
val projectsMap = projects.map(p => p.module -> p).toMap
val projectsMap = projects.map(p => p.moduleVersion -> p).toMap
val testRepository: Repository = new TestRepository(projectsMap)
val repositories = Seq[Repository](
@ -161,7 +161,7 @@ object ResolverTests extends TestSuite {
}
'notFound{
async {
val dep = Dependency(Module("acme", "playy", "2.4.0"))
val dep = Dependency(Module("acme", "playy"), "2.4.0")
val res = await(resolve(
Set(dep),
fetchFrom(repositories)
@ -170,7 +170,7 @@ object ResolverTests extends TestSuite {
val expected = Resolution(
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope),
errors = Map(dep.module -> Seq("Not found"))
errors = Map(dep.moduleVersion -> Seq("Not found"))
)
assert(res == expected)
@ -178,7 +178,7 @@ object ResolverTests extends TestSuite {
}
'single{
async {
val dep = Dependency(Module("acme", "config", "1.3.0"))
val dep = Dependency(Module("acme", "config"), "1.3.0")
val res = await(resolve(
Set(dep),
fetchFrom(repositories)
@ -187,7 +187,7 @@ object ResolverTests extends TestSuite {
val expected = Resolution(
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope),
projectsCache = Map(dep.module -> (testRepository, projectsMap(dep.module)))
projectsCache = Map(dep.moduleVersion -> (testRepository, projectsMap(dep.moduleVersion)))
)
assert(res == expected)
@ -195,8 +195,8 @@ object ResolverTests extends TestSuite {
}
'oneTransitiveDependency{
async {
val dep = Dependency(Module("acme", "play", "2.4.0"))
val trDep = Dependency(Module("acme", "play-json", "2.4.0"))
val dep = Dependency(Module("acme", "play"), "2.4.0")
val trDep = Dependency(Module("acme", "play-json"), "2.4.0")
val res = await(resolve(
Set(dep),
fetchFrom(repositories)
@ -206,8 +206,8 @@ object ResolverTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope, trDep.withCompileScope),
projectsCache = Map(
projectsMap(dep.module).kv,
projectsMap(trDep.module).kv
projectsMap(dep.moduleVersion).kv,
projectsMap(trDep.moduleVersion).kv
)
)
@ -216,10 +216,10 @@ object ResolverTests extends TestSuite {
}
'twoTransitiveDependencyWithProps{
async {
val dep = Dependency(Module("acme", "play", "2.4.1"))
val dep = Dependency(Module("acme", "play"), "2.4.1")
val trDeps = Seq(
Dependency(Module("acme", "play-json", "2.4.0")),
Dependency(Module("acme", "config", "1.3.0"))
Dependency(Module("acme", "play-json"), "2.4.0"),
Dependency(Module("acme", "config"), "1.3.0")
)
val res = await(resolve(
Set(dep),
@ -230,8 +230,8 @@ object ResolverTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope) ++ trDeps.map(_.withCompileScope),
projectsCache = Map(
projectsMap(dep.module).kv
) ++ trDeps.map(trDep => projectsMap(trDep.module).kv)
projectsMap(dep.moduleVersion).kv
) ++ trDeps.map(trDep => projectsMap(trDep.moduleVersion).kv)
)
assert(res == expected)
@ -239,11 +239,11 @@ object ResolverTests extends TestSuite {
}
'exclude{
async {
val dep = Dependency(Module("acme", "play-extra-no-config", "2.4.1"))
val dep = Dependency(Module("acme", "play-extra-no-config"), "2.4.1")
val trDeps = Seq(
Dependency(Module("acme", "play", "2.4.1"),
Dependency(Module("acme", "play"), "2.4.1",
exclusions = Set(("acme", "config"))),
Dependency(Module("acme", "play-json", "2.4.0"),
Dependency(Module("acme", "play-json"), "2.4.0",
exclusions = Set(("acme", "config")))
)
val res = await(resolve(
@ -255,8 +255,8 @@ object ResolverTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope) ++ trDeps.map(_.withCompileScope),
projectsCache = Map(
projectsMap(dep.module).kv
) ++ trDeps.map(trDep => projectsMap(trDep.module).kv)
projectsMap(dep.moduleVersion).kv
) ++ trDeps.map(trDep => projectsMap(trDep.moduleVersion).kv)
)
assert(res == expected)
@ -264,11 +264,11 @@ object ResolverTests extends TestSuite {
}
'excludeOrgWildcard{
async {
val dep = Dependency(Module("acme", "play-extra-no-config-no", "2.4.1"))
val dep = Dependency(Module("acme", "play-extra-no-config-no"), "2.4.1")
val trDeps = Seq(
Dependency(Module("acme", "play", "2.4.1"),
Dependency(Module("acme", "play"), "2.4.1",
exclusions = Set(("*", "config"))),
Dependency(Module("acme", "play-json", "2.4.0"),
Dependency(Module("acme", "play-json"), "2.4.0",
exclusions = Set(("*", "config")))
)
val res = await(resolve(
@ -280,8 +280,8 @@ object ResolverTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope) ++ trDeps.map(_.withCompileScope),
projectsCache = Map(
projectsMap(dep.module).kv
) ++ trDeps.map(trDep => projectsMap(trDep.module).kv)
projectsMap(dep.moduleVersion).kv
) ++ trDeps.map(trDep => projectsMap(trDep.moduleVersion).kv)
)
assert(res == expected)
@ -289,7 +289,7 @@ object ResolverTests extends TestSuite {
}
'filter{
async {
val dep = Dependency(Module("hudsucker", "mail", "10.0"))
val dep = Dependency(Module("hudsucker", "mail"), "10.0")
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -300,7 +300,7 @@ object ResolverTests extends TestSuite {
rootDependencies = Set(dep.withCompileScope),
dependencies = Set(dep.withCompileScope),
projectsCache = Map(
projectsMap(dep.module).kv
projectsMap(dep.moduleVersion).kv
)
)
@ -309,9 +309,9 @@ object ResolverTests extends TestSuite {
}
'parentDepMgmt{
async {
val dep = Dependency(Module("se.ikea", "billy", "18.0"))
val dep = Dependency(Module("se.ikea", "billy"), "18.0")
val trDeps = Seq(
Dependency(Module("acme", "play", "2.4.0"),
Dependency(Module("acme", "play"), "2.4.0",
exclusions = Set(("acme", "play-json")))
)
val res = await(resolve(
@ -330,10 +330,10 @@ object ResolverTests extends TestSuite {
}
'parentDependencies{
async {
val dep = Dependency(Module("org.gnome", "panel-legacy", "7.0"))
val dep = Dependency(Module("org.gnome", "panel-legacy"), "7.0")
val trDeps = Seq(
Dependency(Module("org.gnu", "glib", "13.4")),
Dependency(Module("org.gnome", "desktop", "7.0")))
Dependency(Module("org.gnu", "glib"), "13.4"),
Dependency(Module("org.gnome", "desktop"), "7.0"))
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -350,9 +350,9 @@ object ResolverTests extends TestSuite {
}
'propertiesInExclusions{
async {
val dep = Dependency(Module("com.mailapp", "mail-client", "2.1"))
val dep = Dependency(Module("com.mailapp", "mail-client"), "2.1")
val trDeps = Seq(
Dependency(Module("gov.nsa", "secure-pgp", "10.0"), exclusions = Set(("*", "crypto"))))
Dependency(Module("gov.nsa", "secure-pgp"), "10.0", exclusions = Set(("*", "crypto"))))
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -369,7 +369,7 @@ object ResolverTests extends TestSuite {
}
'depMgmtInParentDeps{
async {
val dep = Dependency(Module("com.thoughtworks.paranamer", "paranamer", "2.6"))
val dep = Dependency(Module("com.thoughtworks.paranamer", "paranamer"), "2.6")
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -386,9 +386,9 @@ object ResolverTests extends TestSuite {
}
'depsFromDefaultProfile{
async {
val dep = Dependency(Module("com.github.dummy", "libb", "0.3.3"))
val dep = Dependency(Module("com.github.dummy", "libb"), "0.3.3")
val trDeps = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6")))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -407,9 +407,9 @@ object ResolverTests extends TestSuite {
val f =
for (version <- Seq("0.5.3", "0.5.4", "0.5.5", "0.5.6")) yield {
async {
val dep = Dependency(Module("com.github.dummy", "libb", version))
val dep = Dependency(Module("com.github.dummy", "libb"), version)
val trDeps = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6")))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -430,9 +430,9 @@ object ResolverTests extends TestSuite {
'depsScopeOverrideFromProfile{
async {
// Like com.google.inject:guice:3.0 with org.sonatype.sisu.inject:cglib
val dep = Dependency(Module("com.github.dummy", "libb", "0.4.2"))
val dep = Dependency(Module("com.github.dummy", "libb"), "0.4.2")
val trDeps = Seq(
Dependency(Module("org.escalier", "librairie-standard", "2.11.6")))
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))
val res = await(resolve(
Set(dep),
fetchFrom(repositories),
@ -452,9 +452,9 @@ object ResolverTests extends TestSuite {
'propertySubstitution{
val res =
Resolver.withProperties(
Seq(Dependency(Module("a-company", "a-name", "${a.property}"))),
Seq(Dependency(Module("a-company", "a-name"), "${a.property}")),
Map("a.property" -> "a-version"))
val expected = Seq(Dependency(Module("a-company", "a-name", "a-version")))
val expected = Seq(Dependency(Module("a-company", "a-name"), "a-version"))
assert(res == expected)
}

View File

@ -7,10 +7,10 @@ import scalaz.{-\/, \/, EitherT}
import scalaz.concurrent.Task
import scalaz.Scalaz._
class TestRepository(projects: Map[Module, Project]) extends Repository {
def find(module: Module, cachePolicy: CachePolicy) =
class TestRepository(projects: Map[(Module, String), Project]) extends Repository {
def find(module: Module, version: String, cachePolicy: CachePolicy) =
EitherT(Task.now(
projects.get(module).toRightDisjunction("Not found")
projects.get((module, version)).toRightDisjunction("Not found")
))
def versions(organization: String, name: String, cachePolicy: CachePolicy) =
EitherT(Task.now[String \/ Versions](

View File

@ -69,7 +69,7 @@ class Backend($: BackendScope[Unit, State]) {
def updateTree(resolution: Resolution, target: String, reverse: Boolean) = {
def depsOf(dep: Dependency) =
resolution.projectsCache.get(dep.module).toSeq.flatMap(t => Resolver.finalDependencies(dep, t._2).filter(resolution.filter getOrElse Resolver.defaultFilter))
resolution.projectsCache.get(dep.moduleVersion).toSeq.flatMap(t => Resolver.finalDependencies(dep, t._2).filter(resolution.filter getOrElse Resolver.defaultFilter))
lazy val reverseDeps = {
var m = Map.empty[Module, Seq[Dependency]]
@ -169,7 +169,7 @@ class Backend($: BackendScope[Unit, State]) {
def addModule(e: ReactEventI) = {
e.preventDefault()
$.modState{ state =>
val modules = state.modules :+ Dependency(Module("", "", ""))
val modules = state.modules :+ Dependency(Module("", ""), "")
println(s"Modules:\n${modules.mkString("\n")}")
state.copy(modules = modules, editModuleIdx = modules.length - 1)
}
@ -211,26 +211,26 @@ object App {
def depItem(dep: Dependency) =
<.tr(
^.`class` := (if (res.errors.contains(dep.module)) "danger" else ""),
^.`class` := (if (res.errors.contains(dep.moduleVersion)) "danger" else ""),
<.td(dep.module.organization),
<.td(dep.module.name),
<.td(dep.module.version),
<.td(dep.version),
<.td(Seq[Seq[TagMod]](
if (dep.scope == Scope.Compile) Seq() else Seq(infoLabel(dep.scope.name)),
if (dep.`type`.isEmpty || dep.`type` == "jar") Seq() else Seq(infoLabel(dep.`type`)),
if (dep.classifier.isEmpty) Seq() else Seq(infoLabel(dep.classifier)),
if (dep.optional) Seq(infoLabel("optional")) else Seq(),
res.errors.get(dep.module).map(errs => errorLabel("Error", errs.mkString("; "))).toSeq
res.errors.get(dep.moduleVersion).map(errs => errorLabel("Error", errs.mkString("; "))).toSeq
)),
<.td(Seq[Seq[TagMod]](
res.projectsCache.get(dep.module) match {
res.projectsCache.get(dep.moduleVersion) match {
case Some((repo: Remote, _)) =>
// FIXME Maven specific, generalize if/when adding support for Ivy
val relPath =
dep.module.organization.split('.').toSeq ++ Seq(
dep.module.name,
dep.module.version,
s"${dep.module.name}-${dep.module.version}"
dep.version,
s"${dep.module.name}-${dep.version}"
)
Seq(
@ -274,9 +274,9 @@ object App {
def remove = apply("remove")
}
val moduleEditModal = ReactComponentB[(Module, Int, Backend)]("EditModule")
val moduleEditModal = ReactComponentB[((Module, String), Int, Backend)]("EditModule")
.render{ P =>
val (module, moduleIdx, backend) = P
val ((module, version), moduleIdx, backend) = P
<.div(^.`class` := "modal fade", ^.id := "moduleEdit", ^.role := "dialog", ^.aria.labelledby := "moduleEditTitle",
<.div(^.`class` := "modal-dialog", <.div(^.`class` := "modal-content",
<.div(^.`class` := "modal-header",
@ -304,8 +304,8 @@ object App {
<.div(^.`class` := "form-group",
<.label(^.`for` := "inputVersion", "Version"),
<.input(^.`class` := "form-control", ^.id := "inputVersion", ^.placeholder := "Version",
^.onChange ==> backend.updateModule(moduleIdx, (dep, value) => dep.copy(module = dep.module.copy(version = value))),
^.value := module.version
^.onChange ==> backend.updateModule(moduleIdx, (dep, value) => dep.copy(version = value)),
^.value := version
)
),
<.div(^.`class` := "modal-footer",
@ -326,7 +326,7 @@ object App {
<.tr(
<.td(dep.module.organization),
<.td(dep.module.name),
<.td(dep.module.version),
<.td(dep.version),
<.td(
<.a(Attr("data-toggle") := "modal", Attr("data-target") := "#moduleEdit", ^.`class` := "icon-action",
^.onClick ==> backend.editModule(idx),
@ -363,7 +363,7 @@ object App {
deps.zipWithIndex.map((depItem _).tupled)
)
),
moduleEditModal((deps.lift(editModuleIdx).fold(Module("", "", ""))(_.module), editModuleIdx, backend))
moduleEditModal((deps.lift(editModuleIdx).fold((Module("", ""), ""))(_.moduleVersion), editModuleIdx, backend))
)
}
.build