summaryrefslogblamecommitdiff
path: root/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
blob: 948b13dfb237350003542bb2eadaa02653c29820 (plain) (tree)
1
2
3
4
5
6
7
8
9
            
                  
 
                                                 
                     
                                     
                                  
                               
                                            

                                                                    
 
                                                           


                               
                                         
                                                




                                                        











                                                                                        
 
                              
                                                                                       
                                             
                                      
                                                    
                               
                                                                              
               


                                                                                                          
                                                                          

   
                                                        







                                                                                                


                        



                          
                                                                             




                                    
                     
                                   







                                    
                     
                                   
             
     

   
                                                                   

                                               




                                           
                         


                             

     

   


                                                                                 
 

                                                                            

   

                                        
                                      
                                     
                                                                   
                                        
 
                   
                  
 





                                                    

                                 

                 
                      
                


                       

   

                                       
                                                                 
   
                                       

                                                               
 






                                                                                  


                                                                

                                                                  

                                         

 













                                                                     
                                          







                                                                     
                                                                                        
                                     
                       
                              


                               
                                        
               
                
                            



                                    






                                                                                 


   
package mill
package scalajslib

import ammonite.ops.{Path, exists, ls, mkdir, rm}
import coursier.Cache
import coursier.maven.MavenRepository
import mill.eval.{PathRef, Result}
import mill.eval.Result.Success
import mill.scalalib.Lib.resolveDependencies
import mill.scalalib.{CompilationResult, Dep, DepSyntax, TestModule}
import mill.util.{Ctx, Loose}

trait ScalaJSModule extends scalalib.ScalaModule { outer =>

  def scalaJSVersion: T[String]

  trait Tests extends TestScalaJSModule {
    override def scalaWorker = outer.scalaWorker
    override def scalaVersion = outer.scalaVersion()
    override def scalaJSVersion = outer.scalaJSVersion()
    override def moduleDeps = Seq(outer)
  }

  private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r
  private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r

  def scalaJSBinaryVersion = T{
    scalaJSVersion() match {
      case ReleaseVersion(major, minor, _) => s"$major.$minor"
      case MinorSnapshotVersion(major, minor, _) => s"$major.$minor"
      case _ => scalaJSVersion()
    }
  }

  def scalaJSBridgeVersion = T{ scalaJSVersion().split('.').dropRight(1).mkString(".") }

  def sjsBridgeClasspath = T {
    val jsBridgeKey = "MILL_SCALAJS_BRIDGE_" + scalaJSBridgeVersion().replace('.', '_')
    val jsBridgePath = sys.props(jsBridgeKey)
    if (jsBridgePath != null) Success(
      Agg(PathRef(Path(jsBridgePath), quick = true))
    ) else resolveDependencies(
      Seq(Cache.ivy2Local, MavenRepository("https://repo1.maven.org/maven2")),
      "2.12.4",
      Seq(
        ivy"com.lihaoyi::mill-scalajslib-jsbridges-${scalaJSBridgeVersion()}:${sys.props("MILL_VERSION")}"
      )
    ).map(_.filter(_.path.toString.contains("mill-scalajslib-jsbridges")))
  }

  def scalaJSLinkerClasspath: T[Loose.Agg[PathRef]] = T{
    val commonDeps = Seq(
      ivy"org.scala-js::scalajs-tools:${scalaJSVersion()}",
      ivy"org.scala-js::scalajs-sbt-test-adapter:${scalaJSVersion()}"
    )
    val envDep = scalaJSBinaryVersion() match {
      case v if v.startsWith("0.6") => ivy"org.scala-js::scalajs-js-envs:${scalaJSVersion()}"
      case v if v.startsWith("1.0") => ivy"org.scala-js::scalajs-env-nodejs:${scalaJSVersion()}"
    }
    resolveDependencies(
      repositories,
      "2.12.4",
      commonDeps :+ envDep
    )
  }

  def toolsClasspath = T { sjsBridgeClasspath() ++ scalaJSLinkerClasspath() }

  def fastOpt = T {
    link(
      ScalaJSBridge.scalaJSBridge(),
      toolsClasspath(),
      runClasspath(),
      finalMainClassOpt().toOption,
      FastOpt
    )
  }

