diff options
Diffstat (limited to 'src/main/scala')
-rw-r--r-- | src/main/scala/workbench/Plugin.scala | 128 | ||||
-rw-r--r-- | src/main/scala/workbench/Server.scala | 18 |
2 files changed, 125 insertions, 21 deletions
diff --git a/src/main/scala/workbench/Plugin.scala b/src/main/scala/workbench/Plugin.scala index 3a1b175..dd3cab9 100644 --- a/src/main/scala/workbench/Plugin.scala +++ b/src/main/scala/workbench/Plugin.scala @@ -1,9 +1,15 @@ package com.lihaoyi.workbench +import scala.concurrent.ExecutionContext.Implicits.global import sbt._ import sbt.Keys._ import autowire._ -import upickle.Js -import scala.concurrent.ExecutionContext.Implicits.global +import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys +import scala.scalajs.tools.io._ +import scala.scalajs.tools.optimizer.ScalaJSOptimizer +import scala.scalajs.sbtplugin.ScalaJSPluginInternal._ +import scala.scalajs.sbtplugin.Implicits._ + +import ScalaJSKeys._ object Plugin extends sbt.Plugin { val refreshBrowsers = taskKey[Unit]("Sends a message to all connected web pages asking them to refresh the page") @@ -15,6 +21,11 @@ object Plugin extends sbt.Plugin { val bootSnippet = settingKey[String]("piece of javascript to make things happen") val updatedJS = taskKey[List[String]]("Provides the addresses of the JS files that have changed") + val sjs = inputKey[Unit]("Run a command via the sjs REPL, which compiles it to Javascript and runs it in the browser") + val replFile = taskKey[File]("The temporary file which holds the source code for the currently executing sjs REPL") + val sjsReset = taskKey[Unit]("Reset the currently executing sjs REPL") + + lazy val replHistory = collection.mutable.Buffer.empty[String] val workbenchSettings = Seq( localUrl := ("localhost", 12345), @@ -77,23 +88,9 @@ object Plugin extends sbt.Plugin { streams.value.log.info("workbench: Splicing " + path) val prefix = "http://localhost:12345/" - var s = IO.read(new sbt.File(path.drop(prefix.length))) - - s = s.replace("\nvar ScalaJS = ", "\nvar ScalaJS = ScalaJS || ") - s = s.replaceAll( - "\n(ScalaJS\\.c\\.[a-zA-Z_$0-9]+\\.prototype) = (.*?\n)", - """ - |$1 = $1 || {} - |(function(){ - | var newProto = $2 - | for (var attrname in newProto) { $1[attrname] = newProto[attrname]; } - |})() - |""".stripMargin - ) - for(char <- Seq("d", "c", "h", "i", "n", "m")){ - s = s.replaceAll("\n(ScalaJS\\." + char + "\\.[a-zA-Z_$0-9]+) = ", "\n$1 = $1 || ") - } - IO.write(new sbt.File(path.drop(prefix.length) + ".js"), s.getBytes) + val s = munge(sbt.IO.read(new sbt.File(path.drop(prefix.length)))) + + sbt.IO.write(new sbt.File(path.drop(prefix.length) + ".js"), s.getBytes) server.value.Wire[Api].run(path + ".js", None).call() } } @@ -103,5 +100,96 @@ object Plugin extends sbt.Plugin { server.value.kill() state }} - ) + ) ++ inConfig(Compile)(Seq( + artifactPath in sjs := crossTarget.value / "repl.js", + replFile := { + val f = sourceManaged.value / "repl.scala" + println("Creating replFile\n" + replHistory.mkString("\n")) + sbt.IO.write(f, replHistory.mkString("\n")) + f + }, + sources in Compile += replFile.value, + sjs <<= Def.inputTaskDyn { + import sbt.complete.Parsers._ + val str = sbt.complete.Parsers.any.*.parsed.mkString + val newSnippet = s""" + @scalajs.js.annotation.JSExport object O${replHistory.length}{ + $str + }; + import O${replHistory.length}._ + """ + replHistory.append(newSnippet) + Def.taskDyn { + // Basically C&Ped from fastOptJS, since we dont want this + // special mode from triggering updateBrowsers or similar + val s = streams.value + val output = (artifactPath in sjs).value + + val taskCache = WritableFileVirtualTextFile(s.cacheDirectory / "fastopt-js") + + sbt.IO.createDirectory(output.getParentFile) + + val relSourceMapBase = + if ((relativeSourceMaps in fastOptJS).value) + Some(output.getParentFile.toURI()) + else None + + import ScalaJSOptimizer._ + + (scalaJSOptimizer in fastOptJS).value.optimizeCP( + Inputs(input = (preLinkClasspath in fastOptJS).value), + OutputConfig( + output = WritableFileVirtualJSFile(output), + cache = None, + wantSourceMap = (emitSourceMaps in fastOptJS).value, + relativizeSourceMapBase = relSourceMapBase, + checkIR = (checkScalaJSIR in fastOptJS).value, + disableInliner = (inliningMode in fastOptJS).value.disabled, + batchInline = (inliningMode in fastOptJS).value.batch), + s.log + ) + // end of C&P + val outPath = sbt.IO.relativize( + baseDirectory.value, + (artifactPath in sjs).value + ).get + + sbt.IO.write( + (artifactPath in sjs).value, + sbt.IO.read(output) + s"\n\nO${replHistory.length - 1}()" + ) + Def.task { + server.value.Wire[Api].run( + s"http://localhost:12345/$outPath", + None + ).call() + () + } + }.dependsOn(packageJSDependencies, packageLauncher, compile) + }, + sjsReset := { + println("Clearing sjs REPL History") + replHistory.clear() + }, + sjsReset <<= sjsReset.triggeredBy(fastOptJS) + )) + + def munge(s0: String) = { + var s = s0 + s = s.replace("\nvar ScalaJS = ", "\nvar ScalaJS = ScalaJS || ") + s = s.replaceAll( + "\n(ScalaJS\\.c\\.[a-zA-Z_$0-9]+\\.prototype) = (.*?\n)", + """ + |$1 = $1 || {} + |(function(){ + | var newProto = $2 + | for (var attrname in newProto) { $1[attrname] = newProto[attrname]; } + |})() + |""".stripMargin + ) + for(char <- Seq("d", "c", "h", "i", "n", "m")){ + s = s.replaceAll("\n(ScalaJS\\." + char + "\\.[a-zA-Z_$0-9]+) = ", "\n$1 = $1 || ") + } + s + } } diff --git a/src/main/scala/workbench/Server.scala b/src/main/scala/workbench/Server.scala index 3ffe288..4df8b61 100644 --- a/src/main/scala/workbench/Server.scala +++ b/src/main/scala/workbench/Server.scala @@ -1,8 +1,9 @@ package com.lihaoyi.workbench import akka.actor.{ActorRef, Actor, ActorSystem} +import akka.util.ByteString import com.typesafe.config.ConfigFactory -import sbt.IO +import sbt.{Logger, IO} import spray.httpx.encoding.Gzip import spray.routing.SimpleRoutingApp import akka.actor.ActorDSL._ @@ -12,6 +13,20 @@ import spray.http.{HttpEntity, AllOrigins, HttpResponse} import spray.http.HttpHeaders.`Access-Control-Allow-Origin` import concurrent.duration._ import scala.concurrent.Future +import scala.io.Source +import scala.scalajs.tools.optimizer.{ScalaJSClosureOptimizer, ScalaJSOptimizer} +import scala.scalajs.tools.io._ +import scala.scalajs.tools.logging.Level +import scala.tools.nsc +import scala.tools.nsc.Settings + +import scala.tools.nsc.backend.JavaPlatform +import scala.tools.nsc.plugins.Plugin +import scala.tools.nsc.util.ClassPath.JavaContext +import scala.collection.mutable +import scala.tools.nsc.typechecker.Analyzer +import scala.scalajs.tools.classpath.{CompleteNCClasspath, CompleteCIClasspath, PartialIRClasspath, PartialClasspath} +import scala.tools.nsc.util.{JavaClassPath, DirectoryClassPath} class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingApp{ implicit val system = ActorSystem( @@ -107,4 +122,5 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA } } def kill() = system.shutdown() + }
\ No newline at end of file |