Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Rhino based compilation of Coffeescript for Coffeescript filter #6

Merged
merged 1 commit into from over 2 years ago

2 participants

Dag Liodden James Strachan
Dag Liodden
Collaborator

Quick win for server based compilation (based on https://github.com/daggerrz/scala-coffee), but I have a few open questions that need to be answered before merging:

  • How to get access to line number information to give user a more informative error message on Coffee compilation error
  • Is there any reason to keep the browser based compilation at all (even when in dev-mode)?
  • How to handle the Rhino dep. Move to separate module?
Dag Liodden
Collaborator

Open questions:

  • How to get access to line number information to give user a more informative error message on Coffee compilation error
  • Is there any reason to keep the browser based compilation at all (even when in dev-mode)?
  • How to handle the Rhino dep. Move to separate module?
James Strachan jstrachan merged commit 1c73f37 into from September 08, 2011
James Strachan jstrachan closed this September 08, 2011
James Strachan
Owner
James Strachan
Owner

An optional dependency on rhino sounds fine to me; added an enable/disable flag too. Still pondering the line number issue...

Dag Liodden
Collaborator

Sounds good, most will probably opt to use the compiler once it's there and for those who don't want the extra dep, a flag should solve their problems. Line numbers would be really useful, but it looks like that will require some refactoring of the Scalate pipeline. I'm not familiar enough with the source to make any suggestions, though...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Sep 08, 2011
Dag Liodden Coffeescript compilation support for coffeescript filter 978df58
This page is out of date. Refresh to see the latest.
8  scalate-core/pom.xml
@@ -92,6 +92,14 @@
92 92
       <optional>true</optional>
93 93
     </dependency>
94 94
 
  95
+    <!-- Coffescript compiler -->
  96
+    <dependency>
  97
+        <groupId>rhino</groupId>
  98
+        <artifactId>js</artifactId>
  99
+        <version>1.7R2</version>
  100
+        <optional>true</optional>
  101
+    </dependency>
  102
+
95 103
     <!-- testing -->
96 104
     <dependency>
97 105
       <groupId>org.scalatest</groupId>
8  scalate-core/src/main/resources/org/fusesource/scalate/filter/coffee-script.js
8 additions, 0 deletions not shown
79  scalate-core/src/main/scala/org/fusesource/scalate/filter/CoffeeScriptFilter.scala
@@ -19,6 +19,8 @@ package org.fusesource.scalate
19 19
 package filter
20 20
 
21 21
 import org.fusesource.scalate.support.RenderHelper
  22
+import org.mozilla.javascript._
  23
+import java.io.InputStreamReader
22 24
 
23 25
 /**
24 26
  * Surrounds the filtered text with &lt;script&gt; and CDATA tags.
@@ -31,16 +33,71 @@ object CoffeeScriptFilter extends Filter {
31 33
 
32 34
   def filter(context: RenderContext, content: String) = {
33 35
 
34  
-    // TODO: 'if( !context.engine.isDevelopmentMode )' then
35  
-    // we should try to compile the coffescript on the server
36  
-    // side and cache it.
  36
+    if( !context.engine.isDevelopmentMode )
  37
+      Compiler.compile(content, Some(context.currentTemplate)).fold({ error =>
  38
+        throw new CompilerException(error.message, Nil)
  39
+      }, { coffee =>
  40
+        """<script type='text/javascript'>
  41
+           |  #<![CDATA[
  42
+           |    """.stripMargin+RenderHelper.indent("    ", coffee)+"""
  43
+           |  #]]>
  44
+           |</script>""".stripMargin
  45
+      })
  46
+    else {
  47
+      context.attributes("REQUIRES_COFFEE_SCRIPT_JS") = "true"
  48
+      """<script type='text/coffeescript'>
  49
+         |  #<![CDATA[
  50
+         |    """.stripMargin+RenderHelper.indent("    ", content)+"""
  51
+         |  #]]>
  52
+         |</script>""".stripMargin
  53
+    }
  54
+  }
  55
+
  56
+  /**
  57
+   * A Scala / Rhino Coffeescript compiler.
  58
+   */
  59
+  object Compiler {
  60
+
  61
+    /**
  62
+     * Compiles a string of Coffeescript code to Javascript.
  63
+     *
  64
+     * @param code the Coffeescript code
  65
+     * @param sourceName a descriptive name for the code unit under compilation (e.g a filename)
  66
+     * @param bare if true, no function wrapper will be generated
  67
+     * @return the compiled Javascript code
  68
+     */
  69
+    def compile(code: String, sourceName: Option[String] = None, bare : Boolean = false)
  70
+      : Either[CompilationError, String] = withContext { ctx =>
  71
+      val scope = ctx.initStandardObjects()
  72
+      ctx.evaluateReader(
  73
+        scope,
  74
+        new InputStreamReader(getClass.getResourceAsStream("coffee-script.js"), "UTF-8"),
  75
+        "coffee-script.js", 1, null
  76
+      )
  77
+
  78
+      val coffee = scope.get("CoffeeScript", scope).asInstanceOf[NativeObject]
  79
+      val compileFunc = coffee.get("compile", scope).asInstanceOf[Function]
  80
+      val opts = ctx.evaluateString(scope, "({bare: %b});".format(bare), null, 1, null)
37 81
 
38  
-    context.attributes("REQUIRES_COFFEE_SCRIPT_JS") = "true"
39  
-    """<script type='text/coffeescript'>
40  
-       |  #<![CDATA[
41  
-       |    """.stripMargin+RenderHelper.indent("    ", content)+"""
42  
-       |  #]]>
43  
-       |</script>""".stripMargin
  82
+      try {
  83
+        Right(compileFunc.call(ctx, scope, coffee, Array(code, opts)).asInstanceOf[String])
  84
+      } catch {
  85
+        case e : JavaScriptException =>
  86
+          Left(CompilationError(sourceName, e.getValue.toString))
  87
+      }
  88
+    }
  89
+
  90
+    def withContext[T](f: Context => T): T = {
  91
+      val ctx = Context.enter()
  92
+      try {
  93
+        ctx.setOptimizationLevel(-1) // Do not compile to byte code (max 64kb methods)
  94
+        f(ctx)
  95
+      } finally {
  96
+        Context.exit()
  97
+      }
  98
+    }
44 99
   }
45  
-  
46  
-}
  100
+
  101
+  case class CompilationError(sourceName: Option[String], message: String)
  102
+}
  103
+
2  scalate-core/src/test/resources/org/fusesource/scalate/scaml/coffee.scaml
... ...
@@ -0,0 +1,2 @@
  1
+:coffeescript
  2
+  alert "Hello, Coffee!"
18  scalate-core/src/test/scala/org/fusesource/scalate/filter/CoffeeScriptFilterTest.scala
... ...
@@ -0,0 +1,18 @@
  1
+package org.fusesource.scalate.filter
  2
+
  3
+import org.fusesource.scalate.TemplateTestSupport
  4
+
  5
+class CoffeeScriptFilterTest extends TemplateTestSupport {
  6
+  test("coffeescript filter") {
  7
+    assertUriOutputContains("/org/fusesource/scalate/scaml/coffee.scaml", """<script type='text/javascript'>
  8
+  #<![CDATA[
  9
+    (function() {
  10
+      alert("Hello, Coffee!");
  11
+    }).call(this);
  12
+
  13
+  #]]>
  14
+</script>
  15
+""")
  16
+  }
  17
+
  18
+}
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.