  def fullOpt = T {
    link(
      ScalaJSBridge.scalaJSBridge(),
      toolsClasspath(),
      runClasspath(),
      finalMainClassOpt().toOption,
      FullOpt
    )
  }

  override def runLocal(args: String*) = T.command { run(args:_*) }

  override def run(args: String*) = T.command {
    finalMainClassOpt() match{
      case Left(err) => Result.Failure(err)
      case Right(_) =>
        ScalaJSBridge.scalaJSBridge().run(
          toolsClasspath().map(_.path),
          nodeJSConfig(),
          fastOpt().path.toIO
        )
        Result.Success(())
    }

  }

  override def runMainLocal(mainClass: String, args: String*) = T.command[Unit] {
    mill.eval.Result.Failure("runMain is not supported in Scala.js")
  }

  override def runMain(mainClass: String, args: String*) = T.command[Unit] {
    mill.eval.Result.Failure("runMain is not supported in Scala.js")
  }

  def link(worker: ScalaJSWorker,
           toolsClasspath: Agg[PathRef],
           runClasspath: Agg[PathRef],
           mainClass: Option[String],
           mode: OptimizeMode)(implicit ctx: Ctx.Dest): PathRef = {
    val outputPath = ctx.dest / "out.js"

    mkdir(ctx.dest)
    rm(outputPath)

    val classpath = runClasspath.map(_.path)
    val sjsirFiles = classpath
      .filter(path => exists(path) && path.isDir)
      .flatMap(ls.rec)
      .filter(_.ext == "sjsir")
    val libraries = classpath.filter(_.ext == "jar")
    worker.link(
      toolsClasspath.map(_.path),
      sjsirFiles,
      libraries,
      outputPath.toIO,
      mainClass,
      mode == FullOpt
    )
    PathRef(outputPath)
  }

  override def scalacPluginIvyDeps = T{
    super.scalacPluginIvyDeps() ++
    Seq(ivy"org.scala-js:::scalajs-compiler:${scalaJSVersion()}")
  }
  override def scalaLibraryIvyDeps = T{
    Seq(ivy"org.scala-js::scalajs-library:${scalaJSVersion()}")
  }

  // publish artifact with name "mill_sjs0.6.4_2.12" instead of "mill_sjs0.6_2.12"
  def crossFullScalaJSVersion: T[Boolean] = false
  def artifactScalaJSVersion: T[String] = T {
    if (crossFullScalaJSVersion()) scalaJSVersion()
    else scalaJSBinaryVersion()
  }

  override def artifactSuffix: T[String] = T {
    s"_sjs${artifactScalaJSVersion()}_${artifactScalaVersion()}"
  }

  override def platformSuffix = s"_sjs${artifactScalaJSVersion()}"

  def nodeJSConfig = T { NodeJSConfig() }
}

trait TestScalaJSModule extends ScalaJSModule with TestModule {
  def scalaJSTestDeps = T {
    resolveDeps(T.task {
      Loose.Agg(
        ivy"org.scala-js::scalajs-library:${scalaJSVersion()}",
        ivy"org.scala-js::scalajs-test-interface:${scalaJSVersion()}"
      )
    })
  }

  def fastOptTest = T {
    link(
      ScalaJSBridge.scalaJSBridge(),
      toolsClasspath(),
      scalaJSTestDeps() ++ runClasspath(),
      None,
      FastOpt
    )
  }

  override def testLocal(args: String*) = T.command { test(args:_*) }

  override def test(args: String*) = T.command {
    val (close, framework) = mill.scalajslib.ScalaJSBridge.scalaJSBridge().getFramework(
        toolsClasspath().map(_.path),
        nodeJSConfig(),
        testFrameworks().head,
        fastOptTest().path.toIO
      )

    val (doneMsg, results) = scalaWorker
      .worker()
      .runTests(
        _ => Seq(framework),
        runClasspath().map(_.path),
        Agg(compile().classes.path),
        args
      )
    val res = TestModule.handleResults(doneMsg, results)
    // Hack to try and let the Node.js subprocess finish streaming it's stdout
    // to the JVM. Without this, the stdout can still be streaming when `close()`
    // is called, and some of the output is dropped onto the floor.
    Thread.sleep(100)
    close()
    res
  }

}