Problem
When using Waypoint 10.0.0-M6 together with Laminar 18.0.0-M4 in a multi-module Scala.js project, the Scala.js linker fails due to conflicting tuplez versions on the classpath.
Root cause
Airstream 18.0.0-M4 depends on tuplez-full 0.5.0-M2, while Waypoint's transitive dependency url-dsl 0.7.0 depends on tuplez-full-light 0.4.0. These are different artifacts (different artifact IDs), so neither evicts the other during dependency resolution. Both end up on the classpath, each providing their own app.tulz.tuplez.Composition class with incompatible methods:
Airstream 18.0.0-M4 ──→ tuplez-full 0.5.0-M2 (has Composition.Scalar$plusT, no decompose)
Waypoint 10.0.0-M6 ──→ url-dsl 0.7.0 ──→ tuplez-full-light 0.4.0 (has Composition.decompose, no Scalar$plusT)
This means excluding either artifact breaks the other:
- With both on classpath →
Scalar$plusT not found (Airstream's combineWith fails)
- Exclude
tuplez-full-light → decompose not found (url-dsl's PathSegment./ fails)
- Exclude
tuplez-full → Scalar$plusT not found (same as having both)
Error messages
With both artifacts present (default):
Referring to non-existent method app.tulz.tuplez.Composition$.Scalar$plusT()app.tulz.tuplez.Composition
called from constructor MyComponent.<init>(...)void
With tuplez-full-light excluded:
Referring to non-existent method app.tulz.tuplez.Composition.decompose(java.lang.Object)scala.Tuple2
called from urldsl.language.PathSegment.$div(...)urldsl.language.PathSegment
Notes
- Compile succeeds in all cases — the error only appears at Scala.js link time (both
fastLinkJS and fullLinkJS) in multi-module setups where one module depends on Laminar and the downstream module adds Waypoint.
- In a single-module project, the linker happens to pick the right class and the conflict doesn't surface.
Minimal reproduction
Requires Mill 1.1.2, Scala 3.8.2, Scala.js 1.20.2.
Project structure
├── build.mill
├── lib/src/MyComponent.scala # depends on Laminar only
└── app/src/Main.scala # depends on lib + Waypoint
build.mill
package build
import mill.*, scalalib.*, scalajslib.*, scalajslib.api.*
trait CommonJS extends ScalaJSModule {
def scalaVersion = "3.8.2"
def scalaJSVersion = "1.20.2"
def moduleKind = ModuleKind.ESModule
}
object lib extends CommonJS {
def mvnDeps = Seq(mvn"com.raquo::laminar::18.0.0-M4")
}
object app extends CommonJS {
def mvnDeps = Seq(mvn"com.raquo::waypoint::10.0.0-M6")
def moduleDeps = Seq(lib)
}
lib/src/MyComponent.scala
package mylib
import com.raquo.laminar.api.L.*
class MyComponent {
val combined = Var(1).signal.combineWith(
Var(2).signal, Var(3).signal, Var(4).signal, Var(5).signal,
Var(true).signal, Var(true).signal, Var(false).signal, Var(false).signal
)
def element: HtmlElement = div(child.text <-- combined.map(_.toString))
}
app/src/Main.scala
import mylib.MyComponent
object Main {
def main(args: Array[String]): Unit = println(new MyComponent().element)
}
Reproduce
./mill app.compile # ✓ succeeds
./mill app.fastLinkJS # ✗ linking error
./mill app.fullLinkJS # ✗ linking error
Suggested fix
Align the tuplez dependency across Waypoint's url-dsl and Airstream so they use the same artifact and version — ideally tuplez-full 0.5.0-M2 to match Airstream 18.0.0-M4.
Problem
When using Waypoint 10.0.0-M6 together with Laminar 18.0.0-M4 in a multi-module Scala.js project, the Scala.js linker fails due to conflicting
tuplezversions on the classpath.Root cause
Airstream 18.0.0-M4 depends on
tuplez-full0.5.0-M2, while Waypoint's transitive dependencyurl-dsl0.7.0 depends ontuplez-full-light0.4.0. These are different artifacts (different artifact IDs), so neither evicts the other during dependency resolution. Both end up on the classpath, each providing their ownapp.tulz.tuplez.Compositionclass with incompatible methods:This means excluding either artifact breaks the other:
Scalar$plusTnot found (Airstream'scombineWithfails)tuplez-full-light→decomposenot found (url-dsl'sPathSegment./fails)tuplez-full→Scalar$plusTnot found (same as having both)Error messages
With both artifacts present (default):
With
tuplez-full-lightexcluded:Notes
fastLinkJSandfullLinkJS) in multi-module setups where one module depends on Laminar and the downstream module adds Waypoint.Minimal reproduction
Requires Mill 1.1.2, Scala 3.8.2, Scala.js 1.20.2.
Project structure
build.milllib/src/MyComponent.scalaapp/src/Main.scalaReproduce
Suggested fix
Align the
tuplezdependency across Waypoint'surl-dsland Airstream so they use the same artifact and version — ideallytuplez-full0.5.0-M2 to match Airstream 18.0.0-M4.