summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-11-10 21:10:07 -0800
committerLi Haoyi <haoyi@dropbox.com>2014-11-10 21:10:07 -0800
commit8ced367e0d736b429f0b39ae7fde2b76b1d64ed5 (patch)
treee1154648219f8367c2537580f207e8f100e75590
parent2846d28c95d183792d6d809d1c3884b619b6f937 (diff)
downloadhands-on-scala-js-8ced367e0d736b429f0b39ae7fde2b76b1d64ed5.tar.gz
hands-on-scala-js-8ced367e0d736b429f0b39ae7fde2b76b1d64ed5.tar.bz2
hands-on-scala-js-8ced367e0d736b429f0b39ae7fde2b76b1d64ed5.zip
More refactoring
-rw-r--r--book/src/main/scala/book/Book.scala167
-rw-r--r--book/src/main/scala/book/BookData.scala44
-rw-r--r--book/src/main/scala/book/Main.scala65
-rw-r--r--book/src/main/scala/book/Utils.scala144
-rw-r--r--book/src/main/scalatex/book/Index.scalatex24
-rw-r--r--book/src/main/scalatex/book/Intro.scalatex44
-rw-r--r--book/src/main/scalatex/book/handson/CanvasApp.scalatex20
-rw-r--r--book/src/main/scalatex/book/handson/ClientServer.scalatex6
-rw-r--r--book/src/main/scalatex/book/handson/CommandLine.scalatex12
-rw-r--r--book/src/main/scalatex/book/handson/GettingStarted.scalatex44
-rw-r--r--book/src/main/scalatex/book/handson/PublishingModules.scalatex18
-rw-r--r--book/src/main/scalatex/book/handson/WebPage.scalatex38
-rw-r--r--book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex26
-rw-r--r--book/src/main/scalatex/book/indepth/DesignSpace.scalatex30
-rw-r--r--book/src/main/scalatex/book/indepth/JavaAPIs.scalatex8
-rw-r--r--book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex38
-rwxr-xr-xscalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala5
17 files changed, 366 insertions, 367 deletions
diff --git a/book/src/main/scala/book/Book.scala b/book/src/main/scala/book/Book.scala
index e11c512..9473698 100644
--- a/book/src/main/scala/book/Book.scala
+++ b/book/src/main/scala/book/Book.scala
@@ -1,110 +1,101 @@
package book
import acyclic.file
-import scalatex._
import scalatags.Text.tags2
-import scala.collection.mutable
import scalatags.Text.all._
/**
* Created by haoyi on 10/26/14.
*/
object Book {
- import Utils.sect
- val myTable = Seq(
- ("Most of java.lang.*", "j.l.Thread, j.l.Runtime, ..."),
- ("Almost all of scala.*", "s.c.parallel, s.tools.nsc"),
- ("Some of java.util.*", "org.omg.CORBA, sun.misc.*"),
- ("Scala Macros: upickle, scala-async, scalaxy, etc", "Reflection: scala-pickling, scala-reflect"),
- ("Pure-Scala ecosystem: shapeless, scalaz, scalatags, utest", "Java-dependent: Scalatest, Scalate"),
- ("JS stuff: XmlHttpRequest, Websockets. Localstorage", " JVM stuff: Netty, akka, spray, file IO, JNI"),
- ("HTML DOM, Canvas, WebGL", "AWT, Swing, SWT, OpenGL"),
- ("JavaScript libraries: chipmunk.js, hand.js, react.js, jquery", "Java ecosystem: guice, junit, apache-commons, log4j"),
- ("IntelliJ, Eclipse, SBT, Chrome console, firebug", "Scala REPL, Yourkit, VisualVM, JProfiler")
+ val autoResources = Seq(
+ "META-INF/resources/webjars/highlightjs/8.2-1/highlight.min.js",
+ "META-INF/resources/webjars/highlightjs/8.2-1/styles/idea.min.css",
+ "META-INF/resources/webjars/highlightjs/8.2-1/languages/scala.min.js",
+ "META-INF/resources/webjars/highlightjs/8.2-1/languages/javascript.min.js",
+ "META-INF/resources/webjars/highlightjs/8.2-1/languages/bash.min.js",
+ "META-INF/resources/webjars/highlightjs/8.2-1/languages/diff.min.js",
+ "META-INF/resources/webjars/highlightjs/8.2-1/languages/xml.min.js",
+ "css/pure-min.css",
+ "css/grids-responsive-min.css",
+ "css/layouts/side-menu.css",
+ "js/ui.js",
+ "example-fastopt.js",
+ "webpage/weather.js"
)
+ val manualResources = Seq(
+ "images/javascript-the-good-parts-the-definitive-guide.jpg",
+ "images/Hello World.png",
+ "images/Hello World White.png",
+ "images/Hello World Console.png",
+ "images/IntelliJ Hello.png",
+ "images/Dropdown.png",
+ "images/Scalatags Downloads.png"
+ )
- object hl{
- def highlight(snippet: Seq[String], lang: String) = {
- val string = snippet.mkString
- val lines = string.split("\n", -1)
- if (lines.length == 1){
- code(cls:=lang + " highlight-me", lines(0), padding:=0, display:="inline")
- }else{
- val minIndent = lines.map(_.takeWhile(_ == ' ').length)
- .filter(_ > 0)
- .min
- val stripped = lines.map(_.drop(minIndent))
- .dropWhile(_ == "")
- .mkString("\n")
+ val includes = for(res <- autoResources) yield {
+ if (res.endsWith(".js"))
+ script(src:=res)
+ else if (res.endsWith(".css"))
+ link(rel:="stylesheet", href:=res)
+ else
+ raw("")
+ }
- pre(code(cls:=lang + " highlight-me", stripped))
- }
+ val txt = Index()
+ val contentBar = {
+ def rec(current: Node, depth: Int): Seq[Frag] = {
+ println("\t"*depth + current.name)
+ Seq(
+ li(
+ a(
+ current.name,
+ href:="#"+sect.munge(current.name),
+ paddingLeft := s"${depth * 10 + 10}px",
+ cls := "menu-item" + (if (depth == 1) " menu-item-divided " else "")
+ )
+ )
+ ) ++ current.children.flatMap(rec(_, depth + 1))
}
- def javascript(code: String*) = highlight(code, "javascript")
- def scala(code: String*) = highlight(code, "scala")
- def bash(code: String*) = highlight(code, "bash")
- def diff(code: String*) = highlight(code, "diff")
- def html(code: String*) = highlight(code, "xml")
-
- def ref(filepath: String, start: String = "", end: String = "\n") = {
-
- val lang = filepath.split('.').last match {
- case "js" => "javascript"
- case "scala" => "scala"
- case "sbt" => "scala"
- case "sh" => "bash"
- case "html" => "xml"
- case x =>
- println("??? " + x)
- ???
- }
-
- val lines = io.Source.fromFile(filepath).getLines().toVector
+ println("TABLE OF CONTENTS")
+ rec(sect.structure, 0)
+ }
+ val site = Seq(
+ raw("<!doctype html>"),
+ html(
+ head(
+ meta(charset:="utf-8"),
+ meta(name:="viewport", content:="width=device-width, initial-scale=1.0"),
+ tags2.title("Hands-on Scala.js"),
+ includes
+ ),
- def indent(line: String) = line.takeWhile(_.isWhitespace).length
+ div(id:="layout")(
+ a(href:="#menu", id:="menuLink", cls:="menu-link")(
+ span
+ ),
- val startLine = lines.indexWhere(_.contains(start))
- if (startLine == -1){
- throw new Exception("Can't find marker: " + start)
- }
- val whitespace = indent(lines(startLine))
- val endLine = lines.indexWhere(
- line => line.contains(end) || (indent(line) < whitespace && line.trim != ""),
- startLine
+ div(id:="menu")(
+ div(cls:="pure-menu pure-menu-open")(
+ a(cls:="pure-menu-heading", href:="#")(
+ "Contents"
+ ),
+ ul(cls:="menu-item-list")(
+ contentBar
+ )
+ )
+ )
+ ),
+ div(id:="main",
+ div(id:="main-box")(
+ txt
+ )
)
- val sliced =
- if (endLine == -1) lines.drop(startLine)
- else lines.slice(startLine, endLine)
- val blob = sliced.map(_.drop(whitespace)).mkString("\n")
+ )
+ ).render
+
- pre(code(cls:=lang + " highlight-me", blob))
- }
- }
- import java.io.File
- def recursiveListFiles(f: File): Array[File] = {
- val these = f.listFiles
- these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
- }
- lazy val javaAPIs = {
- val roots = Seq(
- "output/scala-js/javalanglib/src/main/scala",
- "output/scala-js/javalib/src/main/scala"
- )
- for{
- root <- roots
- file <- recursiveListFiles(new File(root))
- if file != null
- if file.isFile
- } yield{
- val path = file.getPath
- .drop(root.length + 1)
- .dropRight(".scala".length)
- val filename = path.replace('/', '.')
- val docpath = s"https://docs.oracle.com/javase/7/docs/api/$path.html"
- filename -> docpath
- }
- }
}
diff --git a/book/src/main/scala/book/BookData.scala b/book/src/main/scala/book/BookData.scala
new file mode 100644
index 0000000..0a47aaf
--- /dev/null
+++ b/book/src/main/scala/book/BookData.scala
@@ -0,0 +1,44 @@
+package book
+
+import acyclic.file
+
+object BookData {
+ val myTable = Seq(
+ ("Most of java.lang.*", "j.l.Thread, j.l.Runtime, ..."),
+ ("Almost all of scala.*", "s.c.parallel, s.tools.nsc"),
+ ("Some of java.util.*", "org.omg.CORBA, sun.misc.*"),
+ ("Scala Macros: upickle, scala-async, scalaxy, etc", "Reflection: scala-pickling, scala-reflect"),
+ ("Pure-Scala ecosystem: shapeless, scalaz, scalatags, utest", "Java-dependent: Scalatest, Scalate"),
+ ("JS stuff: XmlHttpRequest, Websockets. Localstorage", " JVM stuff: Netty, akka, spray, file IO, JNI"),
+ ("HTML DOM, Canvas, WebGL", "AWT, Swing, SWT, OpenGL"),
+ ("JavaScript libraries: chipmunk.js, hand.js, react.js, jquery", "Java ecosystem: guice, junit, apache-commons, log4j"),
+ ("IntelliJ, Eclipse, SBT, Chrome console, firebug", "Scala REPL, Yourkit, VisualVM, JProfiler")
+ )
+
+ lazy val javaAPIs = {
+ import java.io.File
+ def recursiveListFiles(f: File): Array[File] = {
+ val these = f.listFiles
+ these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
+ }
+
+ val roots = Seq(
+ "output/scala-js/javalanglib/src/main/scala",
+ "output/scala-js/javalib/src/main/scala"
+ )
+ for{
+ root <- roots
+ file <- recursiveListFiles(new File(root))
+ if file != null
+ if file.isFile
+ } yield{
+ val path = file.getPath
+ .drop(root.length + 1)
+ .dropRight(".scala".length)
+ val filename = path.replace('/', '.')
+ val docpath = s"https://docs.oracle.com/javase/7/docs/api/$path.html"
+ filename -> docpath
+ }
+ }
+}
+
diff --git a/book/src/main/scala/book/Main.scala b/book/src/main/scala/book/Main.scala
index 78c9c33..8ebcbd4 100644
--- a/book/src/main/scala/book/Main.scala
+++ b/book/src/main/scala/book/Main.scala
@@ -1,14 +1,8 @@
package book
import acyclic.file
import java.io.InputStream
-import java.nio.charset.StandardCharsets
import java.nio.file.{Paths, Files}
-import org.eclipse.jgit.api.Git
-
-import scala.collection.mutable
-import scalatags.Text.all._
-import scalatags.Text.tags2
object Main {
@@ -23,68 +17,15 @@ object Main {
Files.copy(src, Paths.get(dest))
}
- val txt = Index.template
- val contentBar = {
- def rec(current: Node, depth: Int): Seq[Frag] = {
- println("\t"*depth + current.name)
- Seq(
- li(
- a(
- current.name,
- href:="#"+Utils.munge(current.name),
- paddingLeft := s"${depth * 10 + 10}px",
- cls := "menu-item" + (if (depth == 1) " menu-item-divided " else "")
- )
- )
- ) ++ current.children.flatMap(rec(_, depth + 1))
- }
-
- println("TABLE OF CONTENTS")
- rec(Utils.structure, 0)
- }
- val site = Seq(
- raw("<!doctype html>"),
- html(
- head(
- meta(charset:="utf-8"),
- meta(name:="viewport", content:="width=device-width, initial-scale=1.0"),
- tags2.title("Hands-on Scala.js"),
- Utils.includes
- ),
-
- div(id:="layout")(
- a(href:="#menu", id:="menuLink", cls:="menu-link")(
- span
- ),
-
- div(id:="menu")(
- div(cls:="pure-menu pure-menu-open")(
- a(cls:="pure-menu-heading", href:="#")(
- "Contents"
- ),
- ul(cls:="menu-item-list")(
- contentBar
- )
- )
- )
- ),
- div(id:="main",
- div(id:="main-box")(
- txt
- )
- )
- )
- ).render
-
def main(args: Array[String]): Unit = {
println("Writing Book")
+ write(Book.site, "output/index.html")
- write(site, "output/index.html")
-
- for(res <- Utils.autoResources ++ Utils.manualResources) {
+ for(res <- Book.autoResources ++ Book.manualResources) {
copy(getClass.getResourceAsStream("/" + res), "output/" + res)
}
+ sect.structure
println("Writing Done")
}
diff --git a/book/src/main/scala/book/Utils.scala b/book/src/main/scala/book/Utils.scala
index 01682da..fd9dd97 100644
--- a/book/src/main/scala/book/Utils.scala
+++ b/book/src/main/scala/book/Utils.scala
@@ -3,54 +3,16 @@ package book
import acyclic.file
import scala.collection.mutable
import scalatags.Text.all._
-
-case class Node(name: String, children: mutable.Buffer[Node])
-object Utils{
- val autoResources = Seq(
- "META-INF/resources/webjars/highlightjs/8.2-1/highlight.min.js",
- "META-INF/resources/webjars/highlightjs/8.2-1/styles/idea.min.css",
- "META-INF/resources/webjars/highlightjs/8.2-1/languages/scala.min.js",
- "META-INF/resources/webjars/highlightjs/8.2-1/languages/javascript.min.js",
- "META-INF/resources/webjars/highlightjs/8.2-1/languages/bash.min.js",
- "META-INF/resources/webjars/highlightjs/8.2-1/languages/diff.min.js",
- "META-INF/resources/webjars/highlightjs/8.2-1/languages/xml.min.js",
- "css/pure-min.css",
- "css/grids-responsive-min.css",
- "css/layouts/side-menu.css",
- "js/ui.js",
- "example-fastopt.js",
- "webpage/weather.js"
- )
-
- val manualResources = Seq(
- "images/javascript-the-good-parts-the-definitive-guide.jpg",
- "images/Hello World.png",
- "images/Hello World White.png",
- "images/Hello World Console.png",
- "images/IntelliJ Hello.png",
- "images/Dropdown.png",
- "images/Scalatags Downloads.png"
- )
-
- val includes = for(res <- Utils.autoResources) yield {
- if (res.endsWith(".js"))
- script(src:=res)
- else if (res.endsWith(".css"))
- link(rel:="stylesheet", href:=res)
- else
- raw("")
- }
-
+object sect{
var indent = 0
-
val headers = Seq[((String, String) => scalatags.Text.Tag, Option[Frag => Frag])](
((h, s) => div(cls:="header")(
h1(h),
h2(s)
), Some(f => div(cls:="content", f))),
((h, s) => div(cls:="header")(
- h1(id:=Utils.munge(h), h),
+ h1(id:=munge(h), h),
br
), None),
(h1(_, _), None),
@@ -61,27 +23,89 @@ object Utils{
(h6(_, _), None)
)
- var structure: Node = null
- case class sect(name: String, subname: String = ""){
- indent += 1
- val newNode = Node(name, mutable.Buffer.empty)
- val (headerWrap, contentWrap) = headers(indent-1)
- if (structure!= null) structure.children.append(newNode)
- val prev = structure
- structure = newNode
- def apply(args: Frag*) = {
- val wrappedContents = contentWrap.getOrElse((x: Frag) => x)(args)
- val res = Seq[Frag](
- if (name == "") ""
- else headerWrap(name, subname)(cls:="content-subhead", id:=munge(name)),
- wrappedContents
- )
- indent -= 1
- if (prev != null) structure = prev
- res
- }
- }
+ var structure = Node("root", mutable.Buffer.empty)
+
def munge(name: String) = {
name.replace(" ", "")
}
}
+case class sect(name: String, subname: String = ""){
+ sect.indent += 1
+ val newNode = Node(name, mutable.Buffer.empty)
+ val (headerWrap, contentWrap) = sect.headers(sect.indent-1)
+ sect.structure.children.append(newNode)
+ val prev = sect.structure
+ sect.structure = newNode
+ def apply(args: Frag*) = {
+ val wrappedContents = contentWrap.getOrElse((x: Frag) => x)(args)
+ val res = Seq[Frag](
+ if (name == "") ""
+ else headerWrap(name, subname)(cls:="content-subhead", id:=sect.munge(name)),
+ wrappedContents
+ )
+ sect.indent -= 1
+ sect.structure = prev
+ res
+ }
+}
+case class Node(name: String, children: mutable.Buffer[Node])
+
+object hl{
+ def highlight(snippet: Seq[String], lang: String) = {
+ val string = snippet.mkString
+ val lines = string.split("\n", -1)
+ if (lines.length == 1){
+ code(cls:=lang + " highlight-me", lines(0), padding:=0, display:="inline")
+ }else{
+ val minIndent = lines.map(_.takeWhile(_ == ' ').length)
+ .filter(_ > 0)
+ .min
+ val stripped = lines.map(_.drop(minIndent))
+ .dropWhile(_ == "")
+ .mkString("\n")
+
+ pre(code(cls:=lang + " highlight-me", stripped))
+ }
+ }
+
+ def javascript(code: String*) = highlight(code, "javascript")
+ def scala(code: String*) = highlight(code, "scala")
+ def bash(code: String*) = highlight(code, "bash")
+ def diff(code: String*) = highlight(code, "diff")
+ def html(code: String*) = highlight(code, "xml")
+
+ def ref(filepath: String, start: String = "", end: String = "\n") = {
+
+ val lang = filepath.split('.').last match {
+ case "js" => "javascript"
+ case "scala" => "scala"
+ case "sbt" => "scala"
+ case "sh" => "bash"
+ case "html" => "xml"
+ case x =>
+ println("??? " + x)
+ ???
+ }
+
+ val lines = io.Source.fromFile(filepath).getLines().toVector
+
+ def indent(line: String) = line.takeWhile(_.isWhitespace).length
+
+ val startLine = lines.indexWhere(_.contains(start))
+ if (startLine == -1){
+ throw new Exception("Can't find marker: " + start)
+ }
+ val whitespace = indent(lines(startLine))
+ val endLine = lines.indexWhere(
+ line => line.contains(end) || (indent(line) < whitespace && line.trim != ""),
+ startLine
+ )
+ val sliced =
+ if (endLine == -1) lines.drop(startLine)
+ else lines.slice(startLine, endLine)
+ val blob = sliced.map(_.drop(whitespace)).mkString("\n")
+
+
+ pre(code(cls:=lang + " highlight-me", blob))
+ }
+} \ No newline at end of file
diff --git a/book/src/main/scalatex/book/Index.scalatex b/book/src/main/scalatex/book/Index.scalatex
index 3f0c69f..6948be5 100644
--- a/book/src/main/scalatex/book/Index.scalatex
+++ b/book/src/main/scalatex/book/Index.scalatex
@@ -18,45 +18,45 @@
This book does not spend time on pontifying a philosophy or ideology behind Scala.js or Scala. It instead spends its words on hands-on tutorials and in-depth dives into parts of the Scala.js platform, to try and get you acquainted with Scala.js as soon as possible, so you can make your own decisions about its merits or qualities.
@sect("Intro to Scala.js")
- @Intro.template
+ @Intro()
@sect("Hands On", "Writing your first Scala.js programs")
@p
This half of the book walks you through various facets of the Scala.js development experience. From making your first app, to testing and publishing modules, to writing an integrated client-server application.
@sect("Getting Started")
- @handson.GettingStarted.template
+ @handson.GettingStarted()
@sect("Making a Canvas App")
- @handson.CanvasApp.template
+ @handson.CanvasApp()
@sect("Interactive Web Pages")
- @handson.WebPage.template
+ @handson.WebPage()
@sect("The Command Line")
- @handson.CommandLine.template
+ @handson.CommandLine()
@sect("Publishing Modules")
- @handson.PublishingModules.template
+ @handson.PublishingModules()
@sect("Integrating Client-Server")
- @handson.ClientServer.template
+ @handson.ClientServer()
@sect("Scala.js in Depth", "Exploring Scala.js")
@p
This half of the book dives into a few aspects of Scala.js much more deeply that the hands-on introduction does. It's aimed at someone who has already used Scala.js, and wants to explore the edge-cases, how things work under-the-cover, or why it has been designed in such a way. It's not a formal specification; rather, it's aim is to be a useful reference to read instead of (or in preparation for) digging into the implementation code.
@sect("Javascript Interoperability")
- @indepth.JavascriptInterop.template
+ @indepth.JavascriptInterop()
@sect("Deviations from Scala-JVM")
- @indepth.SemanticDifferences.template
+ @indepth.SemanticDifferences()
@sect("The Compilation Pipeline")
- @indepth.CompilationPipeline.template
+ @indepth.CompilationPipeline()
@sect("Scala.js' Design Space")
- @indepth.DesignSpace.template
+ @indepth.DesignSpace()
@sect("Java APIs")
- @indepth.JavaAPIs.template \ No newline at end of file
+ @indepth.JavaAPIs() \ No newline at end of file
diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex
index dbe1960..f9e0761 100644
--- a/book/src/main/scalatex/book/Intro.scalatex
+++ b/book/src/main/scalatex/book/Intro.scalatex
@@ -23,19 +23,19 @@
});
@p
- As you can see, both of the above programs do identical things: it's just that the one on the left is written in Scala and the one on the right is in Javascript.
+ As you can see, both of the above programs do identical things: it's just that the one on the left is written in Scala and the one on the right is in Javascript.
@p
Traditionally, Scala has been a language which runs on the JVM. This eliminates it from consideration in many cases, e.g. when you need to build interactive web apps, you need to fall back to Javascript to run your client-side code, at a great loss in terms of toolability and maintainability. Scala.js lets you to develop web applications with the safety and toolability that comes with a statically typed language:
@ul
- @li
+ @li
Typo-safety due to its compiler which catches many silly errors before the code is run
- @li
+ @li
In-editor support for autocomplete, error-highlghting, refactors, and intelligent navigation
- @li
+ @li
Very small compiled executables, in the 100-400kb range
- @li
+ @li
Source-maps for ease of debugging
@p
@@ -43,7 +43,7 @@
@sect{About Javascript}
@p
- Javascript is the language supported by web browsers, and is the only language available if you wish to write interactive web applications. As more and more activity moves online, the importance of web apps will only increase over time.
+ Javascript is the language supported by web browsers, and is the only language available if you wish to write interactive web applications. As more and more activity moves online, the importance of web apps will only increase over time.
@sect{Javascript-the-language}
@p
@@ -58,11 +58,11 @@
However, even as Javascript-the-language sucks, Javascript-the-platform has some very nice properties that make it a good target for application developers:
@ul
- @li
+ @li
Zero-install distribution: just go to a URL and have the application downloaded and ready to use.
- @li
+ @li
Hyperlinks: being able to link to a particular page or item within a web app is a feature other platforms lack, and makes it much easier to cross-reference between different systems
- @li
+ @li
Sandboxed security: before the advent of mobile apps, web apps were the most secure runtime available, offering none of the risk or worry that comes with installing desktop software
@p
@@ -71,7 +71,7 @@
@hr
@p
- This is where Scala.js comes in. As developers we want Javascript-the-platform, with its ease-of-distribution, hyperlinks and security characteristics. We do not want Javascript-the-language, with its propensity for bugs, verbosity, and fragility.
+ This is where Scala.js comes in. As developers we want Javascript-the-platform, with its ease-of-distribution, hyperlinks and security characteristics. We do not want Javascript-the-language, with its propensity for bugs, verbosity, and fragility.
@sect{The Promise}
@@ -91,7 +91,7 @@
At a first approximation, Scala.js provides you a sane language to do development in the web browser. Not only do you have an expressive language with static types, you also have great tooling with IDEs like IntelliJ and Eclipse, a rich library of standard collections, and many other modern conveniences that we take for granted but are curiously missing when working in the wild west of web development: the browser!
@p
- While not useful for small applications, where most of the logic is gluing together external APIs, this comes in very useful in large applications where a lot of the complexity and room-for-error is entirely internal. With larger apps, you can no longer blame browser vendors for confusing APIs that make your code terrible: these confusing APIs only lurk in the peripherals around a larger, complex application. One thing you learn working in large-ish web client-side code-bases is that the bulk of the confusion and complexity is no-one's fault but your own, as a team.
+ While not useful for small applications, where most of the logic is gluing together external APIs, this comes in very useful in large applications where a lot of the complexity and room-for-error is entirely internal. With larger apps, you can no longer blame browser vendors for confusing APIs that make your code terrible: these confusing APIs only lurk in the peripherals around a larger, complex application. One thing you learn working in large-ish web client-side code-bases is that the bulk of the confusion and complexity is no-one's fault but your own, as a team.
@p
At this point, all of Google, Facebook, and Microsoft have all announced work on a typed variant of Javascript. These are not academic exercises: Dart/AtScript/Flow/Typescript are all problems that solve a real need, that these large companies have all faced once they've grown beyond a certain size. Clearly, Javascript isn't cutting it anymore, and the convenience and "native-ness" of the language is more than made up for in the constant barrage of self-inflicted problems. Scala.js takes this idea and runs with it!
@@ -128,12 +128,12 @@
@sect{Client-Server Integration}
@p
- There is an endless supply of new platforms which have promised to change-the-way-we-do-web-development-forever. From old-timers like @a("Ur-Web", href:="http://www.impredicative.com/ur/"), to @a("GWT", href:="http://www.gwtproject.org/"), to Asana's @a("LunaScript", href:="https://asana.com/luna"), to more recently things like @a("Meteor.js", href:="https://www.meteor.com/").
+ There is an endless supply of new platforms which have promised to change-the-way-we-do-web-development-forever. From old-timers like @a("Ur-Web", href:="http://www.impredicative.com/ur/"), to @a("GWT", href:="http://www.gwtproject.org/"), to Asana's @a("LunaScript", href:="https://asana.com/luna"), to more recently things like @a("Meteor.js", href:="https://www.meteor.com/").
@p
- One common theme in all these platforms is that their main selling point is their tight, seamless client-server integration, to the point where you can just make method calls across the client-server boundary and the platform/language/compiler figures out what to do.
+ One common theme in all these platforms is that their main selling point is their tight, seamless client-server integration, to the point where you can just make method calls across the client-server boundary and the platform/language/compiler figures out what to do.
@p
With Scala.js and Scala-JVM, such conveniences like making method calls across the client-server boundary is the boring reality. Not only are the calls transparent, they are also statically checked, so any mistake in the route name or the parameters it expects, or the result type it returns to you, will be caught by the compiler long before even manual testing.
-
+
@hr
@p
@@ -152,27 +152,27 @@
A whirlwind tour of the various things that Scala.js can be used for. We will cover:
@ul
- @li
+ @li
Your first Scala.js application: setting up your development environment, cloning the example repository, debugging and finally publishing your first toy application
- @li
+ @li
An interactive web app using Scala.js: how you interact with the HTML DOM, how you utilize Ajax calls and other browser APIs that are common in Javascript-heavy applications
- @li
+ @li
A Scala.js library: how to write a module that can be depended on by applications both your own and by others, and be used both with Scala.js and Scala-on-the-JVM
- @li
+ @li
Client-Server integration: We will build a simple web application with a Scala server and Scala.js client. In the process, we'll explore how to share code between client and server, how to get compiler-checked/boilerplate-free Ajax calls between client and server, and many other long-standing holy-grails of web development
-
- @p
+
+ @p
After going through this chapter and following along with the exercises, you should have a good sense of how Scala.js works and how it feels building things in Scala.js. You would not be an expert, but you'll know where to get started if you decide to try out Scala.js for your next project.
@sect{Part 2: In Depth}
@p
This section of the book will cover lots of content that does not fit in the earlier Hands-On portion of the book. Things that aren't immediately necessary to get something up and running, things that only advanced users would care about, things that you probably don't need to know but you'd like to know out of intellectual curiosity.
- @p
+ @p
In general, this section of the book will go much deeper into Scala.js, much more than is necessary to get your first applications built. We will talk about the small number of semantic differences between Scala.js and Scala, details of the foreign-function-interface with Javascript, the various optimization levels and what they do. Nothing pressing or urgently needed, but all very interesting, and worth reading if you want to really understand Scala.js in depth.
@hr
-
+
@p
As mentioned earlier, these chapters can be read in any order. In keeping with the spirit of the book, we'll jump right into the Hands On to give you the experience, and save the talking and philosophizing for later. Let's go!
diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
index 03a3779..5f40270 100644
--- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex
+++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
@@ -2,7 +2,7 @@
@p
By this point, you've already cloned and got your hands dirty fiddling around with the toy @a("workbench-example-app", href:="https://github.com/lihaoyi/workbench-example-app"). You have your editor set up, SBT installed, and have published the example application in a way you can host online for other people to see. Maybe you've even made some changes to the application to see what happens. Hopefully you're curious, and want to learn more.
-@p
+@p
In this section of the book, we will walk through making a small canvas application. This will expose you to important concepts like:
@ul
@@ -49,7 +49,7 @@
Apart from mouse events, keyboard events, scroll events, input events, etc. are all usable from Scala.js as you'd expect
@sect{Making a Clock using setInterval}
-
+
@p
You've already seen this in the previous example, but @hl.scala{dom.setInterval} can be used to schedule recurring, periodic events in your program. Common use cases include running the event loop for a game, making smooth animations, and other tasks of that sort which require some work to happen over a period of time.
@@ -84,7 +84,7 @@
In this example we will make a spiritual clone of the popular @a("Flappy Bird", href:="http://en.wikipedia.org/wiki/Flappy_Bird") video game. This game involves a few simple rules
@ul
- @li
+ @li
Your character starts in the middle of the screen
@li
Gravity pulls your character down
@@ -111,7 +111,7 @@
@hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "/*variables*/", end="def runLive")
@p
- This is where we start defining things that are relevant to Flappy Box. There are roughly two groups of values here: immutable constants in the top group, and mutable variables in the bottom. The rough meaning of each variable is documented in the comments, and we'll see exactly how we use them later.
+ This is where we start defining things that are relevant to Flappy Box. There are roughly two groups of values here: immutable constants in the top group, and mutable variables in the bottom. The rough meaning of each variable is documented in the comments, and we'll see exactly how we use them later.
@p
One notable thing is that we're using a @hl.scala{collection.mutable.Queue} to store the list of obstacles. This is defined in the Scala standard library; in general, all the collections in the Scala standard library can be used without issue in Scala.js.
@@ -134,7 +134,7 @@
@li
Rendering everything, including the player as the namesake box
- @p
+ @p
This function basically contains all the game logic, from motion, to collision-detection, to rendering, so it's pretty large. Not that large though! And entirely understandable, even if it takes a few moments to read through.
@hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "def runDead", "def run()")
@@ -181,14 +181,14 @@
@hl.scala{obstacles} is a Scala @hl.scala{mutable.Queue}, as we defined it earlier, and all the methods on it are Scala method calls
@li
@hl.scala{renderer} is a Javascript @hl.javascript{CanvasRenderingContext2D}, and all the methods on it are Javascript method calls directly on the Javascript object
- @li
+ @li
@hl.scala{frame} is a Scala @hl.scala{Int}, and obeys Scala semantics, though it is implemented as a Javascript @hl.javascript{Number} under the hood.
- @li
+ @li
@hl.scala{playerY} and @hl.scala{playerV} are Scala @hl.scala{Double}s, implemented directly as Javascript @hl.javascript{Number}s
@p
- This reveals something pretty interesting about Scala.js: even though Scala at-first-glance is a very different language from Javascript, the interoperation with Javascript is so seamless that you can't even tell from the code which values/methods are defined in Scala and which values/methods come from Javascript!
- @p
+ This reveals something pretty interesting about Scala.js: even though Scala at-first-glance is a very different language from Javascript, the interoperation with Javascript is so seamless that you can't even tell from the code which values/methods are defined in Scala and which values/methods come from Javascript!
+ @p
These two classes of values/methods are treated very differently by the compiler when it comes to emitting the executable Javascript blob, but the compiler does not need extra syntax telling it which things belong to Scala and which to Javascript: the types are sufficient. @hl.scala{renderer}, for example is of type @hl.scala{dom.CanvasRenderContext2D} which is a subtype of @hl.scala{scalajs.js.Object}, indicating to the compiler that it needs special treatment. Primitives like @hl.scala{Double}s and @hl.scala{Int}s have similar treatment
@p
@@ -209,5 +209,5 @@
@li
Draw something pretty! We have a working canvas, a nice programming language, and a way to easily publish the results online. @a("Various", href:="http://www.scala-js-fiddle.com/gist/77a3840678d154257ca1/KochSnowflake.scala") @a("fractals", href:="http://www.scala-js-fiddle.com/gist/77a3840678d154257ca1/KochCurve.scala"), or @a("colorful visualizations", href:="http://www.scala-js-fiddle.com/gist/9443f8e0ecc68d1058ad/RayTracer.scala") are all possibilities.
- @p
+ @p
By this point you've some experience building stand-alone, single-canvas Scala.js applications, which has hopefully given you a feel for how Scala.js works. The problem is that few web applications satisfy the criteria of being stand-alone single-page canvas applications! Most web applications need to deal with the DOM of the HTML page, need to fetch data from web services, and generally need to do a lot of other messy things. We'll go into that in the next chapter
diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex
index 817f568..771dbde 100644
--- a/book/src/main/scalatex/book/handson/ClientServer.scalatex
+++ b/book/src/main/scalatex/book/handson/ClientServer.scalatex
@@ -31,7 +31,7 @@
└── src/main/scala/simple
├── Page.scala
└── Server.scala
-
+
@p
First, let's do the wiring in @code{build.sbt}:
@@ -41,7 +41,7 @@
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 Cross-platform Modules) and the settings to add Scalatags and 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.
@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 additioanl 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.
-
+
@p
Next, let's kick off the Spray server in our Scala-JVM main method:
@@ -92,7 +92,7 @@
@sect{Boilerplate-free Serialization}
@p
- The Ajax/RPC layer is one of the more fragile parts of web applications. Often, you have your various Ajax endpoints written once on the server, have a set of routes written to connect those Ajax endpoints to URLs, and client code (traditionally Javascript) made calls to those URLs with "raw" data: basically whatever you wanted, packed in an ad-hoc mix of CSV and JSON and raw-strings.
+ The Ajax/RPC layer is one of the more fragile parts of web applications. Often, you have your various Ajax endpoints written once on the server, have a set of routes written to connect those Ajax endpoints to URLs, and client code (traditionally Javascript) made calls to those URLs with "raw" data: basically whatever you wanted, packed in an ad-hoc mix of CSV and JSON and raw-strings.
@p
This has always been annoying boilerplate, and Scala.js removes it. With uPickle, you can simply call @hl.scala{upickle.write(...)} and @hl.scala{upickle.read[T](...)} to convert your collections, primitives or case-classes to and from JSON. This means you do not need to constantly re-invent different ways of making Ajax calls: you can just fling the data right across the network from client to server and back again.
diff --git a/book/src/main/scalatex/book/handson/CommandLine.scalatex b/book/src/main/scalatex/book/handson/CommandLine.scalatex
index dffeaaf..1bffef2 100644
--- a/book/src/main/scalatex/book/handson/CommandLine.scalatex
+++ b/book/src/main/scalatex/book/handson/CommandLine.scalatex
@@ -18,7 +18,7 @@
@code{package}: bundling up our Scala.js IR into a @code{.jar} file, for publishing or distribution as a library
@li
@code{fastOptJS}: aggregating our Scala.js IR and converting it to a @code{.js} executable.
- @li
+ @li
@code{fullOptJS}: aggregating our Scala.js IR and converting it to a smaller, faster @code{.js} executable.
@li
@code{run}: run your compiled Scala.js code as Javascript in Rhino, Node.js or Phantom.js
@@ -94,7 +94,7 @@
@p
Again, I won't go into much details, as exactly what this optimization does is described in the chapter on the Compilation Pipeline. This command is somewhat too-slow to be running during iterative development, and is instead typically used just before deployment to minimize the size of the file your users have to download.
- @sect{The run Command}
+ @sect{The run Command}
@hl.bash
> run
@p
@@ -111,7 +111,7 @@
@p
Running @code{sbt run} with the above Scala.js code will print out
-
+
@hl.bash
Hello World!
In Scala.js, 1/0 is 0!
@@ -133,12 +133,12 @@
@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 Headless Runtimes and Run Configurations to learn more about the other settings and why you would want to use them.
- @sect{The test Command}
+ @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 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.
@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
@p
@@ -161,7 +161,7 @@
@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{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.
@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.
@li
diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
index ae97eb2..0ad211f 100644
--- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex
+++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
@@ -3,15 +3,15 @@
To get started with Scala.js, you will need to prepare a few things:
@ul
- @li
+ @li
@a("sbt", href:="http://www.scala-sbt.org/"): SBT is the most common build-tool in the Scala community, and is what we will use for building our Scala.js application. Their home page has a link to download and install it.
- @li
- An editor for Scala: @a("IntelliJ Scala", href:="http://blog.jetbrains.com/scala/") and @a("Eclipse ScalaIDE", href:="http://scala-ide.org/") are the most popular choices and work on all platforms, though there are others.
+ @li
+ An editor for Scala: @a("IntelliJ Scala", href:="http://blog.jetbrains.com/scala/") and @a("Eclipse ScalaIDE", href:="http://scala-ide.org/") are the most popular choices and work on all platforms, though there are others.
@li
@a("Git", href:="http://git-scm.com/"): This is a version-control system that we will use to download and manage our Scala.js projects.
- @li
+ @li
A terminal: on OSX you have @a("Terminal.app", href:="http://guides.macrumors.com/Terminal") already installed, in Linux you have @a("Terminal", href:="https://help.ubuntu.com/community/UsingTheTerminal"), and on Windows you have @a("PowerShell", href:="http://en.wikipedia.org/wiki/Windows_PowerShell").
- @li
+ @li
Your favorite web browser: @a("Chrome", href:="https://www.google.com/chrome") and @a("Firefox", href:="https://www.mozilla.org/en-US/firefox") are the most popular.
@p
@@ -58,7 +58,7 @@
[success] Total time: 11 s, completed Oct 26, 2014 3:42:21 PM
1. Waiting for source changes... (press enter to interrupt)
-@p
+@p
The line @code{Waiting for source changes...} is telling you that your Scala.js program is ready! Now, when you go to the web URL @code{http://localhost:12345/target/scala-2.11/classes/index-dev.html} in your browser, you should see the following:
@img(src:="images/Hello World.png", maxWidth:="100%")
@@ -98,7 +98,7 @@
@p
We've downloaded, compiled, ran, and made changes to our first Scala.js application. Let's now take a closer look at the code that we just ran:
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala")
+ @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala")
@p
It's a good chunk of code, though not a huge amount. To someone who didn't know about Scala.js, they would just think it's normal Scala, albeit with this unusual @hl.scala{dom} library and a few weird annotations. Let's pick it apart starting from the top:
@@ -124,7 +124,7 @@
@p
@hl.scala{document.getElementById} is the exact same API that's used in normal Javascript, as documented @a("here", href:="https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById"). In fact, the entire @hl.scala{org.scalajs.dom} namespace (imported at the top of the file) comprises statically typed facades for the javascript APIs provided by the browser.
- @p
+ @p
We need to perform the @hl.scala{asInstanceOf} call because depending on what you pass to @hl.scala{getElementById} and @hl.scala{getContext}, you could be returned elements and contexts of different types. Hence we need to tell the compiler explicitly that we're expecting a @hl.scala{dom.HTMLCanvasElement} and @hl.scala{dom.CanvasRenderingContext2D} back from these methods for the strings we passed in.
@hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "def run", "dom.setInterval")
@@ -133,13 +133,13 @@
This is the part of the Scala.js program which does the real work. It runs 10 iterations of a @a("small algorithm", href:="http://en.wikipedia.org/wiki/Sierpinski_triangle#Chaos_game") that generates a Sierpinski Triangle point-by-point. The steps, as described by the linked article, are roughly:
@ul
- @li
+ @li
Pick a random corner of the large-triangle
- @li
+ @li
Move your current-position @hl.scala{p} halfway between its current location and that corner
- @li
+ @li
Draw a dot
- @li
+ @li
Repeat
@p
@@ -164,17 +164,17 @@
@p
Of the two, only the Scala.js plugin is really necessary. The Workbench plugin is a convenience that makes development easier. Without it you'd need to keep a terminal open to view the SBT logspam, and manually refresh the page when compilation finished. Not the end of the world.
-
+
@sect{build.sbt}
@hl.ref("output/workbench-example-app/build.sbt")
@p
The @code{build.sbt} project file for this application is similarly unremarkable: It includes the settings for the two SBT plugins we saw earlier, as well as boilerplate @hl.scala{name}/@hl.scala{version}/@hl.scala{scalaVersion} values common to all projects.
- @p
+ @p
Of interest is the @hl.scala{libraryDependencies}. In Scala-JVM, this key is used to declare dependencies on libraries from Maven Central, so you can use them in your Scala-JVM projects. In Scala.js, the same key is used to declare dependencies on libraries so you can use them in your Scala.js projects! Re-usable libraries can be built and published with Scala.js just as you do on Scala-JVM, and here we make use of one which provides the typed facades with which we used to access the DOM in the application code.
- @p
+ @p
Lastly, we have two Workbench related settings: @hl.scala{bootSnippet} basically tells Workbench how to restart your application when a new compilation run finishes, and @hl.scala{updateBrowsers} actually tells it to perform this application-restarting.
@sect{src/main/resources/index-dev.html}
@@ -185,7 +185,7 @@
@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.
-
+
@p
Lastly, only @hl.scala("@JSExport")ed objects and methods can be called from Javascript. Also, although this example only exports the @hl.scala{main} method which is called once, there is nothing stopping you from exporting any number of objects and methods and calling them whenever you need to. In this way, you can easily make a Scala.js "library" which is available to external Javascript as an API.
@@ -251,7 +251,7 @@
haoyi-mbp:temp haoyi$ du -h target/scala-2.11/example-opt.js
144K target/scala-2.11/example-opt.js
- @p
+ @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 Google Closure Compiler.
@hl.javascript("""
@@ -269,7 +269,7 @@
Even the fully-optimized version of our toy Scala.js app are pretty large. There are some factors that mitigate the large size of these executables:
@ul
- @li
+ @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 much larger ~2000 line application is only 288k.
@li
This size is pre-@a("gzip", href:="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.
@@ -293,17 +293,17 @@
If you've made it this far, you've downloaded, made modifications to, and published a toy Scala.js application. At the same time, we've gone over many of the key concepts in the Scala.js development process:
@ul
- @li
+ @li
Getting a Scala.js application
@li
Building it and seeing it work in the browser
- @li
+ @li
Made modifications to it to see it update
@li
Examined the source code to try and understand what it's doing
@li
Examined the output code, at two levels of optimization, to see how the Scala.js compiler works
- @li
+ @li
Packaged the application in a form that can be easily published online
@p
@@ -323,4 +323,4 @@
Try publishing the output code somewhere. You only need @code{example-opt.js} and @code{index-opt.html}; try putting them somewhere online where the world can see it.
@p
- When you're done poking around our toy web application, read on to the next chapter, where we will explore making something more meaty using the Scala.js toolchain!
+ When you're done poking around our toy web application, read on to the next chapter, where we will explore making something more meaty using the Scala.js toolchain!
diff --git a/book/src/main/scalatex/book/handson/PublishingModules.scalatex b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
index b329966..cad4647 100644
--- a/book/src/main/scalatex/book/handson/PublishingModules.scalatex
+++ b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
@@ -50,7 +50,7 @@
@p
In @code{Simple.scala} we have the shared, cross-platform API of our library: a single @hl.scala{object} with a single method @hl.scala{def} which does what we want, which can then be used in either Scala.js or Scala-JVM. In general, you can put as much shared logic here as you want: classes, objects, methods, anything that can run on both Javascript and on the JVM. We're chopping off the last 5 characters (the milliseconds) to keep the formatted dates slightly less verbose.
-
+
@p
However, when it comes to actually formatting the date, we have a problem: Javascript and Java provide different utilities for formatting dates! They both let you format them, but they provide different APIs. Thus, to do the formatting of each individual date, we call out to the @hl.scala{Platform.format} function, which is implemented twice: once in @code{js/} and once in @code{jvm/}:
@@ -62,14 +62,14 @@
@hl.ref("examples/crossBuilds/simple/jvm/src/main/scala/simple/Platform.scala")
@p
- In the @code{js/} version, we are using the Javascript @hl.javascript{Date} object to take the millis and do what we want. In the @code{jvm/} version, we instead use @hl.scala{java.text.SimpleDateFormat} with a custom formatter (The syntax is defined @a("here", href:="http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html")).
-
+ In the @code{js/} version, we are using the Javascript @hl.javascript{Date} object to take the millis and do what we want. In the @code{jvm/} version, we instead use @hl.scala{java.text.SimpleDateFormat} with a custom formatter (The syntax is defined @a("here", href:="http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html")).
+
@p
Again, you can put as much platform-specific logic in these files as you want, to account for differences in the available APIs. Maybe you want to use @hl.scala{js.JSON.parse} for parsing JSON blobs in @code{js/}, but @hl.scala{Jackson} or @hl.scala{GSON} for parsing them in @code{jvm/}.
@p
Lastly, you'll also have noticed the two identical @hl.scala{main} methods in the platform-specific code. This is an implementation detail around the fact that Scala.js picks up the main method differently from Scala-JVM, using @hl.scala{js.JSApp} instead of looking for a @hl.scala{main(args: Array[String]): Unit} method. These two main methods allow us to test our implementations
@sect{Running the Module}
- @hl.bash
+ @hl.bash
> ; js/run; jvm/run
[info] Running simple.Platform
Running on JS! 1
@@ -86,15 +86,15 @@
As you can see, both runs printed the same results, modulo three things:
@ul
- @li
- The "Running on XXX!" statement which shows us we're actually running on two platforms.
+ @li
+ The "Running on XXX!" statement which shows us we're actually running on two platforms.
@li
The other hint is the time taken: the second run is instant while the first takes three seconds! This is because by default we run on @a("Rhino", href:="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino"), which is a simple interpreter hundreds of times slower than running code natively on the JVM.
@li
In Scala-JVM the double 1.0 is printed as @code{1.0}, while in Scala.js it's printed as @code{1}. This is one of a small number of differences between Scala.js and Scala-JVM, and verifies that we are indeed running on both platforms!
@p
- You've by this point set up a basic cross-building Scala.js/Scala-JVM project!
+ You've by this point set up a basic cross-building Scala.js/Scala-JVM project!
@hr
@@ -106,7 +106,7 @@
Flesh it out! Currently this module only does a single, trivial thing. If you've done any web development before, I'm sure you can find some code snippet, function or algorithm that you'd like to share between client and server. Try implementing it in the @code{shared/} folder to be usable in both Scala.js and Scala-JVM
@li
Publish it! Both @code{sbt publishLocal} and @code{sbt publishSigned} work on this module, for publishing either locally, Maven Central via Sonatype, or Bintray. Running the command bare should be sufficient to publish both the @code{js} or @code{jvm} projects, or you can also specify which one e.g. @code{jvm/publishLocal} to publish only one subproject.
-
+
@p
This @code{jvm} project works identically to any other Scala-JVM project, and the @code{js} project works identically to the Command Line API described earlier. Thus you can do things like @code{fastOptStage::run} to run the code on Node.js, setting @hl.scala{requiresDOM := true}, run @code{fullOptStage::run} to run the code with full, aggressive optimizations. And of course, things that work in both Scala.js and Scala-JVM can be run on both, basic commands such as code{run} or @code{test}.
@@ -174,7 +174,7 @@
[success] Total time: 0 s, completed Nov 8, 2014 7:42:39 PM
@p
- And you'll see that we've run our unit tests twice: once on Scala.js in Rhino, and once on Scala-JVM! As expected, the first run in Rhino took much longer (4 seconds!) than the second, as Rhino is much slower than running code directly on the JVM. You can configure the Scala.js run to run in Node.js or PhantomJS, as well as running under different optimization levels.
+ And you'll see that we've run our unit tests twice: once on Scala.js in Rhino, and once on Scala-JVM! As expected, the first run in Rhino took much longer (4 seconds!) than the second, as Rhino is much slower than running code directly on the JVM. You can configure the Scala.js run to run in Node.js or PhantomJS, as well as running under different optimization levels.
@hr
diff --git a/book/src/main/scalatex/book/handson/WebPage.scalatex b/book/src/main/scalatex/book/handson/WebPage.scalatex
index c3f66a4..5415ec7 100644
--- a/book/src/main/scalatex/book/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/book/handson/WebPage.scalatex
@@ -41,7 +41,7 @@
@p
With that, the above snippet of code re-written using Scalatags looks as follows:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/HelloWorld1.scala")
@@ -56,7 +56,7 @@
The Scalatags github page has @a("comprehensive documentation", href:="https://github.com/lihaoyi/scalatags#hello-world") on how to express all manner of HTML fragments using Scalatags, so anyone who's familiar with how HTML works can quickly get up to speed. Instead of a detailed listing, we'll walk through some interactive examples to show Scalatags in action!
@sect{User Input}
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Inputs.scala", "val box")
@@ -76,7 +76,7 @@
@p
To begin with, let's define our list of items: Fruits!
-
+
@hl.ref("examples/demos/src/main/scala/webpage/Search0.scala", "val listings", "def")
@p
@@ -90,7 +90,7 @@
@p
Lastly, we just need to define the input box and output-container (as we did earlier), set the @hl.scala{onkeyup} event handler, and place it in a larger fragment, and then into our target:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Search0.scala", "val output")
@@ -101,7 +101,7 @@
@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:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Search1.scala", "def renderListings", "lazy val")
@@ -134,7 +134,7 @@
One half of the web application faces forwards towards the user, managing and rendering HTML or Canvas for the user to view and interact with. Another half faces backwards, talking to various web-services or databases which turn the application from a standalone-widget into part of a greater whole. We've already seen how to make the front half, let's now talk about working with the back half.
@sect{Raw Javascript}
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather0.scala", "val xhr")
@@ -142,12 +142,12 @@
@div(id:="div6")
@script("Weather0().main(document.getElementById('div6'))")
@p
- The above snippet of code uses the raw Javascript Ajax API in order to make a request to @a("openweathermap.org", href:="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 @a("here", href:="http://openweathermap.org/current"). For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
+ The above snippet of code uses the raw Javascript Ajax API in order to make a request to @a("openweathermap.org", href:="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 @a("here", href:="http://openweathermap.org/current"). For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
@p
As you can see, using the raw Javascript API to make the Ajax call looks almost identical to actually doing this in Javascript, shown below:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/resources/webpage/weather.js", "var xhr")
@@ -160,32 +160,32 @@
The primary syntactic differences are:
@ul
- @li
+ @li
@hl.scala{val}s for immutable data v.s. mutable @hl.javascript{var}s.
@li
@hl.scala("=>") v.s. @hl.javascript{function} to define the callback.
@li
- Scalatags' @hl.scala{pre} v.s. @hl.javascript{document.createElement}
+ Scalatags' @hl.scala{pre} v.s. @hl.javascript{document.createElement}
@p
Overall, they're pretty close, which is a common theme in Scala.js: using Javascript APIs in Scala.js is often as seamless and easy as using them in Javascript itself, and it often looks almost identical.
@sect{dom.extensions}
@p
- Although the Javascript XMLHttpRequest API is workable, it's kind of awkward and clunky compared to what you're used to in Scala. We create a half-baked object, set some magic properties, and call a magic function, which all has to be done in the correct order or it won't work.
+ Although the Javascript XMLHttpRequest API is workable, it's kind of awkward and clunky compared to what you're used to in Scala. We create a half-baked object, set some magic properties, and call a magic function, which all has to be done in the correct order or it won't work.
@p
With Scala.js, we provide a simpler API that is more clearly functional. First, you need to import some things into scope:
@hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "import dom", "val url =")
-
+
@p
The first import brings in Scala adapters to several DOM APIs, which allow you to use them more idiomatically from Scala. The second brings in an implicit @hl.scala{ExecutionContext} that we'll need to run our asynchronous operations.
@p
Then we need the code itself:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "val url")
@@ -203,7 +203,7 @@
@p
First, let's make the call prettyprint the document, so at least we can see what it contains:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather2.scala", "Ajax.get")
@@ -217,7 +217,7 @@
@p
Now that we've pretty-printed it, we can immediately see what data it contains and which part of the data we want. Let's change the previous example's @hl.scala{onSuccess} call to extract the @hl.scala{weather}, @hl.scala{temp} and @hl.scala{humidity} and put them in a nice, human-friendly format for us to enjoy:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather3.scala", "Ajax.get")
@@ -226,7 +226,7 @@
@script("Weather3().main(document.getElementById('div10'))")
@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.
+ 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.
@p
Calls on @hl.scala{js.Dynamic} resolve directly to javascript property/method references, and will fail at run-time with an exception if used wrongly. This is also why we need to call @hl.scala{.toString} or @hl.scala{.asInstanceOf}on the values before use: without these casts, the compiler can't be sure what kind of value is underneath the @hl.scala{js.Dynamic} type, and so we have to provide it the guarantee that it is what it needs.
@@ -254,12 +254,12 @@
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.
@div(id:="div11")
- @script("WeatherSearch().main(document.getElementById('div11'))")
+ @script("WeatherSearch().main(document.getElementById('div11'))")
@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.
-@sect{Interactive Web Pages Recap}
+@sect{Interactive Web Pages Recap}
@p
In this chapter, we've explored the basics of how you can use Scala.js to build interactive web pages. The two main contributions are using Scalatags to render HTML in a concise, safe way, and making Ajax calls to external web services. We combined these two capabilities in a small weather-search app that let a user interactively search for the weather in different cities around the world.
@p
@@ -272,6 +272,6 @@
Using @hl.scala{new dom.XMLHttpRequest} to make web requests feels just like the Javascript code to do so
@li
Using @hl.scala{Ajax.get(...)} and working with the resultant : @hl.scala{Future} feels a lot cleaner than directly using the Javascript API
-
+
@p
You're at this point reasonably pro
diff --git a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
index 7282ff1..f54efbf 100644
--- a/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
+++ b/book/src/main/scalatex/book/indepth/CompilationPipeline.scalatex
@@ -15,7 +15,7 @@
Scala.js often is delivered to many users over the network, and long download times force users to wait, degrading the user experience
@p
- These factors combined means that Scala.js has to put in extra effort to optimize the code to reduce it's size at compile-time.
+ These factors combined means that Scala.js has to put in extra effort to optimize the code to reduce it's size at compile-time.
@sect{Raw Verbosity}
@p
@@ -40,26 +40,26 @@
Scala.js applications often run in the browser. Not just any browser, but the browsers of your users, who had come to your website or web-app to try and accomplish some task. This is in stark contrast the Scala-JVM applications, which most often run on servers: servers that you own and control, and can deploy code to at your leisure.
@p
- When running code on your own servers in some data center, you often do not care how big the compiled code is: the Scala standard library is several (6-7) megabytes, which added to your own code and any third-party libraries you're using, may add up to tens of megabytes, maybe a hundred or two if it's a relatively large application. Even that pales in comparison to the size of the JVM, which weighs in the 100s of megabytes.
+ When running code on your own servers in some data center, you often do not care how big the compiled code is: the Scala standard library is several (6-7) megabytes, which added to your own code and any third-party libraries you're using, may add up to tens of megabytes, maybe a hundred or two if it's a relatively large application. Even that pales in comparison to the size of the JVM, which weighs in the 100s of megabytes.
@p
Even so, you are deploying your code on an machine (virtual or real) which has several gigabytes of memory and 100s of gigabytes of disk space. Even if the size of the code makes deployment slower, you only deploy fresh code a handful of times a day at most, and the size of your executable typically does not worry you.
@p
- Scala.js is different: it runs in the browsers of your users. Before it can run in their browser, it first has to be downloaded, probably over a connection that is much slower than the one used to deploy your code to your servers or data-center. It probably is downloaded thousands of times per day, and every user which downloads it must pay the cost of waiting for it to finish downloading before they can take any actions on your website.
+ Scala.js is different: it runs in the browsers of your users. Before it can run in their browser, it first has to be downloaded, probably over a connection that is much slower than the one used to deploy your code to your servers or data-center. It probably is downloaded thousands of times per day, and every user which downloads it must pay the cost of waiting for it to finish downloading before they can take any actions on your website.
@p
A typical website loads ~100kb-1mb of Javascript, and 1mb is on the heavy side. Most Javascript libraries weigh in on the order of 50-100kb. For Scala.js to be useful in the browser, it has to be able to compare favorably with these numbers.
-
+
@hr
-
+
@p
Thus, while on Scala-JVM you typically have executables that (including dependencies) end up weighing 10s to 100s of megabytes, Scala.js has a much tighter budget. A hello world Scala.js application weighs in at around 100kb, and as you write more code and use more libraries (and parts of the standard library) this number rises to the 100s of kb. This isn't tiny, especially compared to the many small Javascript libraries out there, but it definitely is much smaller than what you'd be used to on the JVM.
@sect{Whole Program Optimizaton}
@p
- At a first approximation, Scala.js achieves its tiny executables by using whole-program optimization. Scala-JVM, like Java, allows for separate compilation: this means that after compilation, you can combine your compiled code with code compiled separately, which can interact with the code you already compiled in an ad-hoc basis: code from both sides can call each others methods, instantiate each others classes, etc. without any limits.
+ At a first approximation, Scala.js achieves its tiny executables by using whole-program optimization. Scala-JVM, like Java, allows for separate compilation: this means that after compilation, you can combine your compiled code with code compiled separately, which can interact with the code you already compiled in an ad-hoc basis: code from both sides can call each others methods, instantiate each others classes, etc. without any limits.
@p
- Even things like package-private do not help you: Java packages are separate-compile-able too, and multiple compilation runs can dump things in the same package! You may think that private members and methods may be some salvation, but the Java ecosystem typically relies heavily on reflection, which depends on the fact that these private things remain exactly as-they-are.
+ Even things like package-private do not help you: Java packages are separate-compile-able too, and multiple compilation runs can dump things in the same package! You may think that private members and methods may be some salvation, but the Java ecosystem typically relies heavily on reflection, which depends on the fact that these private things remain exactly as-they-are.
@p
Overall, this makes it difficult to do any meaningful optimization: you never know whether or not you can eliminate a class, method or field. Even if it's not used anywhere you can see, it could easily be used by some other code compiled separately, or accessed through reflection.
@@ -76,8 +76,8 @@
@ul
@li
- @b{Initial Compilation}: @code{.scala} files to @code{.class} and @code{.sjsir} files
- @li
+ @b{Initial Compilation}: @code{.scala} files to @code{.class} and @code{.sjsir} files
+ @li
@b{Optimization}: @code{.sjsir} files to smallish/fast @code{.js} files
@li
@b{Closure-Compiler}: @code{.js} files to smaller/faster @code{.js} files
@@ -100,7 +100,7 @@
@sect{Initial Compilation}
@p
As described earlier, the Scala.js compiler is implemented as a Scala compiler plugin, and lives in the main repository in @a("compiler/", href:="https://github.com/scala-js/scala-js/tree/master/compiler"). The bulk of the plugin runs after the @code{mixin} phase in the @a("Scala compilation pipeline", href:="http://stackoverflow.com/a/4528092/871202"). By this point:
-
+
@ul
@li
Types and implicits have all been inferred
@@ -112,14 +112,14 @@
@hl.scala{trait}s have been @a("replaced by interfaces and classes", href:="http://stackoverflow.com/a/2558317/871202")
@p
- Overall, by the time the Scala.js compiler plugin takes action, most of the high-level features of the Scala language have already been removed. Compared to a hypothetical, alternative "from scratch" implementation, this approach has several advantages:
+ Overall, by the time the Scala.js compiler plugin takes action, most of the high-level features of the Scala language have already been removed. Compared to a hypothetical, alternative "from scratch" implementation, this approach has several advantages:
@ul
@li
It helps ensure that the semantics of these features always, 100% match that of Scala-JVM
@li
It reduces the amount of implementation work required by re-using the existing compilation phases
-
+
@p
This first phase is mostly a translation from the Scala compiler's internal AST to the Scala.js Intermediate Representation, and does not contain very many interesting optimizations. At the end of the initial compilation, the Scala compiler with Scala.js plugin results in two sets of files:
@@ -169,7 +169,7 @@
@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.
+ 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.
@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.
diff --git a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
index abdd655..24e77ae 100644
--- a/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
+++ b/book/src/main/scalatex/book/indepth/DesignSpace.scalatex
@@ -3,7 +3,7 @@
@sect("Why No Reflection?")
- @p
+ @p
Scala.js prohibits reflection as it makes dead-code elimination difficult, and the compiler relies heavily on dead-code elimination to generate reasonably-sized executables. The chapter on the Compilation Pipeline goes into more detail of why, but a rough estimate of the effect of various optimizations on a small application is:
@ul
@@ -11,13 +11,13 @@
@b{Full Output} - ~20mb
@li
@b{Naive Dead-Code-Elimnation} - ~800kb
- @li
+ @li
@b{Inlining Dead-Code-Elimnation} - ~600kb
@li
@b{Minified by Google Closure Compiler} - ~200kb
@p
- The default output size of 20mb makes the executables difficult to work with. Even though browsers can deal with 20mb Javascript blobs, it takes the browser several seconds to even load it, and up to a minute after that for the JIT to optimize the whole thing.
+ The default output size of 20mb makes the executables difficult to work with. Even though browsers can deal with 20mb Javascript blobs, it takes the browser several seconds to even load it, and up to a minute after that for the JIT to optimize the whole thing.
@sect{Dead Code Elimination}
@p
@@ -56,7 +56,7 @@
@sect{Whither Reflection?}
@p
To imagine why reflection makes this difficult, imagine a slightly modified program which includes some reflective calls in @hl.scala{App.main}
-
+
@hl.scala
@@JSExport
object App extends js.JSApp{
@@ -74,7 +74,7 @@
@p
Here, we're assuming @hl.scala{userInput()} is some method which returns a @hl.scala{String} that was input by the user or otherwise somehow decided at runtime.
@p
- We can start the same process: @hl.scala{App.main} is live since we @hl.scala{@@JSExport}ed it, but what objects or methods are reachable from @hl.scala{App.main}? The answer is: it depends on the values of @hl.scala{userInput()}, which we don't know. And hence we don't know which classes or methods are reachable! Depending on what @hl.scala{userInput()} returns, any or all methods and classes could be used by @hl.scala{App.main()}.
+ We can start the same process: @hl.scala{App.main} is live since we @hl.scala{@@JSExport}ed it, but what objects or methods are reachable from @hl.scala{App.main}? The answer is: it depends on the values of @hl.scala{userInput()}, which we don't know. And hence we don't know which classes or methods are reachable! Depending on what @hl.scala{userInput()} returns, any or all methods and classes could be used by @hl.scala{App.main()}.
@p
This leaves us a few options:
@@ -90,7 +90,7 @@
All three are possible options: Scala.js started off with #1. #3 is the approach used by @a("Proguard", href:="http://proguard.sourceforge.net/manual/examples.html#annotated"), which lets you annotate things e.g. @hl.scala{@@KeepApplication} to preserve things for reflection and preventing Proguard from eliminating them as dead code.
@p
- In the end, Scala.js chose #2. This is helped by the fact that overall, Scala code tends not to use reflection as heavily as Java, or dynamic languages which use it heavily. Scala uses techniques such as @a("lambdas", href:="http://docs.scala-lang.org/tutorials/tour/anonymous-function-syntax.html") or @a("implicits", href:="http://docs.scala-lang.org/tutorials/tour/implicit-parameters.html") to satisfy many use cases which Java has traditionally used reflection for, while friendly to the optimizer.
+ In the end, Scala.js chose #2. This is helped by the fact that overall, Scala code tends not to use reflection as heavily as Java, or dynamic languages which use it heavily. Scala uses techniques such as @a("lambdas", href:="http://docs.scala-lang.org/tutorials/tour/anonymous-function-syntax.html") or @a("implicits", href:="http://docs.scala-lang.org/tutorials/tour/implicit-parameters.html") to satisfy many use cases which Java has traditionally used reflection for, while friendly to the optimizer.
@p
There are a range of use-cases for reflection where you want to inspect an object's structure or methods, where lambdas or implicits don't help. People use reflection to @a("serialize objects", href:="http://jackson.codehaus.org/DataBindingDeepDive"), or for @a("routing messages to methods", href:="https://access.redhat.com/documentation/en-US/Fuse_ESB_Enterprise/7.1/html/Implementing_Enterprise_Integration_Patterns/files/BasicPrinciples-BeanIntegration.html"). However, both these cases can be satisfied by...
@@ -110,14 +110,14 @@
import upickle._
case class Thing(a: Int, b: String)
- write(Thing(1, "gg"))
+ write(Thing(1, "gg"))
// res23: String = {"a": 1, "b": "gg"}
@p
- Or to @a("route messages to the appropiate methods", href:="https://github.com/lihaoyi/autowire") without boilerplate, and @i{without} using reflection!
+ Or to @a("route messages to the appropiate methods", href:="https://github.com/lihaoyi/autowire") without boilerplate, and @i{without} using reflection!
@p
- The fact that you can satisfy these use cases with macros is non-obvious: in dynamic languages, macros only get an AST, which is basically opaque when you're only passing a single value to it. With Scala, you get the value @i{together with it's type}, which lets you inspect the type and generate the proper serialization/routing code that is impossible to do in a dynamic language with macros.
+ The fact that you can satisfy these use cases with macros is non-obvious: in dynamic languages, macros only get an AST, which is basically opaque when you're only passing a single value to it. With Scala, you get the value @i{together with it's type}, which lets you inspect the type and generate the proper serialization/routing code that is impossible to do in a dynamic language with macros.
@p
Using macros here also plays well with the Scala.js optimizer: the macros are fully expanded before the optimizer is run, so by the time the optimizer sees the code, there is no more magic left: it is then free to do dead-code-elimination/inlining/other-optimizations without worrying about reflection causing the code to do weird things at runtime. Thus, we've managed to substitute most of the main use-cases of reflection, and so can do without it.
@@ -140,12 +140,12 @@
@p
Scala.js works around this in the general case by adding a @hl.javascript{| 0} to the translation, e.g.
-
+
@hl.scala
/*JVM*/
15 / 4 // 3
@hl.javascript
- /*JS*/
+ /*JS*/
(15 / 4) | 0 // 3
@p
@@ -183,13 +183,13 @@
@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, 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.
-
-@sect("Why Jars instead of RequireJS/CommonJS")
- @p
+
+@sect("Why Jars instead of RequireJS/CommonJS")
+ @p
In JVM-land, the standard method for distributing these libraries is as @a("Maven Artifacts", href:="http://stackoverflow.com/questions/2487485/what-is-maven-artifact"). These are typically published in a public location such as @a("Maven Central", href:="http://search.maven.org/"), where others can find and download them for use. Typically downloads are done automatically by the build-tool: in Scala-JVM typically this is SBT.
@p
In Javascript-land, there are multiple ways of acquiring dependencies: @a("CommonJS", href:="http://en.wikipedia.org/wiki/CommonJS") and @a("RequireJS/AMD", href:="http://requirejs.org/") are two competing standards with a host of implementations. Historically, a third approach has been most common: the developer would simply download the modules himself, check it into source-control and manually add a @hl.html{<script>} tag to the HTML page that will make the functionality available through some global variable.
diff --git a/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex b/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
index 89f0a57..fe5c2df 100644
--- a/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
+++ b/book/src/main/scalatex/book/indepth/JavaAPIs.scalatex
@@ -4,18 +4,18 @@
There are many reasons you may want to port a Java class to Scala.js: you want to use it directly, you may be trying to port a library which uses it. In general, we haven't been porting things "for fun", and obscure classes like @hl.scala{org.omg.corba} will likely never be ported: we've been porting things as the need arises in order to support libraries (e.g. @a("Scala.Rx", href:="https://github.com/lihaoyi/scala.rx") that need them.
@sect{Available Java APIs}
-
+
@ul
- @for(data <- Book.javaAPIs)
+ @for(data <- BookData.javaAPIs)
@li
@a(data._1, href:=data._2)
-
+
@sect{Porting Java APIs}
@p
The process for making Java library classes available in Scala.js is relatively straightforward:
@ul
@li
- Find a class that you want to use in Scala.js, but is not implemented.
+ Find a class that you want to use in Scala.js, but is not implemented.
@li
Write a clean-room implementation in Scala, without looking at the source code of @a("OpenJDK", href:="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 @a("Harmony", href:="http://harmony.apache.org/")
@li
diff --git a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
index 3c89720..bbc86db 100644
--- a/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
+++ b/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
@@ -36,7 +36,7 @@
@hl.scala{2147483648} (> @hl.scala{Int.MaxValue}) matches @hl.scala{Float}, @hl.scala{Double}
@li
@hl.scala{1.2} matches @hl.scala{Float}, @hl.scala{Double}
- @p
+ @p
As a consequence, the following apparent subtyping relationship holds:
@hl.scala
@@ -111,7 +111,7 @@
Note that this also includes calls like
@hl.scala
val A,B,C,D = Value
- @p
+ @p
since they are desugared into separate 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.
@@ -133,11 +133,11 @@
@p
This table gives a quick overview of the sorts of libraries you can and can't use when working on Scala.js:
- @table
+ @table
@thead
@th{Can Use}@th{Can't Use}
@tbody
- @for(tuple <- Book.myTable)
+ @for(tuple <- BookData.myTable)
@tr
@td{@tuple._1}@td{@tuple._2}
@@ -145,11 +145,11 @@
We'll go into each section bit by bit
@sect{Standard Library}
- @table
+ @table
@thead
@th{Can Use}@th{Can't Use}
@tbody
- @for(tuple <- Book.myTable.slice(0, 3))
+ @for(tuple <- BookData.myTable.slice(0, 3))
@tr
@td{@tuple._1}@td{@tuple._2}
@@ -159,11 +159,11 @@
There isn't a full list of library APIs which are available from Scala.js, but a rough idea can be had from looking the the source code @a("on", href:="https://github.com/scala-js/scala-js/tree/master/javalanglib/src/main/scala/java/lang") @a("github", href:="https://github.com/scala-js/scala-js/tree/master/javalib/src/main/scala/java").
@sect{Reflection v.s. Macros}
- @table
+ @table
@thead
@th{Can Use}@th{Can't Use}
@tbody
- @for(tuple <- Book.myTable.slice(3, 4))
+ @for(tuple <- BookData.myTable.slice(3, 4))
@tr
@td{@tuple._1}@td{@tuple._2}
@@ -171,31 +171,31 @@
As described @a("here"), in Reflection is not supported in Scala.js, due to the way it inhibits optimization. This doesn't just mean you can't use reflection yourself: many third-party libraries also use reflection, and you won't be able to use them either.
@p
- On the other hand, Scala.js does support Macros, and macros can in many ways substitute many of the use cases that people have traditionally used reflection for. For example, instead of using a reflection-based serialization library like @code{scala-pickling}, you can use a macro-based library such as @code{upickle}.
+ On the other hand, Scala.js does support Macros, and macros can in many ways substitute many of the use cases that people have traditionally used reflection for. For example, instead of using a reflection-based serialization library like @code{scala-pickling}, you can use a macro-based library such as @code{upickle}.
@sect{Pure-Scala v.s. Java Libraries}
- @table
+ @table
@thead
@th{Can Use}@th{Can't Use}
@tbody
- @for(tuple <- Book.myTable.slice(4, 5))
+ @for(tuple <- BookData.myTable.slice(4, 5))
@tr
@td{@tuple._1}@td{@tuple._2}
@p
Scala.js has access to any pure-Scala libraries that you have cross-compiled to Scala.js, and cross-compiling a pure-Scala library with no dependencies is straightforward. Many of them, such as the ones listed above, have already been cross-compiled and can be used via their maven coordinates.
@p
- You cannot use any libraries which have a Java dependency. This means libraries like Scalatest or Scalate, which depend on a number of external Java libraries or source files, cannot be used from Scala.js. You can only use libraries which have no dependency on Java libraries or sources.
+ You cannot use any libraries which have a Java dependency. This means libraries like Scalatest or Scalate, which depend on a number of external Java libraries or source files, cannot be used from Scala.js. You can only use libraries which have no dependency on Java libraries or sources.
@sect{Javascript APIs v.s. JVM APIs}
- @table
+ @table
@thead
@th{Can Use}@th{Can't Use}
@tbody
- @for(tuple <- Book.myTable.slice(5, 7))
+ @for(tuple <- BookData.myTable.slice(5, 7))
@tr
@td{@tuple._1}@td{@tuple._2}
- @p
+ @p
Apart from depending on Java sources, the other thing that you can't use in Scala.js are JVM-specific APIs. This means that anything which goes down to the underlying operating system, filesystem, GUI or network are unavailable in Scala.js. This makes sense when you consider that these capabilities are no provided by the browser which Scala.js runs in, and it's impossible to re-implement them ourselves.
@p
In exchange for this, Scala.js provides you access to Browser APIs that do related things. Although you can't set up a HTTP server to take in-bound requests, you can make out-bound requests using @hl.scala{dom.XMLHttpRequest} to other servers. You can't write to the filesystem or databases directly, but you can write to the @hl.scala{dom.localStorage} provided by the browser. You can't use Swing or AWT or WebGL but instead work with the DOM and Canvas and WebGL.
@@ -204,18 +204,18 @@
@sect{Java tooling v.s. Scala/Browser tooling}
- @table
+ @table
@thead
@th{Can Use}@th{Can't Use}
@tbody
- @for(tuple <- Book.myTable.slice(7, 8))
+ @for(tuple <- BookData.myTable.slice(7, 8))
@tr
@td{@tuple._1}@td{@tuple._2}
-
+
@p
Lastly, there is the matter of tools. Naturally, all the Scala tools which depend on the JVM are out. This means things like the Yourkit, VisualVM and JProfiler profilers, as well as things like the Scala command-line REPL which relies on classloaders and other such things to run on the JVM
@p
On the other hand, you do get to keep and continue using many tools which are build for Scala but JVM-agnostic. For example, IDEs such a IntelliJ and Eclipse work great with Scala.js; from their point of view, it's just Scala, and things like code-navigation, refactoring and error-highlighting all work out of the box. SBT works with Scala.js too, and you see the same compile-erorrs in the command-line as you would in vanilla Scala, and even things like incremental compilation work un-changed.
@p
- Lastly, you gain access to browser tools that don't work with normal Scala: you can use the Chrome or Firefox consoles to poke at your Scala.js application from the command line, or their profilers/debuggers. With source maps set up, you can even step-through debug your Scala.js application directly in Chrome.
+ Lastly, you gain access to browser tools that don't work with normal Scala: you can use the Chrome or Firefox consoles to poke at your Scala.js application from the command line, or their profilers/debuggers. With source maps set up, you can even step-through debug your Scala.js application directly in Chrome.
diff --git a/scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala b/scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala
index 910639f..4dfc842 100755
--- a/scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala
+++ b/scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala
@@ -49,11 +49,10 @@ class ScalaTexPlugin(val global: Global) extends Plugin {
val shim = s"""
$pkgName
- import Book._
- import Utils.sect
+ import BookData._
import scalatags.Text.all._
object $objectName{
- val template = scalatex.twf("${file.getPath}")
+ def apply() = scalatex.twf("${file.getPath}")
}
"""
unit.body = global.newUnitParser(shim).parse()