Scala macro that produces JavaScript from Scala code. Let it be type safe!
- Variable definitions, basic unary and binary operations
 - Named and anonymous functions
 - Scala Arrays/Seq as JavaScript Array literals
 - Scala Map and anonymous classes as JavaScript object
 - if, while, for..in and for statements
 - Scala if as an expression (e.g. val a = if (true) 1 else 2)
 - Scala match as JavaScript switch
 - Basic Scala class/trait definition to JavaScript object definition translation
 - Global JavaScript functions (parseInt etc)
 - Basic Browser objects (window, history, location etc)
 - Basic HTML DOM objects (Document, Element, Attribute, Node, NodeList etc)
 - Raw JavaScript inclusion
 - Values and function call injections from your Scala code
 - Generated JavaScript eval using Java ScriptEngine
 - Pretty printing and compression using YUI compressor
 - Basic @Javascript macro annotation support
 - Basic toJson/fromJson macros
 - @Typescripted annotation
 
This Scala code has no meaning but shows basic ideas:
val replacement = "text"
val js = javascript {
  window.setTimeout(() => {
    val r = new RegExp("d.*", "g")
    class Point(val x: Int, val y: Int)
    val point = new Point(1, 2)
    def func(i: String) = r.exec(i)
    val list = document.getElementById("myList2")
    val map = collection.mutable.Map[String, String]()
    if (typeof(map) == "string") {
      for (idx <- 0 until list.attributes.length) {
        val attr = list.attributes.item(idx).asInstanceOf[Attribute]
        map(attr.name) = func(attr.textContent).as[String]
      }
    } else {
      val obj = new {
        val field = 1
        def func2(i: Int) = "string"
      }
      val links = Array("https://github.com/nau/scala")
      for (link <- links) {
        include("var raw = 'JavaScript'")
        console.log(link + obj.func2(obj.field) + point.x)
      }
      window.location.href = links(0).replace("scala", "jscala")
    }
  }, 1000)
}
println(js.asString)It will print
window.setTimeout(function () {
    var r = new RegExp("d.*", "g");
    function Point(x, y) {
      this.x = x;
      this.y = y;
    };
    var point = new Point(1, 2);
    function func(i) {
      return r.exec(i);
    };
    var list = document.getElementById("myList2");
    var map = {};
    if (typeof(map) == "string") for (var idx = 0; idx < list.attributes.length; ++idx) {
      var attr = list.attributes.item(idx);
      map[attr.name] = func(attr.textContent);
    } else {
      var obj = {
        field: 1,
        func2: function (i) {
          return "string";
        }
      };
      var links = ["https://github.com/nau/scala"];
      for (var linkIdx = 0, link = links[linkIdx]; linkIdx < links.length; link = links[++linkIdx]) {
        var raw = 'JavaScript';
        console.log((link + obj.func2(obj.field)) + point.x);
      };
      window.location.href = links[0].replace("scala", "jscala");
    };
  }, 1000)In your build.sbt add
scalaVersion := "2.10.3"
libraryDependencies += "org.jscala" %% "jscala-macros" % "0.3"
If you want to try the latest snapshot:
scalaVersion := "2.10.3"
resolvers += Resolver.sonatypeRepo("snapshots")
libraryDependencies += "org.jscala" %% "jscala-macros" % "0.4-SNAPSHOT"
In your code
import org.jscala._
val js = javascript { ... }
println(js.asString)
println(js.compress)
println(js.eval())That's it!
In your build.sbt add
scalaVersion := "2.10.3"
resolvers += Resolver.sonatypeRepo("snapshots")
addCompilerPlugin("org.scala-lang.plugins" % "macro-paradise_2.10.3-RC2" % "2.0.0-SNAPSHOT")
libraryDependencies += "org.jscala" %% "jscala-macros" % "0.3"
libraryDependencies += "org.jscala" %% "jscala-annots" % "0.3"
In your code
import org.jscala._
@Javascript class User(val name: String, val id: Int)
@Javascript(json = false) class Greeter {
  def hello(u: User) {
    print("Hello, " + u.name + "\n")
  }
}
// Run on JVM
val u1 = new User("Alex", 1)
val greeter = new Greeter()
greeter.hello(u1) // prints "Hello, Alex"
val json = u1.js.json.asString
val main = javascript {
    val u = new User("nau", 2)
    val u1Json = eval("(" + inject(json) + ")").as[User] // read User from json string generated above
    val t = new Greeter()
    t.hello(u)
    t.hello(u1Json)
  }
val js = User.jscala.javascript ++ Greeter.jscala.javascript ++ main // join classes definitions with main code
js.eval() // run using Rhino
println(js.asString) // prints resulting JavaScriptRun it and you'll get
Hello, Alex
Hello, nau
Hello, Alex
{
  function User(name, id) {
    this.name = name;
    this.id = id;
  };
  function Greeter() {
    this.hello = function (u) {
      print(("Hello, " + u.name) + "\n");
    };
  };
  var u = new User("nau", 2);
  var u1Json = eval(("(" + "{\n  \"name\": \"Alex\",\n  \"id\": 1\n}") + ")");
  var t = new Greeter();
  t.hello(u);
  t.hello(u1Json);
}See AES example:
https://github.com/nau/jscala/blob/master/jscala-examples/src/main/scala/org/jscalaexample/AES.scala
It's AES Scala implementation which is used for both Scala and JavaScript encryption/decryption.
Make sure you have at least -Xmx750Mb for your sbt. Don't know why but it takes up to 700Mb to compile jscala-macros project.
In sbt shell run tetris task.
It will compile and generate tetris.js file in jscala-examples/javascript-tetris and open Tetris game in your browser.
Tetris is fully written in Scala and translates to JavaScript mostly literally.
Tetris sources are here: jscala-examples/src/main/scala/org/jscalaexample/Tetris.scala
- Language support improvements
 - Web frameworks support: Play, Lift
 
Any feedback is very welcome!
You can use JScala mailing list if you have any questions.
Or simply ask me on Twitter: @atlanter