summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-12-20 22:25:49 -0800
committerLi Haoyi <haoyi@dropbox.com>2014-12-20 22:25:49 -0800
commite6831705b0216f7504129cc6943e1f23e114b959 (patch)
treefba42e201c2a3889aea9a5d2bd6be8d90cc5ce8e
parent37a33d401fd70c56ff7f411afc07a4605e2e1aa2 (diff)
downloadworkbench-e6831705b0216f7504129cc6943e1f23e114b959.tar.gz
workbench-e6831705b0216f7504129cc6943e1f23e114b959.tar.bz2
workbench-e6831705b0216f7504129cc6943e1f23e114b959.zip
basic Scala.js console works
-rw-r--r--build.sbt13
-rw-r--r--example/src/main/scala/example/ScalaJSExample.scala2
-rw-r--r--src/main/scala/workbench/Plugin.scala128
-rw-r--r--src/main/scala/workbench/Server.scala18
4 files changed, 137 insertions, 24 deletions
diff --git a/build.sbt b/build.sbt
index 20618d4..e77fd6a 100644
--- a/build.sbt
+++ b/build.sbt
@@ -39,10 +39,18 @@ lazy val root = project.in(file(".")).settings(defaultSettings:_*).settings(
(fullOptJS in (client, Compile)).value
(artifactPath in (client, Compile, fullOptJS)).value
},
+ resolvers += Resolver.url("scala-js-releases",
+ url("http://dl.bintray.com/content/scala-js/scala-js-releases"))(
+ Resolver.ivyStylePatterns),
+ addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.4"),
libraryDependencies ++= Seq(
+ "org.scala-lang" % "scala-compiler" % scalaVersion.value,
+ "org.scala-lang.modules.scalajs" % s"scalajs-compiler_${scalaVersion.value}" % "0.5.4",
+ "org.scala-lang.modules.scalajs" %% "scalajs-tools" % "0.5.4",
"io.spray" % "spray-can" % "1.3.1",
"io.spray" % "spray-routing" % "1.3.1",
- "com.typesafe.akka" %% "akka-actor" % "2.3.0",
+ "com.typesafe.akka" %% "akka-actor" % "2.3.0",
+ "org.scala-lang.modules" %% "scala-async" % "0.9.1" % "provided",
"com.lihaoyi" %% "autowire" % "0.2.3",
"com.lihaoyi" %% "upickle" % "0.2.5"
),
@@ -56,5 +64,6 @@ lazy val client = project.in(file("client"))
"org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6",
"com.lihaoyi" %%% "autowire" % "0.2.3",
"com.lihaoyi" %%% "upickle" % "0.2.5"
- )
+ ),
+ emitSourceMaps := false
)
diff --git a/example/src/main/scala/example/ScalaJSExample.scala b/example/src/main/scala/example/ScalaJSExample.scala
index 24b047e..0255262 100644
--- a/example/src/main/scala/example/ScalaJSExample.scala
+++ b/example/src/main/scala/example/ScalaJSExample.scala
@@ -20,7 +20,7 @@ object ScalaJSExample {
.asInstanceOf[dom.CanvasRenderingContext2D]
var p = Point(128, 128)
- def color = "black"
+ var color = "black"
var enemiess = List.fill(10)(Point(util.Random.nextInt(255), util.Random.nextInt(255)))
def clear() = {
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