summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Doeraene <sjrdoeraene@gmail.com>2015-01-29 17:50:33 +0100
committerSébastien Doeraene <sjrdoeraene@gmail.com>2015-01-29 17:50:33 +0100
commit33129113b3ebf3dd656bd58c34af476fb852e2f6 (patch)
tree5b84193ecd9eefc05bd9f27b1766e833064182ae
parentea067baf2afe492cde8b10b88e9628812cb4d7cd (diff)
downloadhands-on-scala-js-33129113b3ebf3dd656bd58c34af476fb852e2f6.tar.gz
hands-on-scala-js-33129113b3ebf3dd656bd58c34af476fb852e2f6.tar.bz2
hands-on-scala-js-33129113b3ebf3dd656bd58c34af476fb852e2f6.zip
Upgrade and update the whole thing for Scala.js 0.6.0-RC2.
Both the builds, as well as the text of the book, have been completely updated for Scala.js 0.6.x.
-rw-r--r--book/src/main/scala/book/Main.scala2
-rw-r--r--book/src/main/scalatex/book/Intro.scalatex2
-rw-r--r--book/src/main/scalatex/book/handson/CanvasApp.scalatex10
-rw-r--r--book/src/main/scalatex/book/handson/ClientServer.scalatex4
-rw-r--r--book/src/main/scalatex/book/handson/CommandLine.scalatex48
-rw-r--r--book/src/main/scalatex/book/handson/GettingStarted.scalatex42
-rw-r--r--book/src/main/scalatex/book/handson/PublishingModules.scalatex2
-rw-r--r--book/src/main/scalatex/book/handson/WebPage.scalatex26
-rw-r--r--book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex16
-rw-r--r--book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex80
-rw-r--r--book/src/main/scalatex/book/indepth/DesignSpace.scalatex4
-rw-r--r--book/src/main/scalatex/book/indepth/JavaAPIs.scalatex2
-rw-r--r--book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex214
-rw-r--r--build.sbt8
-rw-r--r--examples/crossBuilds/clientserver/build.sbt18
-rw-r--r--examples/crossBuilds/clientserver/project/build.sbt4
-rw-r--r--examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala2
-rw-r--r--examples/crossBuilds/clientserver2/build.sbt20
-rw-r--r--examples/crossBuilds/clientserver2/project/build.properties1
-rw-r--r--examples/crossBuilds/clientserver2/project/build.sbt4
-rw-r--r--examples/crossBuilds/clientserver2/server/src/main/scala/simple/Page.scala2
-rw-r--r--examples/crossBuilds/simple/build.sbt2
-rw-r--r--examples/crossBuilds/simple/project/build.properties1
-rw-r--r--examples/crossBuilds/simple/project/build.sbt2
-rw-r--r--examples/crossBuilds/simple2/project/build.properties1
-rw-r--r--examples/crossBuilds/simple2/project/build.sbt4
-rw-r--r--examples/demos/build.sbt12
-rw-r--r--examples/demos/src/main/scala/scrollmenu/Controller.scala2
-rw-r--r--examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala2
-rw-r--r--project/build.sbt4
30 files changed, 286 insertions, 255 deletions
diff --git a/book/src/main/scala/book/Main.scala b/book/src/main/scala/book/Main.scala
index 1f8521a..737c130 100644
--- a/book/src/main/scala/book/Main.scala
+++ b/book/src/main/scala/book/Main.scala
@@ -74,7 +74,7 @@ object Main {
frag
)
),
- onload:=s"Controller().main($data)"
+ onload:=s"scrollmenu.Controller().main($data)"
)
}
diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex
index a504aa6..d69a2e5 100644
--- a/book/src/main/scalatex/book/Intro.scalatex
+++ b/book/src/main/scalatex/book/Intro.scalatex
@@ -21,7 +21,7 @@
while ((x < 10)) {
x = ((x + 3) | 0)
};
- ScalaJS.m.s_Predef()
+ ScalaJS.m.s_Predef$()
.println__O__V(x)
// 12
});
diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
index b49f1e5..ae97dee 100644
--- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex
+++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
@@ -25,7 +25,7 @@
@hl.ref(canvasapp/"ScratchPad.scala", "/*setup*/", end = "/*code*/")
@p
- As described earlier, this code uses the @lnk.dom.getElementById} function to fish out the @code{canvas} element that we interested in from the DOM. It then gets a rendering context from that @code{canvas}, and sets the height and width of the canvas to completely fill its containing element. Lastly, it fills out the canvas light-gray, so that we can see it on the page.
+ As described earlier, this code uses the @lnk.dom.getElementById function to fish out the @code{canvas} element that we interested in from the DOM. It then gets a rendering context from that @code{canvas}, and sets the height and width of the canvas to completely fill its containing element. Lastly, it fills out the canvas light-gray, so that we can see it on the page.
@p
Next, let's set some event handlers on the canvas:
@@ -35,7 +35,7 @@
@hl.ref(canvasapp/"ScratchPad.scala", "/*code*/")
@less
- @BookData.example(canvas, "ScratchPad().main")
+ @BookData.example(canvas, "canvasapp.ScratchPad().main")
@p
This code sets up the @lnk.dom.mousedown and @lnk.dom.mouseup events to keep track of whether or not the mouse has currently been clicked. It then draws black squares any time you move the mouse while the button is down. This lets you basically click-and-drag to draw pictures on the canvas. Try it out!
@@ -69,7 +69,7 @@
@hl.ref(canvasapp/"Clock.scala", "/*code*/")
@less
- @BookData.example(canvas, "Clock().main")
+ @BookData.example(canvas, "canvasapp.Clock().main")
@p
As you can see, we're using more @lnk("Canvas APIs", "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D"), in this case dealing with rendering text on the canvas. Another thing we're using is the Javascript @lnk("Date", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date") class, in Scala.js under the full name @hl.scala{scala.scalajs.js.Date}, here imported as @hl.scala{js.Date}. Again, click on the link @i(cls:="fa fa-link ") icon to view the full-code if you're having trouble here.
@@ -151,7 +151,7 @@
At almost 100 lines of code, this is quite a meaty example! Nonetheless, when all is said and done, you will find that the example actually works! Try it out!
@div
- @BookData.example(canvas, "FlappyLine().main")
+ @BookData.example(canvas, "canvasapp.FlappyLine().main")
@sect{Canvas Recap}
@p
@@ -161,7 +161,7 @@
@p
We've by now written a good chunk of Scala.js code, and perhaps debugged some mysterious errors, and tried some new things. One thing you've probably noticed is the efficiency of the process: you make a change in your editor, the browser reloads itself, and life goes on. There is a compile cycle, but after a few runs the compiler warms up and the compilation cycle drops to less than a second.
@p
- Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlighting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
+ Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existing Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlighting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
@sect{Full Scala}
@p
diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex
index 87c959d..0d60192 100644
--- a/book/src/main/scalatex/book/handson/ClientServer.scalatex
+++ b/book/src/main/scalatex/book/handson/ClientServer.scalatex
@@ -12,7 +12,7 @@
@ul
@li
- Javascript on the client v.s. PHP/Perl/Python/Ruby/Java on the client
+ Javascript on the client v.s. PHP/Perl/Python/Ruby/Java on the server
@li
Most back-ends make heavy use of C extensions, and front-end code was tightly coupled to the DOM. Even if you manage to port the main language
@p
@@ -49,7 +49,7 @@
@hl.ref(wd/'examples/'crossBuilds/'clientserver/"build.sbt")
@p
- We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in @sect.ref{Cross Publishing Libraries}) and the settings to add @lnk.github.Scalatags and @lnk.github.uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project. Note also the @hl.scala{packageArchetype.java_application} setting, which isn't strictly necessary depending on what you want to do with the application, but this example needs it as part of the deployment to Heroku.
+ We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{enablePlugins(ScalaJSPlugin)}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in @sect.ref{Cross Publishing Libraries}) and the settings to add @lnk.github.Scalatags and @lnk.github.uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project. Note also the @hl.scala{packageArchetype.java_application} setting, which isn't strictly necessary depending on what you want to do with the application, but this example needs it as part of the deployment to Heroku.
@p
The @code{client} subproject is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{server} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additional thing: we add the output of @code{fastOptJS} from the client to the @code{resources} on the server. This will allow the @code{server} to serve the compiled-javascript from our @code{client} project from its resources.
diff --git a/book/src/main/scalatex/book/handson/CommandLine.scalatex b/book/src/main/scalatex/book/handson/CommandLine.scalatex
index ea845da..b7a16cf 100644
--- a/book/src/main/scalatex/book/handson/CommandLine.scalatex
+++ b/book/src/main/scalatex/book/handson/CommandLine.scalatex
@@ -66,11 +66,11 @@
@hl.bash
> package
@p
- Also like on Scala-JVM, Scala.js also supports the @code{package} command. This command generates a @code{.jar} like it does in Scala-JVM, except this version appends this weird @code{_sjs0.5} suffix.
+ Also like on Scala-JVM, Scala.js also supports the @code{package} command. This command generates a @code{.jar} like it does in Scala-JVM, except this version appends this weird @code{_sjs0.6} suffix.
@hl.bash
target/scala-2.11/
- └── example_sjs0.5_2.11-0.1-SNAPSHOT.jar
+ └── example_sjs0.6_2.11-0.1-SNAPSHOT.jar
@p
The purpose of this suffix is to link the compiled @code{.jar} file to the version of Scala.js used to compile it. This allows you to make sure that you don't accidentally depend on a version of a jar that is incompatible with your current version.
@@ -121,52 +121,62 @@
This exhibits the weirdness of @hl.scala{Double.toString} in Scala.js, which is one of the few ways in which @sect.ref("Deviations from Scala-JVM", "Scala.js deviates from Scala-JVM"). This also shows us we're really running on Scala.js: on Scala-JVM, @hl.scala{(1.0).toString} returns @hl.scala{"1.0"} rather than @hl.scala{"1"}!
@p
- One thing you may be wondering is: when you run a Scala.js program in the terminal, how does it execute the output Javascript? What about the DOM? and Ajax calls? Can it access the filesystem? The answer to all these questions is "it depends": it turns out there are multiple ways you can run Scala.js from the command-line:
+ One thing you may be wondering is: when you run a Scala.js program in the terminal, how does it execute the output Javascript? What about the DOM? and Ajax calls? Can it access the filesystem? The answer to all these questions is "it depends": it turns out there are multiple ways you can run Scala.js from the command-line, dictated by the @i{stage} and the @i{environment}.
+
+ @p
+ By default, runs are done in the @code{PreLinkStage}, which uses the Rhino environment. With the sbt setting @hl.scala{jsDependencies += RuntimeDOM}, the Scala.js runner will automatically set up @b{env.js} in Rhino so that you have access to an emulation of the DOM API.
+
+ @p
+ You can enable a different stage, @code{FastOptStage} or @code{FullOptStage}, with the following sbt command:
+
+ @hl.bash
+ > set scalaJSStage in Global := FastOptStage
+
+ @p
+ In @code{FastOptStage} and @code{FullOptStage}, the environment can be one of @b{Node.js} or @b{PhantomJS}. These JavaScript VMs must be installed separately.
@ul
@li
- @b{Rhino} using @code{sbt run}
- @li
- @b{Node.js} using @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, having installed Node.js separately
+ By default, Node.js is used.
@li
- @b{PhantomJS} using @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, having installed PhantomJS separately, and turned on @hl.scala{jsDependencies += RuntimeDOM} in SBT
+ With the sbt setting @hl.scala{jsDependencies += RuntimeDOM}, PhantomJS is used instead, so that a headless DOM is available.
@p
- Typically, the best way to get started is using Rhino and @code{sbt run}, since it's setup-free, and setting up Node.js or PhantomJS later as necessary. The next two sections elaborate on the differences between these ways of running your code. Check out the later sections on @sect.ref{Headless Runtimes} and @sect.ref{Run Configurations} to learn more about the other settings and why you would want to use them.
+ Typically, the best way to get started is using Rhino, since it's setup-free, and setting up Node.js or PhantomJS later as necessary. The next two sections elaborate on the differences between these ways of running your code. Check out the later sections on @sect.ref{Headless Runtimes} and @sect.ref{Stages} to learn more about the other settings and why you would want to use them.
@sect{The test Command}
@hl.bash
> test
@p
- The @code{sbt test} command behaves very similar to @code{sbt run}. It can also be prefixed e.g. @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, or run in either Rhino, Node.js or PhantomJS.
+ The @code{sbt test} command behaves very similarly to @code{sbt run}. It also runs on Rhino, Node.js or PhantomJS, dependending of the stage and the dependency on the DOM.
@p
- The difference is that instead of simply running your @code{main} method, @code{sbt test} runs whatever test-suite you have set-up, which will look through your @code{tests/} folder to find suites of tests it has to run, and aggregate the results formatted nicely for you to see. The exact operation of this depends on which testing library you're using
+ The difference is that instead of simply running your @code{main} method, @code{sbt test} runs whatever test-suite you have set-up, which will look through your @code{tests/} folder to find suites of tests it has to run, and aggregate the results formatted nicely for you to see. The exact operation of this depends on which testing library you're using.
@p
We won't spend much time talking about @code{sbt test} here. Not because it's not important: it most certainly is! Rather, we will be spending a good amount of time setting up tests in @sect.ref("Cross Publishing Libraries", "the next chapter"), so feel free to jump ahead if you want to see an example usage of @code{sbt test}.
@sect{Headless Runtimes}
@ul
@li
- @lnk.misc.Rhino is the default way of running Scala.js applications, and occurs when you hit @code{sbt run} from the terminal. The upside of using Rhino is that it is pure-Java, and doesn't need any additional binaries or installation. The downside of using Rhino is that it is slow: maybe a hundred times slower than the alternatives, making it not suitable for running long-running, compute-intensive programs. Furthermore, it's a very sparse runtime environment, with no DOM access or similar.
+ @lnk.misc.Rhino is the default way of running Scala.js applications. The upside of using Rhino is that it is pure-Java, and doesn't need any additional binaries or installation. The downside of using Rhino is that it is slow: maybe a hundred times slower than the alternatives, making it not suitable for running long-running, compute-intensive programs.
@li
- @lnk.misc.Nodejs, a relatively new Javascript runtime based on Google's V8 Javascript engine, Node.js lets you run your Scala.js application from the command line much faster than in Rhino, with performance that matches that of modern browsers. However, you need to separately @lnk("install Node.js", "http://nodejs.org/download/") in order to use it. Like Rhino, it comes with a bare-minimal runtime environment, with no DOM or browser-related functionality. You need to run @code{sbt fastOptStage::run} to run using Node.js.
+ @lnk.misc.Nodejs, a relatively new Javascript runtime based on Google's V8 Javascript engine, Node.js lets you run your Scala.js application from the command line much faster than in Rhino, with performance that matches that of modern browsers. However, you need to separately @lnk("install Node.js", "http://nodejs.org/download/") in order to use it. Node.js does not have DOM or browser-related functionality. You need to set the stage with @code{set scalaJSStage in Global := FastOptStage} to run using Node.js.
@li
- @lnk.misc.PhantomJS is a headless Webkit browser. This means that unlike Node.js or Rhino, PhantomJS provides you with a full DOM and all its APIs to use in your tests, if you wish to e.g. test interactions with the HTML of the web page. On the other hand, it is somewhat slower than Node.js, though still much faster than Rhino. Like Node.js, it needs to be installed separately. You need to run You need to run @code{sbt fastOptStage::run}, as well as setting the @hl.scala{jsDependencies += RuntimeDOM} flag in your SBT configuration, to use PhantomJS.
+ @lnk.misc.PhantomJS is a headless Webkit browser. This means that unlike Node.js, PhantomJS provides you with a full DOM and all its APIs to use in your tests, if you wish to e.g. test interactions with the HTML of the web page. On the other hand, it is somewhat slower than Node.js, though still much faster than Rhino. Like Node.js, it needs to be installed separately. You need to set the stage with @code{set scalaJSStage in Global := FastOptStage}, as well as setting the @hl.scala{jsDependencies += RuntimeDOM} flag in your SBT configuration, to use PhantomJS.
@p
- These are your three options to run your Scala.js code via the command-line. Generally, it's easiest to get started with Rhino since it's the default and requires no setup, though you may find it worthwhile to setup Node or Phantom if you need additional speed or DOM-integration in your runs.
+ These are your three options to run your Scala.js code via the command-line. Generally, it's easiest to get started with Rhino since it's the default and requires no setup, though you will quickly find it worthwhile to setup Node.js or PhantomJS to speed up your runs and tests.
-@sect{Run Configurations}
+@sect{Stages}
@p
- You may have noticed that in Rhino we use @code{sbt run}, whereas for Node.js or PhantomJS we use @code{sbt fastOptStage::run}. It turns out that similar to the split between @code{fastOptJS}/@code{fullOptJS}, you have a range of options of how you want to prepare your Scala.js code for execution from the command line:
+ Let us recap the three different stages of execution, and what they mean.
@ul
@li
- @code{sbt run}: this does not perform any optimization of the output Scala.js files, and does lazy-loading to minimize the amount of files being loading into the interpreter. This is necessary for Rhino because it can't handle large blobs of Javascript, but doesn't map to any compilation mode you'd use for the browser.
+ @code{PreLinkStage} (the default): this does not perform any optimization of the output Scala.js files, and does lazy-loading to minimize the amount of files being loading into the interpreter. This is necessary for Rhino because it can't handle large blobs of Javascript, but doesn't map to any compilation mode you'd use for the browser.
@li
- @code{sbt fastOptStage::run}: this performs the same compilation and optimization as @code{sbt fastOptJS}, as described under Fast Optimizations. It then takes the entire output executable (which probably weighs around 1mb) and hands it to Node.js or PhantomJS, which then run it.
+ @code{FastOptStage}: this performs the same compilation and optimization as @code{sbt fastOptJS}, as described under Fast Optimizations. It then takes the entire output executable (which probably weighs around 1mb) and hands it to Node.js or PhantomJS, which then run it.
@li
- @code{sbt fullOptStage::run}: this performs the same compilation and optimization as @code{sbt fullOptJS}, as described under Full Optimizations. This takes longer to run than @code{sbt fastOptStage::run}, and results in a smaller/faster executable. Practically speaking, this size/speed advantage does not matter from the command line, but @code{fullOptStage::run} is still useful to verify that the behavior does not change (it shouldn't!) under the aggressive full optimization.
+ @code{FullOptStage}: this performs the same compilation and optimization as @code{sbt fullOptJS}, as described under Full Optimizations. This takes longer to run than the @code{FastOptStage}, and results in a smaller/faster executable. Practically speaking, this size/speed advantage does not matter from the command line, but @code{FullOptStage} is still useful to verify that the behavior does not change (it shouldn't!) under the aggressive full optimization. This is typically used in continuous integration builds, but rarely manually.
@hr
diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
index a77245d..c200f1e 100644
--- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex
+++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
@@ -182,7 +182,7 @@
This is the HTML page which our toy app lives in, and the same page that we have so far been using to view the app in the browser. To anyone who has used HTML, most of it is probably familiar. Things of note are the @hl.html{<script>} tags: @hl.scala{"../example-fastopt.js"} Is the executable blob spat out by the compiler, which we need to include in the HTML page for anything to happen. This is where the results of your compiled Scala code appear. @hl.scala{"workbench.js"} is the client for the Workbench plugin that connects to SBT, reloads the browser and forwards logspam to the browser console.
@p
- The @hl.scala{ScalaJSExample().main()} call is what kicks off the Scala.js application and starts its execution. Scala.js follows Scala semantics in that @hl.scala{object}s are evaluated lazily, with no top-level code allowed. This is in contrast to Javascript, where you can include top-level statements and object-literals in your code which execute immediately. In Scala.js, nothing happens when @code{../example-fastopt.js} is imported! We have to call the main-method first. In this case, we're passing the canvas object (attained using @hl.javascript{getElementById}) to it so it knows where to do its thing.
+ The @hl.scala{example.ScalaJSExample().main()} call is what kicks off the Scala.js application and starts its execution. Scala.js follows Scala semantics in that @hl.scala{object}s are evaluated lazily, with no top-level code allowed. This is in contrast to Javascript, where you can include top-level statements and object-literals in your code which execute immediately. In Scala.js, nothing happens when @code{../example-fastopt.js} is imported! We have to call the main-method first. In this case, we're passing the canvas object (attained using @hl.javascript{getElementById}) to it so it knows where to do its thing.
@p
@@ -193,7 +193,7 @@
@sect{Publishing}
@p
- The last thing that we'll do with our toy application is to publish it. If you look in the @code{target/scala2.11} folder, you'll see the output of everything we've done so far:
+ The last thing that we'll do with our toy application is to publish it. If you look in the @code{target/scala-2.11} folder, you'll see the output of everything we've done so far:
@hl.bash
target/scala-2.11
@@ -222,28 +222,26 @@
@hl.bash
haoyi-mbp:temp haoyi$ du -h target/scala-2.11/example-fastopt.js
- 856K target/scala-2.11/example-fastopt.js
+ 656K target/scala-2.11/example-fastopt.js
@p
- 856 Kilobytes for a hello world app! That is clearly too large. If you examine the contents of the file, you'll see that your code has been translated into something like this:
+ 656 Kilobytes for a hello world app! That is clearly too large. If you examine the contents of the file, you'll see that your code has been translated into something like this:
@hl.javascript
- var i$2 = i;
- if (((ScalaJS.m.Lexample_ScalaJSExample().count$1 % 30000) === 0)) {
- ScalaJS.m.Lexample_ScalaJSExample().clear__V()
+ var v1 = i;
+ if (((count$1.elem$1 % 3000) === 0)) {
+ ScalaJS.m.Lexample_ScalaJSExample$().example$ScalaJSExample$$clear$1__Lorg_scalajs_dom_CanvasRenderingContext2D__V(ctx$1)
};
- ScalaJS.m.Lexample_ScalaJSExample().count$1 = ((ScalaJS.m.Lexample_ScalaJSExample().count$1 + 1) | 0);
- var jsx$3 = ScalaJS.m.Lexample_ScalaJSExample();
- var jsx$2 = ScalaJS.m.Lexample_ScalaJSExample().p$1;
- var jsx$1 = ScalaJS.m.Lexample_ScalaJSExample().corners$1;
- var this$5 = ScalaJS.m.s_util_Random();
- jsx$3.p$1 = jsx$2.$$plus__Lexample_Point__Lexample_Point(ScalaJS.as.Lexample_Point(jsx$1.apply__I__O(this$5.self$1.nextInt__I__I(3)))).$$div__I__Lexample_Point(2);
- var height = (512.0 / ((255 + ScalaJS.m.Lexample_ScalaJSExample().p$1.y$1) | 0));
- var r = ((ScalaJS.m.Lexample_ScalaJSExample().p$1.x$1 * height) | 0);
- var g = ((((255 - ScalaJS.m.Lexample_ScalaJSExample().p$1.x$1) | 0) * height) | 0);
+ count$1.elem$1 = ((1 + count$1.elem$1) | 0);
+ var jsx$1 = ScalaJS.as.Lexample_Point(p$1.elem$1);
+ var this$4 = ScalaJS.m.s_util_Random$();
+ p$1.elem$1 = jsx$1.$$plus__Lexample_Point__Lexample_Point(ScalaJS.as.Lexample_Point(corners$1.apply__I__O(this$4.self$1.nextInt__I__I(3)))).$$div__I__Lexample_Point(2);
+ var height = (512.0 / ((255 + ScalaJS.as.Lexample_Point(p$1.elem$1).y$1) | 0));
+ var r = ((ScalaJS.as.Lexample_Point(p$1.elem$1).x$1 * height) | 0);
+ var g = ((((255 - ScalaJS.as.Lexample_Point(p$1.elem$1).x$1) | 0) * height) | 0);
@p
- As you can see, this code is still very verbose, with lots of unnecessarily long identifiers such as @hl.javascript{Lexample_ScalaJSExample} in it. This is because we've only performed the @sect.ref{Fast Optimization} on this file, to try and keep the time taken to edit -> compile while developing reasonably short.
+ As you can see, this code is still very verbose, with lots of unnecessarily long identifiers such as @hl.javascript{Lexample_ScalaJSExample$} in it. This is because we've only performed the @sect.ref{Fast Optimization} on this file, to try and keep the time taken to edit -> compile while developing reasonably short.
@sect{Optimization}
@p
@@ -251,13 +249,13 @@
@hl.bash
haoyi-mbp:temp haoyi$ du -h target/scala-2.11/example-opt.js
- 144K target/scala-2.11/example-opt.js
+ 104K target/scala-2.11/example-opt.js
@p
- 144 Kilobytes! Better. Not great, though! In general, Scala.js does not produce tiny executables, although the output size of the compiled executables is dropping all the time. If you look inside that file, you'll see all of the long identifiers have been replaced by short ones by the @lnk("Google Closure Compiler", "https://developers.google.com/closure/compiler/").
+ 104 Kilobytes! Better. Not great, though! In general, Scala.js does not produce tiny executables, although the output size of the compiled executables is dropping all the time. If you look inside that file, you'll see all of the long identifiers have been replaced by short ones by the @lnk("Google Closure Compiler", "https://developers.google.com/closure/compiler/").
@hl.javascript("""
- Rb(P(),N(r(L(Ea),["rgb(",", ",", ",")"])))),Sb(P(),r(L(K),[n,s,C])));fe().te.fillRect(fe().Oc.jc,fe().Oc.kc,1,1);e=e+1|0;c=c+k|0}},50))}ae.prototype.Zf=function(){this.te.fillStyle="black";this.te.fillRect(0,0,255,255)};ae.prototype.main=function(){return ee(),void 0};ae.prototype.a=new I({lj:0},!1,"example.ScalaJSExample$",K,{lj:1,b:1});var be=void 0;function fe(){be||(be=(new ae).c());return be}ba.ScalaJSExample=fe;function le(){}le.prototype=new J;function me(){}me.prototype=le.prototype;
+ y=fb(gb((new F).Ya(["rgb(",", ",", ",")"])),(new F).Ya([(255-c.l.Db|0)*y|0,c.l.Db*y|0,c.l.Eb]));a.fillStyle=y;a.fillRect(c.l.Db,c.l.Eb,1,1);w=1+w|0}}}(a,b,c,e),50)}Xa.prototype.main=function(a){Ya(a)};Xa.prototype.a=new x({$g:0},!1,"example.ScalaJSExample$",B,{$g:1,b:1});var hb=void 0;function bb(){hb||(hb=(new Xa).c());return hb}ba.example=ba.example||{};ba.example.ScalaJSExample=bb;function Da(){this.Pb=null}Da.prototype=new A;
""")
@p
@@ -272,9 +270,9 @@
@ul
@li
- A large portion of this 144k is the Scala standard library, and so the size of the compiled blob does not grow that fast as your program grows. For example, while this ~50 line application is 144k, a @lnk("much larger ~2000 line application", "https://github.com/lihaoyi/roll") is only 288k.
+ A large portion of this 104k is the Scala standard library, and so the size of the compiled blob does not grow that fast as your program grows. For example, while this ~50 line application is 104k, a @lnk("much larger ~2000 line application", "https://github.com/lihaoyi/roll") is only 288k.
@li
- This size is pre-@lnk("gzip", "http://en.wikipedia.org/wiki/Gzip"), and most webservers serve their contents compressed via gzip to reduce the download size. Gzip cuts the actual download size down to 43k, which is more acceptable.
+ This size is pre-@lnk("gzip", "http://en.wikipedia.org/wiki/Gzip"), and most webservers serve their contents compressed via gzip to reduce the download size. Gzip cuts the actual download size down to 28k, which is more acceptable.
@li
You will likely have other portions of the page that are of similar size: e.g. @lnk("JQuery", "http://jquery.com/") is extremely popular, and weights in at a comparable 32kb minified and gzipped, while @lnk("React.js", "http://facebook.github.io/react/downloads.html") weighs in at a cool 150kb gzipped. Scala.js arguably provides more than either of these libraries.
diff --git a/book/src/main/scalatex/book/handson/PublishingModules.scalatex b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
index d2fb269..3cc89ff 100644
--- a/book/src/main/scalatex/book/handson/PublishingModules.scalatex
+++ b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
@@ -40,7 +40,7 @@
@hl.ref(wd/'examples/'crossBuilds/'simple/"build.sbt")
@p
- Unlike the equivalent @code{build.sbt} files you saw in earlier chapters, this does not simply add @hl.scala{scalaJSSettings} to the root project. Rather, it sets up two projects: one in the @code{js/} folder and one in the @code{jvm/} folder, with the @code{js/} version getting the settings from the Scala.js plugin. To both of these, we add @code{shared/main/scala} to the list of source directories. This means that both projects will pick up the sources we symlinked between @code{js/shared/} and @code{jvm/shared/}.
+ Unlike the equivalent @code{build.sbt} files you saw in earlier chapters, this does not simply enable the @hl.scala{ScalaJSPlugin} to the root project. Rather, it sets up two projects: one in the @code{js/} folder and one in the @code{jvm/} folder, with the @code{js/} version getting the Scala.js plugin. To both of these, we add @code{shared/main/scala} to the list of source directories. This means that both projects will pick up the sources we symlinked between @code{js/shared/} and @code{jvm/shared/}.
@sect{Source Files}
@val simple = wd/'examples/'crossBuilds/'simple
diff --git a/book/src/main/scalatex/book/handson/WebPage.scalatex b/book/src/main/scalatex/book/handson/WebPage.scalatex
index 1078d7d..03ed452 100644
--- a/book/src/main/scalatex/book/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/book/handson/WebPage.scalatex
@@ -18,10 +18,10 @@
@hl.ref(webpage/"HelloWorld0.scala")
@less
- @BookData.example(div, "HelloWorld0().main")
+ @BookData.example(div, "webpage.HelloWorld0().main")
@p
- Remember that we're now requiring a @hl.scala{dom.HTMLDivElement} instead of a @hl.scala{dom.HTMLCanvsElement} to be passed in when the Javascript calls @hl.javascript{HelloWorld0().main(...)}. If you're coming to this point from the previous chapter, you'll need to update the on-page Javascript's @hl.javascript{document.getElementById} to pick a @hl.html{<div>} rather than the @hl.html{<canvas>} we were using in the previous chapter.
+ Remember that we're now requiring a @hl.scala{dom.HTMLDivElement} instead of a @hl.scala{dom.HTMLCanvasElement} to be passed in when the Javascript calls @hl.javascript{webpage.HelloWorld0().main(...)}. If you're coming to this point from the previous chapter, you'll need to update the on-page Javascript's @hl.javascript{document.getElementById} to pick a @hl.html{<div>} rather than the @hl.html{<canvas>} we were using in the previous chapter.
@p
This approach works, as the above example shows, but has a couple of disadvantages:
@@ -39,7 +39,7 @@
@p
@lnk("Scalatags", "https://github.com/lihaoyi/scalatags") is a cross-platform Scala.js/Scala-JVM library that is designed to generate HTML. To use Scalatags, you need to add it as a dependency to your Scala.js SBT project, in the @code{build.sbt} file:
- @hl.ref(wd/'examples/'demos/"build.sbt", "com.scalatags", "")
+ @hl.ref(wd/'examples/'demos/"build.sbt", "scalatags", "")
@p
With that, the above snippet of code re-written using Scalatags looks as follows:
@@ -49,7 +49,7 @@
@hl.ref(webpage/"HelloWorld1.scala")
@less
- @BookData.example(div, "HelloWorld1().main")
+ @BookData.example(div, "webpage.HelloWorld1().main")
@p
Scalatags has some nice advantages over plain HTML: it's type-safe, so typos like @hl.scala{dvi} get caught at compile-time. It's also secure, such that you don't need to worry about script-tags in strings or similar. The @lnk("Scalatags Readme", "https://github.com/lihaoyi/scalatags#scalatags") elaborates on these points and other advantages. As you can see, it takes just 1 import at the top of the file to bring it in scope, and then you can use all of Scalatags' functionality.
@@ -63,7 +63,7 @@
@hl.ref(webpage/"Inputs.scala", "val box")
@less
- @BookData.example(div(height:="150px"), "Inputs().main")
+ @BookData.example(div(height:="150px"), "webpage.Inputs().main")
@p
In Scalatags, you build up fragments of type @hl.scala{Frag} using functions like @hl.scala{div}, @hl.scala{h1}, etc., and call @hl.scala{.render} on it to turn it into a real @lnk.dom.Element. Different fragments render to different things: e.g. @hl.scala{input.render} gives you a @lnk.dom.HTMLInputElement, @hl.scala{span.render} gives you a @lnk.dom.HTMLSpanElement. You can then access the properties of these elements: adding callbacks, checking their value, anything you want.
@@ -96,17 +96,17 @@
@hl.ref(webpage/"Search0.scala", "val output")
@less
- @BookData.example(div, "Search0().main")
+ @BookData.example(div, "webpage.Search0().main")
@p
- And there you have it! A working search box. This is a relatively self-contained example: all the items its searching are available locally, no Ajax calls, and there's no fancy handling of the searched items. If we want to, for example, highlght the matched section of each fruit's name, we can modify the @hl.scala{def renderListings} call to do so:
+ And there you have it! A working search box. This is a relatively self-contained example: all the items its searching are available locally, no Ajax calls, and there's no fancy handling of the searched items. If we want to, for example, highlight the matched section of each fruit's name, we can modify the @hl.scala{def renderListings} call to do so:
@split
@more
@hl.ref(webpage/"Search1.scala", "def renderListings", "lazy val")
@less
- @BookData.example(div, "Search1().main")
+ @BookData.example(div, "webpage.Search1().main")
@p
Here, instead of sticking the name of the matched fruits directly into the @hl.scala{li}, we instead first split off the part which matches the query, and then highlght the first section yellow. Easy!
@@ -138,7 +138,7 @@
@hl.ref(webpage/"Weather0.scala", "val xhr")
@less
- @BookData.example(div(height:="400px"), "Weather0().main")
+ @BookData.example(div(height:="400px"), "webpage.Weather0().main")
@p
The above snippet of code uses the raw Javascript Ajax API in order to make a request to @lnk("openweathermap.org", "http://openweathermap.org/"), to get the weather data for the city of Singapore as a JSON blob. The part of the API that we'll be using is documented @lnk("here", "http://openweathermap.org/current"), and if you're interested you can read all about the various options that they provide. For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
@@ -186,7 +186,7 @@
@hl.ref(webpage/"Weather1.scala", "val url")
@less
- @BookData.example(div(height:="400px", overflow:="scroll"), "Weather1().main")
+ @BookData.example(div(height:="400px", overflow:="scroll"), "webpage.Weather1().main")
@p
A single call to @hl.scala{Ajax.get(...)}, with the URL, and we receive a @hl.scala{scala.concurrent.Future} that we can use to get access to the result when ready. Here we're just using it's @hl.scala{onSuccess}, but we could use it in a for-comprehension, with @lnk("Scala Async", "https://github.com/scala/async"), or however else we can use normal @hl.scala{Future}s
@@ -203,7 +203,7 @@
@hl.ref(webpage/"Weather2.scala", "Ajax.get")
@less
- @BookData.example(div(height:="400px"), "Weather2().main")
+ @BookData.example(div(height:="400px"), "webpage.Weather2().main")
@p
We do this by taking @hl.scala{xhr.responseText} and putting it through both @hl.scala{JSON.parse} and @hl.scala{JSON.stringify}, passing in a @hl.scala{space} argument to tell @hl.scala{JSON.stringify} to spread it out nicely.
@@ -216,7 +216,7 @@
@hl.ref(webpage/"Weather3.scala", "Ajax.get")
@less
- @BookData.example(div(height:="400px", overflow:="scroll"), "Weather3().main")
+ @BookData.example(div(height:="400px", overflow:="scroll"), "webpage.Weather3().main")
@p
First we parse the incoming response, extract a bunch of values from it, and then stick it in a Scalatags fragment for us to see. Note how we can use the names of the attributes e.g. @hl.scala{json.name} even though @hl.scala{name} is a dynamic property which you can't be sure exists: this is because @hl.scala{json} is of type @hl.scala{js.Dynamic}, which allows us to refer to arbitrary parameters and methods on the underlying object without type-checking.
@@ -246,7 +246,7 @@
@p
Here is the meat and potatoes of this program: every time it gets called with an array of weather-data, we iterate over the cities in that array. It then does a similar sort of data-extraction that we did earlier, putting the results into the @hl.scala{output} div we defined above, including highlighting.
- @BookData.example(div, "WeatherSearch().main")
+ @BookData.example(div, "webpage.WeatherSearch().main")
@p
And that's the working example! Try searching for cities like "Singapore" or "New York" or "San Francisco" and watch as the search narrows as you enter more characters into the text box. Note that the OpenWeatherMap API limits ambiguous searches to about a dozen results, so if a city doesn't turn up in a partial-search, try entering more characters to narrow it down.
diff --git a/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex b/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex
index 098da08..0b84cfe 100644
--- a/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex
+++ b/book/src/main/scalatex/book/indepth/AdvancedTechniques.scalatex
@@ -45,7 +45,7 @@
@p
@lnk("Scala.Rx", "https://github.com/lihaoyi/scala.rx") is a change-propagation library that implements the @b{Continuous} style of FRP. To begin with, we need to include it in our @code{build.sbt} dependencies:
- @hl.ref(wd/'examples/'demos/"build.sbt", "com.scalarx")
+ @hl.ref(wd/'examples/'demos/"build.sbt", "scalarx", "")
@p
Scala.Rx provides you with smart variables that automatically track dependencies with each other, such that if one smart variable changes, the rest re-compute immediately and automatically. The main primitives in Scala.Rx are:
@@ -85,7 +85,7 @@
@hl.ref(advanced/"BasicRx.scala", "val txt =")
@less
- @example(div, "BasicRx().main")
+ @example(div, "advanced.BasicRx().main")
@p
This snippet sets up a basic data-flow graph. We have our @hl.scala{txt} @hl.scala{Var}, and a bunch of @hl.scala{Rx}s (@hl.scala{numChars}, @hl.scala{numWords}, @hl.scala{avgWordLength}) that are computed based on @hl.scala{txt}.
@@ -109,7 +109,7 @@
@hl.ref(advanced/"BasicRx.scala", "val fruits =")
@less
- @example(div, "BasicRx().main2")
+ @example(div, "advanced.BasicRx().main2")
@p
This is a basic re-implementation of the autocomplete widget we created in the chapter @sect.ref{Interactive Web Pages}, except done using Scala.Rx. Note that unlike the original implementation, we don't need to manage the clearing of the output area via @hl.scala{innerHTML = ""} and the re-rendering via @hl.scala{appendChild(...)}. All this is handled by the same @hl.scala{rxFrag} code we wrote earlier.
@@ -182,7 +182,7 @@
@def exampleDiv = div(height:="200px")
@sect{Direct Use of XMLHttpRequest}
- @example(exampleDiv, "Futures().main0")
+ @example(exampleDiv, "advanced.Futures().main0")
@hl.ref(advanced/"Futures.scala", "def handle0", "main")
@p
@@ -194,7 +194,7 @@
This solution is basically equivalent to the initial code given in the @sect.ref{Raw Javascript} section of @sect.ref{Interactive Web Pages}, with the additional code necessary for aggregation. As described in @sect.ref{dom.extensions}, we can make use of the @hl.scala{Ajax} object to make it slightly tidier.
@sect{Using dom.extensions.Ajax}
- @example(exampleDiv, "Futures().main1")
+ @example(exampleDiv, "advanced.Futures().main1")
@hl.ref(advanced/"Futures.scala", "def handle1", "main")
@p
@@ -203,7 +203,7 @@
However, we still have the messiness inherent in the result aggregation: we don't actually want to perform our action (writing to the @hl.scala{output} div) when one @hl.scala{Future} is complete, but only when @i{all} the @hl.scala{Future}s are complete. Thus we still need to do some amount of manual book-keeping in the @hl.scala{results} buffer.
@sect{Future Combinators}
- @example(exampleDiv, "Futures().main2")
+ @example(exampleDiv, "advanced.Futures().main2")
@hl.ref(advanced/"Futures.scala", "def handle2", "main")
@p
@@ -250,7 +250,7 @@
@hl.ref(advanced/"Async.scala", "// traditional")
@less
- @example(canvas, "Async().main0")
+ @example(canvas, "advanced.Async().main0")
@p
This is a working implementation, and you can play with it on the right. We basically set the three listeners:
@@ -283,7 +283,7 @@
@hl.ref(advanced/"Async.scala", "// async")
@less
- @example(canvas, "Async().main")
+ @example(canvas, "advanced.Async().main")
@p
We have an @hl.scala{async} block, which contains a while loop. Each round around the loop, we wait for the @hl.scala{mousedown} channel to start the path, waiting for either @hl.scala{mousemove} or @hl.scala{mouseup} (which continues the path or ends it respectively), fill the shape, and then wait for another @hl.scala{mousedown} before clearing the canvas and going again.
diff --git a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
index cb55bf9..5d91b62 100644
--- a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
+++ b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
@@ -27,14 +27,14 @@
@li
@b{Initial Compilation}: @code{.scala} files to @code{.class} and @code{.sjsir} files
@li
- @b{Fast Optimization}: @code{.sjsir} files to smallish/fast @code{.js} files
+ @b{Fast Optimization}: @code{.sjsir} files to one smallish/fast @code{.js} file, or
@li
- @b{Full Optimization}: @code{.js} files to smaller/faster @code{.js} files
+ @b{Full Optimization}: @code{.sjsir} files to one smaller/faster @code{.js} file
@p
- @code{.scala} files are the source code you're familiar with. @code{.class} files are the JVM-targetted artifacts which aren't used, but we keep around: tools such as @lnk.misc.IntelliJ or @lnk.misc.Eclipse use these files to provide IDE support for Scala.js code, even if they take no part in compilation. @code{.js} files are the output Javascript, which we can execute in a web browser.
+ @code{.scala} files are the source code you're familiar with. @code{.class} files are the JVM-targetted artifacts which aren't used for actually producing @code{.js} files, but are kept around for pretty much everything else: the compiler uses them for separate compilation and macros, and tools such as @lnk.misc.IntelliJ or @lnk.misc.Eclipse use these files to provide IDE support for Scala.js code. @code{.js} files are the output Javascript, which we can execute in a web browser.
@p
- @code{.sjsir} files are worth calling out: the name stands for "ScalaJS Intermediate Representation", and these files contain compiled code half-way between Scala and Javascript: most Scala features have by this point been replaced their Java/Javascript equivalents, but it still contains Types (which have all been inferred) that can aid in analysis. Many Scala.js specific optimizations take place on this IR.
+ @code{.sjsir} files are worth calling out: the name stands for "ScalaJS Intermediate Representation", and these files contain compiled code half-way between Scala and Javascript: most Scala features have by this point been replaced by their Java/Javascript equivalents, but it still contains Types (which have all been inferred) that can aid in analysis. Many Scala.js specific optimizations take place on this IR.
@p
Each stage has a purpose, and together the stages do bring benefits to offset their cost in complexity. The original compilation pipeline was much more simple:
@@ -83,7 +83,7 @@
@ul
@li
- The original @code{.class} files, as if it were compiled on the JVM. For code that does not use any Javascript interop, these are perfectly valid Java @code{.class} files full of bytecode and can even be executed on the JVM.
+ The original @code{.class} files, @i{almost} as if they were compiled on the JVM, but not quite. They are sufficiently valid that the compiler can execute macros defined in them, but they should not be used to actually run.
@li
The @code{.sjsir} files, destined for further compilation in the Scala.js pipeline.
@@ -94,25 +94,25 @@
This is the only phase in the Scala.js compilation pipeline that separate compilation is possible: you can compile many different sets of Scala.js @code{.scala} files separately, only to combine them later. This is used e.g. for distributing Scala.js libraries as Maven Jars, which are compiled separately by library authors to be combined into a final executable later.
@sect{Fast Optimization}
+ @p
+ Without optimizations, the actual JavaScript code emitted for the above snippet would look like this:
@hl.javascript
- ScalaJS.c.LExample$.prototype.main__V = (function() {
+ ScalaJS.c.Lexample_ScalaJSExample$.prototype.main__V = (function() {
var x = 0;
while ((x < 999)) {
x = ((x + new ScalaJS.c.sci_StringOps().init___T(
- ScalaJS.m.s_Predef().augmentString__T__T("2")
- ).toInt__I()) | 0)
+ ScalaJS.m.s_Predef$().augmentString__T__T("2")).toInt__I()) | 0)
};
- ScalaJS.m.s_Predef().println__O__V(x)
+ ScalaJS.m.s_Predef$().println__O__V(x)
});
-
@p
- This phase is a whole-program optimization of the @code{.sjsir} files, and lives in the @lnk("tools/", "https://github.com/scala-js/scala-js/tree/master/tools") folder of the Scala.js repository. The end result is a rough translation of Scala into the equivalent Javascript (e.g. above):
+ This is a pretty straightforward translation from the intermediate reprensentation into vanilla JavaScript code:
@ul
@li
Scala-style method @hl.scala{def}s become Javascript-style prototype-function-assignment
@li
- Scala @hl.scala{var}s become Javascript @hl.scala{var}s
+ Scala @hl.scala{val}s and @hl.scala{var}s become Javascript @hl.scala{var}s
@li
Scala @hl.scala{while}s become Javascript @hl.scala{while}s
@li
@@ -126,32 +126,57 @@
This is an incomplete description of the translation, but it should give a good sense of how the translation from Scala to Javascript looks like. In general, the output is verbose but straightforward.
@p
- In addition to this superficial translation, the optimizer does a number of things which are more subtle and vary from case to case. The rough operations that get performed are:
+ In addition to this superficial translation, the optimizer does a number of things which are more subtle and vary from case to case. Without diving into too much detail, here are a few optimizations that are performed:
@ul
@li
- @b{Dead-code elimination}: entry-points to the program such as @hl.scala("@JSExport")ed methods/classes are kept, as are any methods/classes that these reference. All others are removed. This reduces the potentially 20mb of Javascript generated by a naive compilation to a more manageable 700kb-1mb for a typical application
+ @b{Dead-code elimination}: entry-points to the program such as @hl.scala("@JSExport")ed methods/classes are kept, as are any methods/classes that these reference. All others are removed. This reduces the potentially 20mb of Javascript generated by a naive compilation to a more manageable 400kb-1mb for a typical application
+ @li
+ @b{Inlining}: under some circumstances, the optimizer inlines the implementation of methods at call sites. For example, it does so for all "small enough" methods. This typically reduces the code size by a small amount, but offers a several-times speedup of the generated code by inlining away much of the overhead from the abstractions (implicit-conversions, higher-order-functions, etc.) in Scala's standard library.
+ @li
+ @b{Constant-folding}: due to inlining and other optimizations, some variables that could have arbitrary are known to contain a constant. These variables are replaced by their respective constants, which, in turn, can trigger more optimizations.
@li
- @b{Inlining}: in cases where a method is only called in a few places, the optimizer inlines the implementation of the method at those callsites. This typically reduces the code size by a small amount, but offers a several-times speedup of the generated code by inlining away much of the overhead from the abstractions (implicit-conversions, higher-order-functions, etc.) in Scala's standard library.
+ @b{Closure elimination}: probably one of the most important optimizations. When inlining a higher-order method such as @code{map}, the optimizer can in turn inline the anonymous function inside the body of the loop, effectively turning polymorphic dispatch with closures into bare-metal loops.
+ @p
+ Applying these optimizations on our examples results in the following JavaScript code instead, which is what you typically execute in fastOpt stage:
+
+ @hl.javascript
+ ScalaJS.c.Lexample_ScalaJSExample$.prototype.main__V = (function() {
+ var x = 0;
+ while ((x < 999)) {
+ var jsx$1 = x;
+ var this$2 = new ScalaJS.c.sci_StringOps().init___T("2");
+ var this$4 = ScalaJS.m.jl_Integer$();
+ var s = this$2.repr$1;
+ x = ((jsx$1 + this$4.parseInt__T__I__I(s, 10)) | 0)
+ };
+ var x$1 = x;
+ var this$6 = ScalaJS.m.s_Console$();
+ var this$7 = this$6.outVar$2;
+ ScalaJS.as.Ljava_io_PrintStream(this$7.tl$1.get__O()).println__O__V(x$1)
+ });
+
@p
As a whole-program optimization, it tightly ties together the code it is compiling and does not let you e.g. inject additional classes later. This does not mean you cannot interact with external code at all: you can, but it has to go through explicitly @hl.scala{@@JSExport}ed methods and classes via Javascript Interop, and not on ad-hoc classes/methods within the module. Thus it's entirely possible to have multiple "whole-programs" running in the same browser; they just will likely have duplicate copies of e.g. standard library classes inside of them, since they cannot share the code as it's not exported.
@p
- While the input for this phase is the aggregate @code{.sjsir} files from your project and all your dependencies, the output is executable Javascript. This phase usually runs in less than a second, outputs a Javascript blob in the 700kb-1mb range, and is suitable for repeated use during development. This corresponds to the @code{fastOptJS} command in SBT.
+ While the input for this phase is the aggregate @code{.sjsir} files from your project and all your dependencies, the output is executable Javascript. This phase usually runs in less than a second, outputs a Javascript blob in the 400kb-1mb range, and is suitable for repeated use during development. This corresponds to the @code{fastOptJS} command in SBT.
@sect{Full Optimization}
@hl.javascript
- be.prototype.main=function(){
- for(var a=0;999>a;)
- a=a+(new de).g(S(L(),"2")).ne()|0;
- ee(); L();
- var b=F(fe); ge();
- a=(new he).g(w(a)); b=bc(0,J(q(b,[a])));
- ie(bc(L(),J(q(F(fe),[je(ke(ge().Vg),b)]))))
- }
+ Fd.prototype.main = function() {
+ for(var a = 0;999 > a;) {
+ var b = (new D).j("2");
+ E();
+ a = a + Ja(0, b.R) | 0
+ }
+ b = Xa(ed().pc.Sb);
+ fd(b, gd(s(), a));
+ fd(b, "\n");
+ };
@p
- The @lnk("Google Closure Compiler", "https://developers.google.com/closure/compiler/") (GCC) is a set of tools that work with Javascript. It has multiple @lnk("levels of optimization", "https://developers.google.com/closure/compiler/docs/compilation_levels"), doing everything from basic whitespace-removal to heavy optimization. It is a old, relatively mature project that is relied on both inside and outside google to optimize the delivery of Javascript to the browser.
+ The @lnk("Google Closure Compiler", "https://developers.google.com/closure/compiler/") (GCC) is a set of tools that work with Javascript. It has multiple @lnk("levels of optimization", "https://developers.google.com/closure/compiler/docs/compilation_levels"), doing everything from basic whitespace-removal to heavy optimization. It is an old, relatively mature project that is relied on both inside and outside Google to optimize the delivery of Javascript to the browser.
@p
Scala.js uses GCC in its most aggressive mode: @lnk("Advanced Optimization", "https://developers.google.com/closure/compiler/docs/api-tutorial3"). GCC spits out a compressed, minified version of the Javascript (above) that @sect.ref{Fast Optimization} spits out: e.g. in the example above, all identifiers have been renamed to short strings, the @hl.javascript{while}-loop has been replaced by a @hl.javascript{for}-loop, and the @hl.scala{println} function has been inlined.
@@ -172,10 +197,9 @@
@p
Notably, GCC @i{does not preserve the semantics of arbitrary Javascript}! In particular, it only works for a subset of Javascript that it understands and can properly analyze. This is an issue when hand-writing Javascript for GCC since it's very easy to step outside that subset and have GCC break your code, but is not a worry when using Scala.js: the Scala.js optimizer (the previous phase in the pipeline) automatically outputs Javascript which GCC understands and can work with.
@p
- GCC duplicates a lot of functionality that the Scala.js optimizer already does, such as DCE and inlining. It is entirely possible to skip the optimization phase, output the naive 20mb Javascript blob, and run GCC on it to bring the size down. However, GCC is much slower than the Scala.js optimizer, taking 60 seconds where the optimizer takes less than 1.
-
+ There is some overlap between the optimizations performed by the Scala.js optimizer and GCC. For example, both apply DCE and inlining in some form. However, there are also a lot of optimizations specific to each tool. In general, the Scala.js optimizer is more concerned about producing very efficient JavaScript code, while GCC shines at making that JavaScript as small as possible (in terms of the number of characters).
@p
- Empirically, running GCC on the output of the optimizer produces the smallest output blobs: ~150-400kb, significantly smaller than the output of running either of them alone, and so that is what we do. This takes 5-10 seconds to run, which makes it somewhat slow for iterative development, so it's typically only run right before final testing and deployment. This corresponds to the @code{fullOptJS} command in SBT.
+ The combination of both these tools produces small and fast output blobs: ~100-400kb. This takes 5-10 seconds to run, which makes it somewhat slow for iterative development, so it's typically only run right before final testing and deployment. This corresponds to the @code{fullOptJS} command in SBT.
@hr
diff --git a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
index f167ea6..594d8c8 100644
--- a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
+++ b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
@@ -151,7 +151,7 @@
(15 / 4) | 0 // 3
@p
- This gives the correct result for most numbers, and is reasonably efficient. However, what about dividing-by-zero?
+ This gives the correct result for most numbers, and is reasonably efficient (actually, it tends to be @i{more} efficient on modern VMs). However, what about dividing-by-zero?
@hl.scala
/*JVM*/
@@ -185,7 +185,7 @@
@p
The decision to not support these exceptional cases comes down to a value judgement: how often do people actually depend on an exception being thrown as part of their program semantics, e.g. by catching it and performing actions? And how often are they just a way of indicating bugs? It turns out that very few @hl.scala{ArithmeticException}s, @hl.scala{ArrayIndexOutOfBoundsException}s, or similar are actually a necessary part of the program! They exist during debugging, but after that, these code paths are never relied upon "in production".
@p
- Thus Scala.js goes for a compromise: in the Fast Optimization mode, we run the code with all these checks in place, so as to catch cases where these errors occur close-to-the-source and make it easy for you to debug them. In Full Optimization mode, on the other hand, we remove these checks, assuming you've already ran through these cases and found any bugs during development.
+ Thus Scala.js goes for a compromise: in the Fast Optimization mode, we run the code with all these checks in place (this is work in progress; currently only @code{asInstanceOf}s are thus checked), so as to catch cases where these errors occur close-to-the-source and make it easy for you to debug them. In Full Optimization mode, on the other hand, we remove these checks, assuming you've already ran through these cases and found any bugs during development.
@p
This is a common pattern in situations where there's a tradeoff between debuggability and speed. In Scala.js' case, it allows us to get good debuggability in development, as well as good performance in production. There's some loss in debuggability in development, sacrificed in exchange for greater performance.
diff --git a/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex b/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
index 89389dc..1a0afa5 100644
--- a/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
+++ b/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
@@ -21,7 +21,7 @@
@li
Write a clean-room implementation in Scala, without looking at the source code of @lnk("OpenJDK", "http://openjdk.java.net/"). This is due to legal-software-license incompatibility between OpenJDK and Scala.js. Reading the docs or specification are fine, as is looking at the source of alternate implementations such as @lnk("Harmony", "http://harmony.apache.org/")
@li
- Submit a pull-request to the @lnk("Scala.js repository", "https://github.com/scala-js/scala-js"), including your implementation, together with tests. See the @lnk("existing tests", "https://github.com/scala-js/scala-js/tree/master/test-suite/src/test/scala/scala/scalajs/testsuite/javalib") in the repository if you need examples of how to write your own.
+ Submit a pull-request to the @lnk("Scala.js repository", "https://github.com/scala-js/scala-js"), including your implementation, together with tests. See the @lnk("existing tests", "https://github.com/scala-js/scala-js/tree/master/test-suite/src/test/scala/org/scalajs/testsuite/javalib") in the repository if you need examples of how to write your own.
@p
In general, this is a simple process, for "pure-Java" classes which do not use any special JVM/Java-specific APIs. However, this will not be possible for classes which do! This means that classes that make use of Java-specific things like:
diff --git a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
index 96f3929..c87c60f 100644
--- a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
+++ b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
@@ -3,130 +3,150 @@
Although Scala.js tries very hard to maintain compatibility with Scala-JVM, there are some parts where the two platforms differs. This can be roughly grouped into two things: differences in the libraries available, and differences in the language itself. This chapter will cover both of these facets.
@sect{Language Differences}
- @sect{Floats are Doubles}
- @p
- Since JavaScript doesn't have a native float type, we sometimes represent Floats using doubles/numbers, rather than with lower-precision 32-bit floats.
- @p
- The choice of how to represent floats is up to the implementation. You may not rely on floats providing 64-bit floating point precision.
+
+ @sect{Primitive data types}
@p
- Float literals are truncated to their (binary) precision. However, output does not truncate to that precision. This can lead to the following behavior (this works as expected when using doubles):
+ All primitive data types work exactly as on the JVM, with the three following
+ exceptions.
- @split
- @half
+ @sect{Floats can behave as Doubles by default}
+ @p
+ Scala.js underspecifies the behavior of @code{Float}s by default. Any @code{Float} value can be stored as a @code{Double} instead, and any operation on @code{Float}s can be computed with double precision. The choice of whether or not to behave as such, when and where, is left to the
+ implementation.
+ @p
+ If exact single precision operations are important to your application, you can enable strict-floats semantics in Scala.js, with the following sbt setting:
@hl.scala
- // Scala-JVM
- > println(13.345f)
- 13.345
-
- @half
+ scalaJSSemantics ~= { _.withStrictFloats(true) }
+ @p
+ Note that this can have a major impact on performance of your application on JS interpreters that do not support @lnk("the Math.fround function", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround").
+
+ @sect{toString of Float, Double and Unit}
+ @p
+ @code{x.toString()} returns slightly different results for floating point numbers and @code{()} (@code{Unit}).
+
+ @split
+ @half
+ @hl.scala
+ // Scala-JVM
+ > println(())
+ ()
+ > println(1.0)
+ 1.0
+ > println(1.4f)
+ 1.4
+
+ @half
+ @hl.scala
+ // Scala.js
+ > println(())
+ undefined
+ > println(1.0)
+ 1
+ > println(1.4f)
+ 1.399999976158142
+
+ @p
+ In general, a trailing @code{.0} is omitted. Floats print in a weird way because they are printed as if they were Doubles, which means their lack of precision shows up.
+ @p
+ To get sensible and portable string representation of floating point numbers, use @code{String.format()} or related methods.
+
+ @sect{Runtime type tests are based on values}
+ @p
+ Instance tests (and consequently pattern matching) on any of @code{Byte}, @code{Short}, @code{Int}, @code{Float}, @code{Double} are based on the value and not the type they were created with. The following are examples:
+ @ul
+ @li
+ 1 matches @code{Byte}, @code{Short}, @code{Int}, @code{Float}, @code{Double}
+ @li
+ 128 (@code{> Byte.MaxValue}) matches @code{Short}, @code{Int}, @code{Float}, @code{Double}
+ @li
+ 32768 (@code{> Short.MaxValue}) matches @code{Int}, @code{Float}, @code{Double}
+ @li
+ 2147483647 matches @code{Int}, @code{Double} if strict-floats are enabled, otherwise @code{Float} as well
+ @li
+ 2147483648 (@code{> Int.MaxValue}) matches @code{Float}, @code{Double}
+ @li
+ 1.5 matches @code{Float}, @code{Double}
+ @li
+ 1.4 matches @code{Double} only if strict-floats are enabled, otherwise @code{Float} and @code{Double}
+ @li
+ @code{NaN}, @code{Infinity}, @code{-Infinity} and @code{-0.0} match @code{Float}, @code{Double}
+ @p
+ As a consequence, the following apparent subtyping relationships hold:
@hl.scala
- // Scala.js
- > println(13.345f)
- 13.345000267028809
-
- @sect{Int division by 0 is undefined}
- @p
- Unlike the JVM where dividing an integer type by 0 throws an exception, in Scala.js integer division by 0 is undefined. This allows for efficient implementation of division. Dividing a Double or Float by 0 yields positive or negative infinity as expected.
-
- @split
- @half
- @hl.scala
- // Scala-JVM
- > 10 / 0
- java.lang.ArithmeticException: / by zero
- @half
- @hl.scala
- // Scala.js
- > 10 / 0
- 0
-
- @p
- This is a consequence of the eternal trade-off between performance and correctness, as described in the section @sect.ref{Why does error behavior differ?}
+ Byte <:< Short <:< Int <:< Double
+ <:< Float <:<
+ @p
+ if strict-floats are enabled, or
+ @hl.scala
+ Byte <:< Short <:< Int <:< Float =:= Double
+ @p
+ otherwise.
- @sect{Primitive isInstanceOf tests are based on value}
+ @sect{Undefined behaviors}
@p
- Instance tests (and consequently pattern matching) on any of @hl.scala{Byte}, @hl.scala{Short}, @hl.scala{Int}, @hl.scala{Float}, @hl.scala{Double} are based on the value and not the type they were created with. The following are examples:
-
+ The JVM is a very well specified environment, which even specifies how some bugs are reported as exceptions. Some examples are:
@ul
@li
- @hl.scala{1} matches @hl.scala{Byte}, @hl.scala{Short}, @hl.scala{Int}, @hl.scala{Float}, @hl.scala{Double}
+ @code{NullPointerException}
@li
- @hl.scala{128} (> @hl.scala{Byte.MaxValue}) matches @hl.scala{Short}, @hl.scala{Int}, @hl.scala{Float}, @hl.scala{Double}
+ @code{ArrayIndexOutOfBoundsException} and @code{StringIndexOutOfBoundsException}
@li
- @hl.scala{32768} (> @hl.scala{Short.MaxValue}) matches @hl.scala{Int}, @hl.scala{Float}, @hl.scala{Double}
+ @code{ClassCastException}
@li
- @hl.scala{2147483648} (> @hl.scala{Int.MaxValue}) matches @hl.scala{Float}, @hl.scala{Double}
+ @code{ArithmeticException} (such as integer division by 0)
@li
- @hl.scala{1.2} matches @hl.scala{Float}, @hl.scala{Double}
+ @code{StackOverflowError} and other @code{VirtualMachineError}s
@p
- As a consequence, the following apparent subtyping relationship holds:
-
- @hl.scala
- Byte <:< Short <:< Int <:< Float =:= Double
-
- @sect{toString for integral Floats and Doubles}
+ Because Scala.js does not receive VM support to detect such erroneous conditions, checking them is typically too expensive.
@p
- Calling toString on a Float or a Double that holds an integral value, will not append ".0" to that value:
-
- @split
- @half
- @hl.scala
- // Scala-JVM:
- > println(1.0)
- 1.0
- @half
- @hl.scala
- // Scala.js:
- > println(1.0)
- 1
+ Therefore, all of these are considered @lnk("undefined behavior", "http://en.wikipedia.org/wiki/Undefined_behavior").
@p
- This is due to how numeric values are represented at runtime in Scala.js: @hl.scala{Float}s and @hl.scala{Double}s are raw Javascript @hl.scala{Number}s, and their @hl.scala{toString} behavior follows from that.
-
+ Some of these, however, can be configured to be compliant with sbt settings. Currently, only @code{ClassCastException}s (thrown by invalid @code{asInstanceOf} calls) are configurable, but the list will probably expand in future versions.
@p
- Use a formatting interpolator if you always want to show decimals:
-
+ Every configurable undefined behavior has 3 possible modes:
+ @ul
+ @li
+ @b{Compliant}: behaves as specified on a JVM
+ @li
+ @b{Unchecked}: completely unchecked and undefined
+ @li
+ @b{Fatal}: checked, but throws @lnk("UndefinedBehaviorError", "http://www.scala-js.org/api/scalajs-library/0.6.0/#scala.scalajs.runtime.UndefinedBehaviorError")s instead of the specified exception.
+ @p
+ By default, undefined behaviors are in Fatal mode for fastOptJS and in Unchecked mode for fullOptJS. This is so that bugs can be detected more easily during development, with predictable exceptions and stack traces. In production code (fullOptJS), the checks are removed for maximum efficiency.
+ @p
+ @code{UndefinedBehaviorError}s are @i{fatal} in the sense that they are not matched by @code{case NonFatal(e)} handlers. This makes sure that they always crash your program as early as possible, so that you can detect and fix the bug. It is @i{never} OK to catch an @code{UndefinedBehaviorError} (other than in a testing framework), since that means your program will behave differently in fullOpt stage than in fastOpt.
+ @p
+ If you need a particular kind of exception to be thrown in compliance with the JVM semantics, you can do so with an sbt setting. For example, this setting enables compliant @code{asInstanceOf}s:
@hl.scala
- val x = 1.0
- println(f"$x%.1f")
- // Scala-JVM: 1.0
- // Scala.js: 1.0
-
- @sect{Unit}
+ scalaJSSemantics ~= { _.withAsInstanceOfs(
+ org.scalajs.core.tools.sem.CheckedBehavior.Compliant) }
@p
- @hl.scala{scala.Unit} is represented using JavaScript's undefined. Therefore, calling @hl.scala{toString()} on @hl.scala{Unit} will return @hl.scala{"undefined"} rather than @hl.scala{"()"}. In practice, this shouldn't matter for most use cases.
-
- @sect{Reflection}
+ Note that this will have (potentially major) performance impacts.
@p
- Java reflection and Scala reflection, are not supported. There is limited support for @lnk("java.lang.Class", "https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html"), e.g., @hl.scala{obj.getClass.getName} will work for any Scala.js object but not for objects that come from JavaScript interop (i.e. anything which @hl.scala{extends js.Object}). Reflection makes it difficult to perform the optimizations that Scala.js heavily relies on. For a more detailed discussion on this topic, take a look at the section @sect.ref{Why No Reflection?}.
+ For a more detailed rationale, see the section @sect.ref{Why does error behavior differ?}.
- @sect{Exceptions}
+ @sect{Reflection}
@p
- In general, Scala.js supports exceptions, including catching them based on their type. However, exceptions that are typically triggered by the JVM have flaky semantics, in particular:
-
- @ul
- @li
- @hl.scala{ArrayIndexOutOfBoundsException} is never thrown. Instead, the value becomes @hl.javascript{undefined}, which will probably propagate through your program and blow up somewhat later.
- @li
- @hl.scala{NullPointerException} is reported as JavaScript @hl.scala{TypeError} instead.
- @li
- @hl.scala{StackOverflowError} is unsupported since the underlying JavaScript exception type varies based on the browser. e.g. in Chrome the browser just hangs and kills the process/tab without any chance for the developer to catch the error.
+ Java reflection and, a fortiori, Scala reflection, are not supported. There is limited support for @code{java.lang.Class}, e.g., @code{obj.getClass.getName} will work for any Scala.js object (not for objects that come from JavaScript interop). Reflection makes it difficult to perform the optimizations that Scala.js heavily relies on. For a more detailed discussion on this topic, take a look at the section @sect.ref{Why No Reflection?}.
@sect{Regular expressions}
@p
- JavaScript regular expressions are slightly different from Java regular expressions. The support for regular expressions in Scala.js is implemented on top of JavaScript regexes.
+ @lnk("JavaScript regular expressions", "http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Regular_Expressions") are slightly different from @lnk("Java regular expressions", "http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html"). The support for regular expressions in Scala.js is implemented on top of JavaScript regexes.
@p
- This sometimes has an impact on functions in the Scala library that use regular expressions themselves. A list of known functions that are affected is given here:
+ This sometimes has an impact on functions in the Scala library that use regular expressions themselves. A list of known functions that are
+ affected is given here:
@ul
@li
- @hl.scala{StringLike.split(x: Array[Char])}
+ @code{StringLike.split(x: Array[Char])}
@sect{Symbols}
@p
- @hl.scala{scala.Symbol} is supported, but is a potential source of memory leaks in applications that make heavy use of symbols. The main reason is that JavaScript does not support weak references, causing all symbols created by Scala.js tow remain in memory throughout the lifetime of the application.
+ @code{scala.Symbol} is supported, but is a potential source of memory leaks in applications that make heavy use of symbols. The main reason is that
+ JavaScript does not support weak references, causing all symbols created by Scala.js to remain in memory throughout the lifetime of the application.
@sect{Enumerations}
@p
- The methods @hl.scala{Value()} and @hl.scala{Value(i: Int)} on @lnk.scala{scala.Enumeration} use reflection to retrieve a string representation of the member name and are therefore -- in principle -- unsupported. However, since Enumerations are an integral part of the Scala library, Scala.js adds limited support for these two methods:
+ The methods @code{Value()} and @code{Value(i: Int)} on @code{scala.Enumeration} use reflection to retrieve a string representation of the member name and are therefore -- in principle -- unsupported. However, since Enumerations are an integral part of the Scala library, Scala.js adds limited support for these two methods:
@p
Calls to either of these two methods of the forms:
@hl.scala
@@ -136,17 +156,17 @@
are statically rewritten to (a slightly more complicated version of):
@hl.scala
val <ident> = Value("<ident>")
- val <ident> = Value(<num>,"<ident>")
+ val <ident> = Value(<num>, "<ident>")
@p
Note that this also includes calls like
@hl.scala
- val A,B,C,D = Value
+ val A, B, C, D = Value
@p
- since they are desugared into separate val definitions.
+ since they are desugared into separate @code{val} definitions.
@p
- Calls to either of these two methods which could not be rewritten, or calls to constructors of the protected Val class without an explicit name as parameter, will issue a warning.
+ Calls to either of these two methods which could not be rewritten, or calls to constructors of the protected <code>Val</code> class without an explicit name as parameter, will issue a warning.
@p
- Note that the name rewriting honors the @hl.scala{nextName} iterator. Therefore, the full rewrite is:
+ Note that the name rewriting honors the @code{nextName} iterator. Therefore, the full rewrite is:
@hl.scala
val <ident> = Value(
if (nextName != null && nextName.hasNext)
@@ -155,7 +175,7 @@
"<ident>"
)
@p
- We believe that this covers most use cases of scala.Enumeration. Please let us know if another (generalized) rewrite would make your life easier.
+ We believe that this covers most use cases of @code{scala.Enumeration}. Please let us know if another (generalized) rewrite would make your life easier.
@sect{Library Differences}
@val myTable = Seq(
diff --git a/build.sbt b/build.sbt
index a1170c6..875b6de 100644
--- a/build.sbt
+++ b/build.sbt
@@ -4,9 +4,6 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.merge.MergeStrategy
import org.eclipse.jgit.transport.{UsernamePasswordCredentialsProvider, RefSpec}
-import scalajs.sbtplugin.ScalaJSPlugin._
-import ScalaJSKeys._
-
val cloneRepos = taskKey[Unit]("Clone stuff from github")
val sharedSettings = Seq(
@@ -27,10 +24,7 @@ lazy val book = Project(
"com.lihaoyi" %% "scalatex-site" % "0.1.1",
"com.lihaoyi" %% "ammonite" % "0.1.0"
),
- (resources in Compile) += {
- (fullOptJS in (demos, Compile)).value
- (artifactPath in (demos, Compile, fullOptJS)).value
- },
+ (resources in Compile) += (fullOptJS in (demos, Compile)).value.data,
(unmanagedResourceDirectories in Compile) ++=
(unmanagedResourceDirectories in (demos, Compile)).value,
diff --git a/examples/crossBuilds/clientserver/build.sbt b/examples/crossBuilds/clientserver/build.sbt
index 048df8a..e162ec4 100644
--- a/examples/crossBuilds/clientserver/build.sbt
+++ b/examples/crossBuilds/clientserver/build.sbt
@@ -1,23 +1,20 @@
import NativePackagerKeys._
-import utest.jsrunner.JsCrossBuild
-import scalajs.sbtplugin.ScalaJSPlugin._
-import ScalaJSKeys._
val sharedSettings = Seq(
unmanagedSourceDirectories in Compile +=
baseDirectory.value / "shared" / "main" / "scala",
libraryDependencies ++= Seq(
- "com.scalatags" %%% "scalatags" % "0.4.2",
- "com.lihaoyi" %%% "upickle" % "0.2.5"
+ "com.lihaoyi" %%% "scalatags" % "0.4.3-RC1",
+ "com.lihaoyi" %%% "upickle" % "0.2.6-RC1"
),
- scalaVersion := "2.11.4"
+ scalaVersion := "2.11.5"
)
lazy val client = project.in(file("client"))
- .settings(scalaJSSettings:_*)
+ .enablePlugins(ScalaJSPlugin)
.settings(sharedSettings:_*)
.settings(
libraryDependencies ++= Seq(
- "org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6"
+ "org.scala-js" %%% "scalajs-dom" % "0.7.0"
)
)
@@ -30,9 +27,6 @@ lazy val server = project.in(file("server"))
"io.spray" %% "spray-routing" % "1.3.2",
"com.typesafe.akka" %% "akka-actor" % "2.3.6"
),
- (resources in Compile) += {
- (fastOptJS in (client, Compile)).value
- (artifactPath in (client, Compile, fastOptJS)).value
- }
+ (resources in Compile) += (fastOptJS in (client, Compile)).value.data
)
diff --git a/examples/crossBuilds/clientserver/project/build.sbt b/examples/crossBuilds/clientserver/project/build.sbt
index c24e2b0..2336450 100644
--- a/examples/crossBuilds/clientserver/project/build.sbt
+++ b/examples/crossBuilds/clientserver/project/build.sbt
@@ -1,6 +1,4 @@
/*project/build.sbt*/
-addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
-
-addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4")
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0-RC2")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4")
diff --git a/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala b/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala
index d657290..19ed953 100644
--- a/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala
+++ b/examples/crossBuilds/clientserver/server/src/main/scala/simple/Page.scala
@@ -3,7 +3,7 @@ import scalatags.Text.all._
object Page{
val boot =
- "Client().main(document.getElementById('contents'))"
+ "simple.Client().main(document.getElementById('contents'))"
val skeleton =
html(
head(
diff --git a/examples/crossBuilds/clientserver2/build.sbt b/examples/crossBuilds/clientserver2/build.sbt
index 4c5045f..c5c020c 100644
--- a/examples/crossBuilds/clientserver2/build.sbt
+++ b/examples/crossBuilds/clientserver2/build.sbt
@@ -1,23 +1,20 @@
-import utest.jsrunner.JsCrossBuild
-import scalajs.sbtplugin.ScalaJSPlugin._
-import ScalaJSKeys._
val sharedSettings = Seq(
unmanagedSourceDirectories in Compile +=
baseDirectory.value / "shared" / "main" / "scala",
libraryDependencies ++= Seq(
- "com.scalatags" %%% "scalatags" % "0.4.2",
- "com.lihaoyi" %%% "upickle" % "0.2.5",
- "com.lihaoyi" %%% "autowire" % "0.2.3"
+ "com.lihaoyi" %%% "scalatags" % "0.4.3-RC1",
+ "com.lihaoyi" %%% "upickle" % "0.2.6-RC1",
+ "com.lihaoyi" %%% "autowire" % "0.2.4-RC1"
),
- scalaVersion := "2.11.4"
+ scalaVersion := "2.11.5"
)
lazy val client = project.in(file("client"))
- .settings(scalaJSSettings:_*)
+ .enablePlugins(ScalaJSPlugin)
.settings(sharedSettings:_*)
.settings(
libraryDependencies ++= Seq(
- "org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6"
+ "org.scala-js" %%% "scalajs-dom" % "0.7.0"
)
)
@@ -29,9 +26,6 @@ lazy val server = project.in(file("server"))
"io.spray" %% "spray-routing" % "1.3.2",
"com.typesafe.akka" %% "akka-actor" % "2.3.6"
),
- (resources in Compile) += {
- (fastOptJS in (client, Compile)).value
- (artifactPath in (client, Compile, fastOptJS)).value
- }
+ (resources in Compile) += (fastOptJS in (client, Compile)).value.data
)
diff --git a/examples/crossBuilds/clientserver2/project/build.properties b/examples/crossBuilds/clientserver2/project/build.properties
new file mode 100644
index 0000000..748703f
--- /dev/null
+++ b/examples/crossBuilds/clientserver2/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.7
diff --git a/examples/crossBuilds/clientserver2/project/build.sbt b/examples/crossBuilds/clientserver2/project/build.sbt
index 7c60a91..f969143 100644
--- a/examples/crossBuilds/clientserver2/project/build.sbt
+++ b/examples/crossBuilds/clientserver2/project/build.sbt
@@ -1,5 +1,3 @@
/*project/build.sbt*/
-addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
-
-addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4")
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0-RC2")
diff --git a/examples/crossBuilds/clientserver2/server/src/main/scala/simple/Page.scala b/examples/crossBuilds/clientserver2/server/src/main/scala/simple/Page.scala
index ce6617c..9b21b5a 100644
--- a/examples/crossBuilds/clientserver2/server/src/main/scala/simple/Page.scala
+++ b/examples/crossBuilds/clientserver2/server/src/main/scala/simple/Page.scala
@@ -3,7 +3,7 @@ import scalatags.Text.all._
object Page{
val boot =
- "Client().main(document.getElementById('contents'))"
+ "simple.Client().main(document.getElementById('contents'))"
val skeleton =
html(
head(
diff --git a/examples/crossBuilds/simple/build.sbt b/examples/crossBuilds/simple/build.sbt
index f1cda7e..ce296c4 100644
--- a/examples/crossBuilds/simple/build.sbt
+++ b/examples/crossBuilds/simple/build.sbt
@@ -3,7 +3,7 @@ val sharedSettings = Seq(
baseDirectory.value / "shared" / "main" / "scala"
)
-lazy val js = project.in(file("js")).settings(scalaJSSettings:_*)
+lazy val js = project.in(file("js")).enablePlugins(ScalaJSPlugin)
.settings(sharedSettings:_*)
lazy val jvm = project.in(file("jvm")).settings(sharedSettings:_*) \ No newline at end of file
diff --git a/examples/crossBuilds/simple/project/build.properties b/examples/crossBuilds/simple/project/build.properties
new file mode 100644
index 0000000..748703f
--- /dev/null
+++ b/examples/crossBuilds/simple/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.7
diff --git a/examples/crossBuilds/simple/project/build.sbt b/examples/crossBuilds/simple/project/build.sbt
index a1dbd1d..b42401a 100644
--- a/examples/crossBuilds/simple/project/build.sbt
+++ b/examples/crossBuilds/simple/project/build.sbt
@@ -1,2 +1,2 @@
/*project/build.sbt*/
-addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5") \ No newline at end of file
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0-RC2")
diff --git a/examples/crossBuilds/simple2/project/build.properties b/examples/crossBuilds/simple2/project/build.properties
new file mode 100644
index 0000000..748703f
--- /dev/null
+++ b/examples/crossBuilds/simple2/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.7
diff --git a/examples/crossBuilds/simple2/project/build.sbt b/examples/crossBuilds/simple2/project/build.sbt
index 5bd83ce..a1be3f8 100644
--- a/examples/crossBuilds/simple2/project/build.sbt
+++ b/examples/crossBuilds/simple2/project/build.sbt
@@ -1,4 +1,4 @@
/*project/build.sbt*/
-addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0-RC2")
-addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4") \ No newline at end of file
+addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.5-RC1")
diff --git a/examples/demos/build.sbt b/examples/demos/build.sbt
index 0db3dec..7b0d9e1 100644
--- a/examples/demos/build.sbt
+++ b/examples/demos/build.sbt
@@ -1,8 +1,6 @@
-import scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._
-
(emitSourceMaps in fullOptJS) := false
-scalaJSSettings
+enablePlugins(ScalaJSPlugin)
name := "Example"
@@ -12,12 +10,12 @@ scalaVersion := "2.11.4"
libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided"
-libraryDependencies += "com.lihaoyi" %%% "upickle" % "0.2.5"
+libraryDependencies += "com.lihaoyi" %%% "upickle" % "0.2.6-RC1"
-libraryDependencies += "org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6"
+libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.7.0"
-libraryDependencies += "com.scalatags" %%% "scalatags" % "0.4.2"
+libraryDependencies += "com.lihaoyi" %%% "scalatags" % "0.4.3-RC1"
-libraryDependencies += "com.scalarx" %%% "scalarx" % "0.2.6"
+libraryDependencies += "com.lihaoyi" %%% "scalarx" % "0.2.7-RC1"
libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "0.9.2" \ No newline at end of file
diff --git a/examples/demos/src/main/scala/scrollmenu/Controller.scala b/examples/demos/src/main/scala/scrollmenu/Controller.scala
index b56da31..cfef7b0 100644
--- a/examples/demos/src/main/scala/scrollmenu/Controller.scala
+++ b/examples/demos/src/main/scala/scrollmenu/Controller.scala
@@ -23,7 +23,7 @@ object Controller{
val Seq(main, menu, layout, menuLink) = Seq(
"main", "menu", "layout", "menuLink"
- ).map(dom.document.getElementById)
+ ).map(dom.document.getElementById(_).asInstanceOf[dom.HTMLElement])
val snippets = dom.document.getElementsByClassName("highlight-me")
diff --git a/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala b/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
index f0b9dd4..9a64314 100644
--- a/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
+++ b/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
@@ -57,7 +57,7 @@ class ScrollSpy(structure: Tree[String],
}
js.Array(
- menuItems.map(name => dom.document.getElementById(Controller.munge(name)))
+ menuItems.map(name => dom.document.getElementById(Controller.munge(name)).asInstanceOf[dom.HTMLElement])
.map((el) => () => offset(el, main)):_*
)
}
diff --git a/project/build.sbt b/project/build.sbt
index a80e91d..43b4d9b 100644
--- a/project/build.sbt
+++ b/project/build.sbt
@@ -1,6 +1,6 @@
-addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0-RC2")
-addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.4")
+addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.5-RC1")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4")