Permalink
Browse files

Adding crossover support and examples.

  • Loading branch information...
1 parent 4128668 commit 91e8506585e5a43812c73e1fab747a40a9cda231 @emezeske committed Dec 31, 2011
View
@@ -3,3 +3,7 @@ pom.xml
lib
classes/
lein-cljsbuild-*.*.*
+example-projects/*/resources
+example-projects/*/classes
+example-projects/*/lib
+example-projects/*/src-cljs/example/crossover
View
153 README.md
@@ -7,9 +7,12 @@ that your project can depend on a specific version of lein-cljsbuild, fetch
it via `lein deps`, and you don't have to install any special executables into
your `PATH`.
+Also, this plugin has built-in support for seamlessly sharing code between
+your Clojure server-side project and your ClojureScript client-side project.
+
[1]: https://github.com/ibdknox/cljs-watch
-## Installation
+## Installation
You can install the plugin via lein:
@@ -19,21 +22,26 @@ Or by adding lein-cljs to your `project.clj` file in the `:dev-dependencies`
section:
```clojure
-(defproject my-thingie "1.2.3"
+(defproject lein-cljsbuild-example "1.2.3"
:dev-dependencies [[emezeske/lein-cljsbuild "0.0.1"]])
```
Make sure you pull down the jar file:
$ lein deps
+## Just Give Me a Damned Example Already!
+
+See the `example-projects` directory for a couple of simple examples
+of how to use lein-cljsbuild.
+
## Configuration
The lein-cljsbuild configuration is specified under the `:cljsbuild` section
-of your `project.clj` file:
+of your `project.clj` file. A simple project might look like this:
```clojure
-(defproject my-thingie "1.2.3"
+(defproject lein-cljsbuild-example "1.2.3"
:dev-dependencies [[emezeske/lein-cljsbuild "0.0.1"]]
:cljsbuild {
; The path to the top-level ClojureScript source directory:
@@ -59,6 +67,143 @@ avoids the time-consuming JVM startup for each build:
$ lein cljsbuild auto
+## Sharing Code Between Clojure and ClojureScript
+
+Sharing code with lein-cljsbuild is accomplished via the configuration
+of "crossovers". A crossover specifies a directory in your Clojure project,
+the content of which should be copied into your ClojureScript project. The
+files in the Clojure directory will be monitored and copied over when they are
+modified. Of course, remember that since the files will be used by both Clojure
+and ClojureScript, they will need to only use the subset of features provided by
+both languages.
+
+Assuming that your top-level directory structure looked something like this:
+
+<pre>
+├── src-clj
+│   └── example
+│   ├── core.clj
+│   ├── something.clj
+│   └── crossover
+│      ├── some_stuff.clj
+│      └── some_other_stuff.clj
+└── src-cljs
+    └── example
+    ├── core.cljs
+    ├── whatever.cljs
+    └── util.cljs
+</pre>
+
+And your `project.clj` file looked like this:
+
+```clojure
+(defproject lein-cljsbuild-example "1.2.3"
+ :dev-dependencies [[emezeske/lein-cljsbuild "0.0.1"]]
+ :source-path "src-clj"
+ :cljsbuild {
+ :source-dir "src-cljs"
+ :output-file "war/javascripts/main.js"
+ :optimizations :whitespace
+ :pretty-print true
+ ; Each entry in the :crossovers vector describes a directory
+ ; containing Clojure code that is meant to be used with the
+ ; ClojureScript code as well. Files in :from-dir are copied
+ ; into :to-dir whenever they are modified.
+ :crossovers [{:from-dir "src-clj/example/crossover"
+ :to-dir "src-cljs/example/crossover"}]})
+```
+
+Then lein-cljsbuild would copy files from `src-clj/example/crossover`
+to `src-cljs/example/crossover`, and you'd end up with this:
+
+<pre>
+├── src-clj
+│   └── example
+│   ├── a_file.clj
+│   ├── core.clj
+│   └── crossover
+│      ├── some_stuff.clj
+│      └── some_other_stuff.clj
+└── src-cljs
+    └── example
+    ├── a_different_file.cljs
+    ├── crossover
+    │   ├── some_stuff.cljs
+    │   └── some_other_stuff.cljs
+    ├── whatever.cljs
+    └── util.cljs
+</pre>
+
+With this setup, you would probably want to add `src-cljs/example/crossover`
+to your `.gitignore` file (or equivalent), as its contents are updated automatically
+by lein-cljsbuild.
+
+## Sharing Macros Between Clojure and ClojureScript
+
+In ClojureScript, macros are still written in Clojure, and can not be written
+in the same file as actual ClojureScript code. Also, to use them in a ClojureScript
+namespace, they must be required via `:require-macros` rather than the usual `:require`.
+
+This makes using the crossover feature to share macros between Clojure and ClojureScript
+a bit difficult, but lein-cljsbuild has some special constructs to make it possible.
+
+Three things need to be done to use lein-cljsbuild to share macros.
+
+### 1. Keep Macros in Separate Files
+
+These examples assume that your project uses the `src-clj/example/crossover`
+directory, and that all of the macros are in a file called
+`src-clj/example/crossover/macros.clj`.
+
+### 2. Tell lein-cljsbuild Which Files Contain Macros
+
+Add this magical comment to any crossover files that contain macros:
+
+```clojure
+;*CLJSBUILD-MACRO-FILE*;
+```
+
+This tells lein-cljsbuild to keep the `.clj` file extension when copying
+the files into the ClojureScript directory, instead of changing it to `.cljs`
+as usual.
+
+### 3. Use Black Magic to Require Macros Specially
+
+In any crossover Clojure file, lein-cljsbuild will automatically erase the
+following string (if it appears):
+
+```clojure
+;*CLJSBUILD-REMOVE*;
+```
+
+This magic can be used to generate a `ns` statement that will work in both
+Clojure and ClojureScript:
+
+```clojure
+(ns example.crossover.some_stuff
+ (:require;*CLJSBUILD-REMOVE*;-macros
+ [example.crossover.macros :as macros]))
+```
+
+Thus, after removing comments, Clojure will see:
+
+```clojure
+(ns example.crossover.some_stuff
+ (:require
+ [example.crossover.macros :as macros]))
+```
+
+However, lein-cljsbuild will remove the `;*CLJSBUILD-REMOVE*;` string entirely,
+before copying the file. Thus, ClojureScript will see:
+
+```clojure
+(ns example.crossover.some_stuff
+ (:require-macros
+ [example.crossover.macros :as macros]))
+```
+
+And thus the macros can be shared.
+
## License
Source Copyright © Evan Mezeske, 2011.
@@ -0,0 +1,18 @@
+This is an example web application that uses [Ring][1], [Compojure][2],
+and [lein-cljsbuild][3]. It demonstrates the use of lein-cljsbuild to
+build ClojureScript into JavaScript. It also shows how to share code
+between Clojure and ClojureScript, including macros.
+
+To play around with this example project, you will first need
+[Leiningen][4] installed.
+
+Set up and start the server like this:
+
+ lein deps
+ lein cljsbuild once
+ lein ring server
+
+[1]: https://github.com/mmcgrana/ring
+[2]: https://github.com/weavejester/compojure
+[3]: https://github.com/emezeske/lein-cljsbuild
+[4]: https://github.com/technomancy/leiningen
@@ -0,0 +1,15 @@
+(defproject cljsbuild-example-advanced "0.0.1"
+ :description "An advanced example of how to use lein-cljsbuild"
+ :source-path "src-clj"
+ :dependencies [[org.clojure/clojure "1.3.0"]
+ [compojure "0.6.5"]
+ [hiccup "0.3.7"]]
+ :dev-dependencies [[emezeske/lein-cljsbuild "0.0.1"]
+ [lein-ring "0.5.0"]]
+ :cljsbuild {:source-dir "src-cljs"
+ :crossovers [{:from-dir "src-clj/example/crossover"
+ :to-dir "src-cljs/example/crossover"}]
+ :output-file "resources/public/js/main.js"
+ :optimizations :whitespace
+ :pretty-print true}
+ :ring {:handler example.routes/app})
@@ -0,0 +1,6 @@
+;*CLJSBUILD-MACRO-FILE*;
+
+(ns example.crossover.macros)
+
+(defmacro reverse-eval [form]
+ (reverse form))
@@ -0,0 +1,7 @@
+(ns example.crossover.shared
+ (:require;*CLJSBUILD-REMOVE*;-macros
+ [example.crossover.macros :as macros]))
+
+(defn make-example-text []
+ (macros/reverse-eval
+ ("code" "shared " "from the " "Hello " str)))
@@ -0,0 +1,16 @@
+(ns example.routes
+ (:use compojure.core
+ example.views
+ [hiccup.middleware :only (wrap-base-url)])
+ (:require [compojure.route :as route]
+ [compojure.handler :as handler]
+ [compojure.response :as response]))
+
+(defroutes main-routes
+ (GET "/" [] (index-page))
+ (route/resources "/")
+ (route/not-found "Page not found"))
+
+(def app
+ (-> (handler/site main-routes)
+ (wrap-base-url)))
@@ -0,0 +1,13 @@
+(ns example.views
+ (:require
+ [example.crossover.shared :as shared])
+ (:use
+ [hiccup core page-helpers]))
+
+(defn index-page []
+ (html5
+ [:head
+ [:title (shared/make-example-text)]
+ (include-js "/js/main.js")]
+ [:body
+ [:h1 (shared/make-example-text)]]))
@@ -0,0 +1,5 @@
+(ns example.hello
+ (:require
+ [example.crossover.shared :as shared]))
+
+(js/alert (shared/make-example-text))
@@ -0,0 +1,17 @@
+This is an example web application that uses [Ring][1], [Compojure][2],
+and [lein-cljsbuild][3]. It demonstrates the use of lein-cljsbuild to
+build ClojureScript into JavaScript.
+
+To play around with this example project, you will first need
+[Leiningen][4] installed.
+
+Set up and start the server like this:
+
+ lein deps
+ lein cljsbuild once
+ lein ring server
+
+[1]: https://github.com/mmcgrana/ring
+[2]: https://github.com/weavejester/compojure
+[3]: https://github.com/emezeske/lein-cljsbuild
+[4]: https://github.com/technomancy/leiningen
@@ -0,0 +1,13 @@
+(defproject cljsbuild-example-simple "0.0.1"
+ :description "A simple example of how to use lein-cljsbuild"
+ :source-path "src-clj"
+ :dependencies [[org.clojure/clojure "1.3.0"]
+ [compojure "0.6.5"]
+ [hiccup "0.3.7"]]
+ :dev-dependencies [[emezeske/lein-cljsbuild "0.0.1"]
+ [lein-ring "0.5.0"]]
+ :cljsbuild {:source-dir "src-cljs"
+ :output-file "resources/public/js/main.js"
+ :optimizations :whitespace
+ :pretty-print true}
+ :ring {:handler example.routes/app})
@@ -0,0 +1,16 @@
+(ns example.routes
+ (:use compojure.core
+ example.views
+ [hiccup.middleware :only (wrap-base-url)])
+ (:require [compojure.route :as route]
+ [compojure.handler :as handler]
+ [compojure.response :as response]))
+
+(defroutes main-routes
+ (GET "/" [] (index-page))
+ (route/resources "/")
+ (route/not-found "Page not found"))
+
+(def app
+ (-> (handler/site main-routes)
+ (wrap-base-url)))
@@ -0,0 +1,10 @@
+(ns example.views
+ (:use [hiccup core page-helpers]))
+
+(defn index-page []
+ (html5
+ [:head
+ [:title "Hello World"]
+ (include-js "/js/main.js")]
+ [:body
+ [:h1 "Hello World"]]))
@@ -0,0 +1,3 @@
+(ns example.hello)
+
+(js/alert "Hello from ClojureScript!")
Oops, something went wrong.

0 comments on commit 91e8506

Please sign in to comment.