Axion5

The official example template of creating a blog with Bootstrap.

Installing and using Clojure and ClojureScript with Emacs
comments: true
published: true
title: Installing and using Clojure and ClojureScript with Emacs
tags: Emacs Clojure ClojureScript install
publishedat: 10 Nov 2014

Clojure is wonderful, being able to use ClojureScript in the browser even more so, however setting up a development environment is a pain in the ass. On top of this what's considered best practice is still changing from almost month to month.

Because of this I intend to document here as accurately as I can the process for getting a working environment in Emacs for both Clojure and ClojureScript. This is going to be a working document where I will update the procedure I use myself on an ongoing basis. In general I will be noting instructions to install the most recent verions of software and scripts. Mostly this is a description of how, not so much of why, but I'll try to include links to more info where relevant.

-------------

Much of the following is freely copied and modified from other tutorials on the net. A list of resources is at the bottom of the page.

0.1Quick setup

I've made a template that sets up what's discussed below with as little effort as possible.

First install Clojure and Leiningen as described below.

Create a new app from the template:

lein new mjstarter foobar

Start the figwheel and nrepl server:

cd foobar
lein cooper

Alternatively start up figwheel separately: lein figwheel and lein repl.

Give it a minute.

Add the following function to Emacs:

(defun mjstarter ()
  "Cider-connect to localhost:15123 and start weasel repl"
  (interactive)
  (cider-connect "localhost" 15123)
  (cider-insert-in-repl "(require 'mjstarter.core) (http://mjstarter.core/piggie)" t))

Make sure that in ~/.lein/profiles.clj is the following:

{:user {:plugins [[cider/cider-nrepl "0.9.0-SNAPSHOT"]]}}

Then:

M-x mjstarter

Then navigate in a browser to http://localhost:3449/index.html

Now you have a ClojureScript repl in Emacs into the browser's JavaScript runtime.

Edit the cljs files in src-cljs and the css files in resources/public. Browser updates without refreshing.

To use a separate server for the app, uncomment line 21 in src-cljs/[project-name]/core.cljs:

;; :websocket-url "ws://{{figwheel-host}}:{{figwheel-port}}/figwheel-ws" 

Then start a server in resources/public.

0.2Clojure

Clojure needs Java installed. So java -version should return something like:
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

Minimum required version is 1.6, see here.

In a freshly installed ubuntu 14.4 :

sudo apt-get update 
# to make sure apt-add-repository is installed:
sudo apt-get install software-properties-common 
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer 

You do not need to install Clojure as such, it's just a jar file. To take care of compiling Clojure programs and general project management there is leiningen.

0.3Leiningen

From wikipedia :

Leiningen is a build automation and dependency management tool for the simple configuration of software projects written in the Clojure programming language.

Leiningen's features can be extended via a plugin system, and it is supplied with a command line interface that can initiate a number of actions, which include:

  • The generation of a simple Clojure project skeleton
  • Packaging of project code and dependencies into an 'Uberjar' .jar file
  • Start an interactive REPL that has the classpath correctly set to load projectdependencies
  • Ahead-of-time (AOT) compilation,
  • Dependency resolution (with automatic library downloading)

And of course it runs your Clojure program if it's not a library. However, running a Clojure program involves automatically booting up a JVM first. Since this can take a few seconds Clojure programs are more easily tested and developed using a repl in a JVM booted and kept running for this purpose. See further below. A repl enables an instant feedback loop to the developer.

To install Leiningen download the lein script

wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein 

Place it on your $PATH where your shell can find it (eg. ~/bin)

sudo cp lein ~/bin

Set it to be executable

chmod a+x ~/bin/lein 

Run it and it will download the self-install package

lein 
> ....
> Downloading Leiningen to /home/michieljoris/.lein/self-installs/leiningen-2.5.0-standalone.jar now...
> ....

Following is a list of most used options:

Leiningen is a tool for working with Clojure projects.
Several tasks are available:
version             Print version for Leiningen and the current JVM.
upgrade             Upgrade Leiningen to specified version or latest stable.
new                 Generate project scaffolding based on a template.
run                 Run a -main function with optional command-line arguments.
uberjar             Package up the project files and dependencies into a jar file.
repl                Start a repl session either with the current project or standalone.
search              Search remote maven repositories for matching jars.

clean               Remove all files from project's target-path.
deps                Download all dependencies.

Run `lein help $TASK` for details.

Generate a simple Clojure project skeleton:

lein new app some-app-name
| .gitignore
| doc
| | intro.md
| LICENSE
| project.clj
| resources
| README.md
| src
| | some_app_name
| | | core.clj
| test
| | some_app_name
| | | core_test.clj
cd some-app-name
lein run

Lein will retrieve some dependencies as specified in project.clj, compile the project and then print:

Hello, World!

Run it again:

lein run

and it'll only have to start the jvm and compile and then prints the same.

Build a standalone jar:

lein uberjar

And then run it with:

java -jar target/uberjar/some-app-name-0.1.0-SNAPSHOT-standalone.jar 

Run a repl in the project:

lein repl

And then at the prompt:

some-app-name.core=> (-main)
Hello, World!
nil
some-app-name.core=> (exit)
Bye for now!

Search for a clojar with:

lein search clojar-name

This will take some time the first time.

A very useful tutorial on leiningen is here. Some interesting sections are checkouts (similar to npm link) and profiles.

Leiningen is configured by the project.clj file in your project directory. For an annotated reference of all of the options that may be set in a project.clj file see here. A sample project.clj follows as used in the rest of this post, uncomment the relevant sections as needed.

(defproject some-app "0.1.0"
  :description "FIXME: write description"
  :url "http://example.com/FIXME";
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  ;; Warns users of earlier versions of Leiningen. Set this if your project
  ;; relies on features only found in newer Leiningen versions.
  :min-lein-version "2.0.0"

  :dependencies [[org.clojure/clojure "1.7.0-beta3"]
                 [org.clojure/clojurescript "0.0-3269"]
                 ]

  ;; Profiles
  ;; Each active profile gets merged into the project map. The :dev
  ;; and :user profiles are active by default, but the latter should be
  ;; looked up in ~/.lein/profiles.clj rather than set in project.clj.
  ;; Use the with-profiles higher-order task to run a task with a
  ;; different set of active profiles.
  ;; See `lein help profiles` for a detailed explanation.
  :profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.0"]
                                  [org.clojure/tools.nrepl "0.2.10"]
                                  [weasel "0.6.0"]
                                  [figwheel "0.3.1"] 
                                  ]
                   :repl-options { ;; Specify the string to print when prompting for input.
                                  ;; defaults to something like (fn [ns] (str *ns* "=> "))
                                  ;; :prompt (fn [ns] (str "your command for <" ns ">, master? " ))
                                  ;; What to print when the repl session starts.
                                  ;; :welcome (println "Welcome to the magical world of the repl!")
                                  ;; Specify the ns to start the REPL in (overrides :main in
                                  ;; this case only)
                                  ;; :init-ns foo.bar
                                  ;; This expression will run when first opening a REPL, in the
                                  ;; namespace from :init-ns or :main if specified.
                                  :init (println "Here we are in" *ns*)
                                  ;; Print stack traces on exceptions (highly recommended, but
                                  ;; currently overwrites *1, *2, etc).
                                  ;; :caught clj-stacktrace.repl/pst+
                                  ;; Skip's the default requires and printed help message.
                                  :skip-default-init false
                                  ;; Customize the socket the repl task listens on and
                                  ;; attaches to.
                                  :host "127.0.0.1"
                                  :port 15123
                                  ;;for more options see the sample project.clj
                                  :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
                   ;; :uberjar {:aot :all}
                   }
             }
  ;; Plugins are code that runs in Leiningen itself and usually
  ;; provide new tasks or hooks.
  :plugins [
            [lein-cljsbuild "1.0.6-SNAPSHOT"]
            [lein-figwheel "0.3.1"]
            [cider/cider-nrepl "0.9.0-SNAPSHOT"] ;; or add to ~/.lein/profiles.clj
            [lein-cooper "1.1.1"]
            ]

  ;;; Entry Point
  ;; The -main function in this namespace will be run at launch
  ;; (either via `lein run` or from an uberjar). It should be variadic:
  ;;
  ;; (ns my.service.runner
  ;; (:gen-class))
  ;;
  ;; (defn -main
  ;; "Application entry point"
  ;; [& args]
  ;; (comment Do app initialization here))
  ;;
  :main some-app.core
  
  ;;; Filesystem Paths
  ;; If you'd rather use a different directory structure, you can set these.
  ;; Paths that contain "inputs" are string vectors, "outputs" are strings.
  :source-paths ["src-clj"]
  ;; :java-source-paths ["src/main/java"] ; Java source is stored separately.
  ;; :test-paths ["test" "src/test/clojure"]
  ;; :resource-paths ["src/main/resource"] ; Non-code files included in classpath/jar.
  ;; All generated files will be placed in :target-path. In order to avoid
  ;; cross-profile contamination (for instance, uberjar classes interfering
  ;; with development), it's recommended to include %s in in your custom
  ;; :target-path, which will splice in names of the currently active profiles.
  ;; :target-path "target/%s/"
  ;; Directory in which to place AOT-compiled files. Including %s will
  ;; splice the :target-path into this value.
  ;; :compile-path "%s/classy-files"
  ;; Directory in which to extract native components from inside dependencies.
  ;; Including %s will splice the :target-path into this value. Note that this
  ;; is not where to *look* for existing native libraries; use :jvm-opts with
  ;; -Djava.library.path=... instead for that.
  ;; :native-path "%s/bits-n-stuff"
  ;; Directories under which `lein clean` removes files.
  ;; Specified by keyword or keyword-chain to get-in path in this defproject.
  ;; Both a single path and a collection of paths are accepted as each.
  ;; For example, if the other parts of project are like:
  ;; :target-path "target"
  ;; :compile-path "classes"
  ;; :foobar-paths ["foo" "bar"]
  ;; :some-app-config {:qux-path "qux"}
  ;; :clean-targets below lets `lein clean` remove files under "target",
  ;; "classes", "foo", "bar", "qux", and "out".
  ;; By default, will protect paths outside the project root and within standard
  ;; lein source directories ("src", "test", "resources/public", "doc", "project.clj").
  ;; However, this protection can be overridden with metadata on the :clean-targets
  ;; vector - ^{:protect false}
  ;; :clean-targets [:target-path :compile-path :foobar-paths
  ;;                 [:some-app-config :qux-path] "out"]
  ;; Workaround for http://dev.clojure.org/jira/browse/CLJ-322 by deleting
  ;; compilation artifacts for namespaces that come from dependencies.
  ;; :clean-non-project-classes true
  ;; Paths to include on the classpath from each project in the
  ;; checkouts/ directory. (See the FAQ in the Readme for more details
  ;; about checkout dependencies.) Set this to be a vector of
  ;; functions that take the target project as argument. Defaults to
  ;; [:source-paths :compile-path :resource-paths], but you could use
  ;; the following to share code from the test suite:
  ;; :checkout-deps-shares [:source-paths :test-paths
  ;;                        ~(fn [p] (str (:root p) "/lib/dev/*"))]

  
  ;; All generated files will be placed in :target-path. In order to avoid
  ;; cross-profile contamination (for instance, uberjar classes interfering
  ;; with development), it's recommended to include %s in in your custom
  ;; :target-path, which will splice in names of the currently active profiles.

  :figwheel {
             :http-server-root "public" ;; this will be in resources/
             :server-port 3449          ;; default

             ;; CSS reloading (optional)
             ;; :css-dirs has no default value 
             ;; if :css-dirs is set figwheel will detect css file changes and
             ;; send them to the browser
             :css-dirs ["resources/public/css"]

             ;; Server Ring Handler (optional)
             ;; if you want to embed a ring handler into the figwheel http-kit
             ;; server
             ;; :ring-handler example.server/handler 

             ;; To be able to open files in your editor from the heads up display
             ;; you will need to put a script on your path.
             ;; that script will have to take a file path and a line number
             ;; ie. in  ~/bin/myfile-opener
             ;; #! /bin/sh
             ;; emacsclient -n +$2 $1
             ;;
             :open-file-command "figwheel-opener"

             ;; if you want to disable the REPL
             ;; :repl false

             ;; to configure a different figwheel logfile path
             ;; :server-logfile "tmp/logs/figwheel-logfile.log" 
             } 
  
  
  :cljsbuild {
              :builds [{
                        :id "dev"
                        ;; The path to the top-level ClojureScript source directory:
                        :source-paths ["src-cljs"]
                        ;; The standard ClojureScript compiler options:
                        ;; (See the ClojureScript compiler documentation for details.)
                        :figwheel {
                                   ;; configure a websocket host, fighweel already knows the port
                                   ;; this is helpful if you want to broadcast to devices
                                   ;; :websocket-host "localhost" ;; or "www.myhost.com", "192.168.0.112"

                                   ;; optional callback
                                   ;; :on-jsload "example.core/fig-reload"

                                   ;; if you want to do REPL based development and not have
                                   ;; have compiled files autoloaded into the client env
                                   ;; :autoload false

                                   ;; The heads up display is enabled by default to disable it: 
                                   ;; :heads-up-display false

                                   ;; when the compiler emits warnings figwheel blocks the loading of files.
                                   ;; To disable this behavior:
                                   ;; :load-warninged-code true

                                   ;; a hook that will rewrite the urls that fighweel is using to
                                   ;; fetch assets.
                                   ;; :url-rewriter "example.core/fig-url-rewrite"
                                   }
                        :compiler {  :main "some-app.core"
                                   ;; The path to the JavaScript file that will be output.
                                   ;; Defaults to "target/cljsbuild-main.js".
                                   :output-to "resources/public/js/main.js"
                                   :asset-path "js/out"
                                   ;; See
                                   ;; https://github.com/clojure/clojurescript/wiki/Source-maps
                                   ;; Sets the output directory for temporary
                                   ;; files used during compilation. Must be
                                   ;; unique among all :builds. Defaults to
                                   ;; "target/cljsbuild-compiler-X" (where X is
                                   ;; a unique integer).
                                   :output-dir "resources/public/js/out"
                                   ;; Defaults to :whitespace.
                                   ;; :source-map "resources/public/js/main.js.map"
                                   :source-map true
                                   ;; The optimization level. May be :whitespace, :simple, or :advanced.
                                   ;; :optimizations :whitespace
                                   ;; :optimizations :simple
                                   ;; :optimizations :advanced
                                   :optimizations :none
                                   
                                   ;; Configure externs files for external libraries.
                                   ;; Defaults to the empty vector [].
                                   ;; For this entry, and those below, you can find a very good explanation at:
                                   ;; http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html
                                   ;; :externs ["jquery-externs.js"]
                                   ;; Adds dependencies on external libraries. Note that files in these directories will be
                                   ;; watched and a rebuild will occur if they are modified.
                                   ;; Defaults to the empty vector [].
                                   ;; :libs ["closure/library/third_party/closure"]
                                   ;; Adds dependencies on foreign libraries. Be sure that the url returns a HTTP Code 200
                                   ;; Defaults to the empty vector [].
                                   ;; :foreign-libs [{:file "http://example.com/remote.js"
                                   ;;                 :provides ["my.example"]}]
                                   ;; Prepends the contents of the given files to each output file.
                                   ;; Defaults to the empty vector [].
                                   ;; :preamble ["license.js"]
                                   ;; Configure the input and output languages for the closure library.
                                   ;; May be :ecmascript3, ecmascript5, or ecmascript5-strict.
                                   ;; Defaults to ecmascript3.
                                   ;; :language-in :ecmascript5
                                   ;; :language-out :ecmascript5
                                   ;; :pretty-print true
                                   }
                        }]
              }
  
  )



0.4ClojureScript

The preferred setup seems to be to compile ClojureScript by Clojure code in a Clojure/Leiningen project. To do this you add a task to Leiningen in your project.clj in your Clojure project created with:
lein new app some-app-name

Add the following to dependencies:

[org.clojure/clojurescript "0.0-3269"]

And this to plugins:

 [lein-cljsbuild "1.0.6-SNAPSHOT"]

And add the configuration for the task/plug as a root key:

  :cljsbuild {
         :builds [{
                    :id "dev"
                    :source-paths ["src-cljs"]
                    :compiler {
                               :main "some-app.core"
                               :output-to "resources/public/js/main.js"
                               :output-dir "resources/public/js/out"
                               :asset-path "js/out"
                               :source-map true
                               :optimizations :none
                               }
                 }]
              }

For more options and an example see the sample project.clj above or here.

You can then compile all ClojureScript files in the [projectdir]/src-cljs by executing:

lein cljsbuild once dev

If you want to recompile when .cljs files change then have Leiningen watch the source directories:

lein cljsbuild auto dev

If there is only one build dev is optional.

Take note:

Source maps also work with :optimizations set to :none. In this case the :source-map value doesn't control file names. So long as the value is truth-y (cf. the leiningen example above), an individual source map file will be generated for every ClojureScript source file.

It's important to note there are some source map option restrictions when using an :optimizations setting other than :none. In these cases :output-to, :output-dir, and :source-map must all share the exact same parent directory.

Setting optimizations to something other than :none slows down compilation greatly. Also make sure source-map is set to true in this case, not a string. Compilation is quite fast in auto mode with recompiling in auto mode taking a fraction of a second when a file changes.

0.5Emacs

Use cider for Clojure programming, install using package manager.

To load the necessary info, otherwise package-install doesn't work:

M-x list-packages
or:
M-x package-refresh-contents
Then:
M-x package-install [RET] cider [RET]

Also make sure clojure-mode is installed:

M-x package-install [RET] clojure-mode [RET]

0.6Clojure repl in Emacs

Cider needs nrepl installed for your project, so first, check version of cider:
M-x cider-version
> CIDER 0.9.0snapshot

Make sure that in ~/.lein/profiles.clj is the following:

{:user {:plugins [[cider/cider-nrepl "0.9.0-SNAPSHOT"]]}}

or per project add to project.clj

:plugins [[cider/cider-nrepl "0.9.0-SNAPSHOT"]]

and that the versions match. Snapshot should be in capitals here.

Open a file from your Clojure or ClojureScript project and start a repl with:

M-x cider-jack-in

If you get the following error when starting the cider-jack-in repl:

error in process filter: let: Symbol's value as variable is void: clojure--prettify-symbols-alist
error in process filter: Symbol's value as variable is void: clojure--prettify-symbols-alist

add this to the Emacs init files;

(defconst clojure--prettify-symbols-alist
  '(("fn"  . ?λ)))

This shouldn't be because this is defined in clojure-mode.el however somehow it isn't evaluated for me.

You can also start a repl in the project's directory (using bash) with:

lein repl
nREPL server started on port 56155 on host 127.0.0.1 - nrepl://127.0.0.1:56155
....

, take note of host and port and then to connect to it in Emacs:

M-x cider-connect [RET] localhost [RET] 56155

Some cider keyboard shortcuts:

C-c C-z to switch to the repl from a clj buffer:
C-M-x to evaluate top form. Output goes to repl
C-c M-n	Switch to namespace of the current buffer
C-x C-e	Evaluate the expression immediately preceding point
C-c C-k	Compile current buffer
C-, to save and load buffer into repl (custom shortcut)
C-c C-f	Evaluate the top level form under point and pretty-print the result in a popup buffer.
C-c C-b	Interrupt any pending evaluations.
C-c C-d d Display doc string for the symbol at point. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol.

In the repl:

C-j	Open a new line and indent.
C-c M-o	Clear the entire REPL buffer, leaving only a prompt.
C-c C-o	Remove the output of the previous evaluation from the REPL buffer.
C-c C-u	Kill all text from the prompt to the current point.
C-c C-b C-c C-c	Interrupt any pending evaluations.
C-up C-down	Goto to previous/next input in history.
M-p M-n	Search the previous/next item in history using the current input as search pattern. If M-p/M-n is typed two times in a row, the second invocation uses the same search pattern (even if the current input has changed).
M-s M-r	Search forward/reverse through command history with regex.
C-c C-n C-c C-p	Move between the current and previous prompts in the REPL buffer. Pressing RET on a line with old input copies that line to the newest prompt.
TAB	Complete symbol at point.
C-c C-d d	Display doc string for the symbol at point. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol
C-c M-n	Select a namespace and switch to it.

You can connect to multiple nREPL servers using M-x cider-jack-in multiple times. To close the current nREPL connection, use M-x nrepl-close. M-x cider-quit closes all connections.

Many more shortcuts at the cider site, as well as info on configuring cider.

0.7ClojureScript repl

Before explaining how to start a ClojureScript repl in Emacs it is instructional to see how it's done from the shell command line.

First off, a ClojureScript repl always builds on and is started in a Clojure repl. So first thing to do is to start a Clojure repl:

lein repl

From here we can connect to a JavaScript execution environment. The Clojure and ClojureScript libraries come with support for a ClojureScript repl. From the ClojureScript wiki:

The basic usage of the (ClojureScript) REPL is always the same:

  • require cljs.repl
  • require the namespace which implements the desired evaluation environment
  • create a new evaluation environment
  • start the REPL with the created environment

Using the REPL will also feel the same in each environment; forms are entered, results are printed and side-effects happen where they make the most sense.

So in practical terms, copy and paste the following into a Clojure repl:

  (do (require '[cljs.repl :as repl])
      (require '[cljs.repl.rhino :as rhino]) ;; require the rhino implementation of IJavaScriptEnv
      (def env (rhino/repl-env)) ;; create a new environment
      (repl/repl env)) ;; start the REPL

This connects to a generic JavasScript environment namely Rhino.

If you want to connect to a browser's JavaScript runtime enter the followiing into a Lein repl:

  ;; Execute in repl (started with lein repl):
  (do (require '[cljs.repl :as repl])
      (require '[cljs.repl.browser :as browser])
      (def env (browser/repl-env :port 8090)) ; <<< port 8090
      (repl/repl env)) ;;starts cljs repl and connects to browser 

This will open a cljs prompt, but it will not be responsive. What you've done in fact is start a server in the Clojure runtime that listens on port 8090. You will need a JavaScript runtime to connect to this and start interacting with this 'repl in a repl' and evaluate ClojureScript code (after it's compiled to JavaScript by cljs.repl).

For this purpose, create a webpage, for example, index.html that contains the following html:

<html>
  <head>
    <meta charset="UTF-8">
    <title>Browser-connected REPL</title>
  </head>
  <body>
    <div id="content">
      <script type="text/javascript" src="out/goog/base.js"></script>
      <script type="text/javascript" src="js/main.js"></script>
      <script type="text/javascript">
        goog.require('foo');
      </script>
    </div>
  </body>
</html>

Leave out the out/goog/base.js script tag if you've set optimizations to anything other than :none.

Then create a ClojureScript file in "src-cljs/sample-app/core.cljs" like so:

  ;;This runs in the browser (as javascript):
  (ns sample-app.core
    (:require [clojure.browser.repl :as repl]))
  ;; This connects to the repl server started in the lein repl:
  (repl/connect "http://localhost:8090/repl";) ; <<< port 8090

And make sure the project gets compiled:

lein cljsbuild auto dev

This will produce a main.js file in the "www/js" directory in your project, which will be loaded and evaluated by the browser when it loads index.html. So refresh the page if it's opened already.

Now the ClojureScript repl should be responsive and able to evaluate ClojureScript code in the browser's JavaScript environment. Try:

(. js/console (log "Hello!!!"))

0.8ClojureScript repl in Emacs: piggieback, Austin and weasel

The whole process described in the previous section does not work in Emacs since the "ClojureScript REPL requires/assumes that it is running in a terminal environment". Piggieback to the rescue! Check out its docs for more info on why it's needed in Emacs/nREPL sessions and what it does to enable the ClojureScript repl on top of nREPL.

.

0.8.1Piggieback

To use piggieback we only need to add one more dependency to our project:

[com.cemerick/piggieback "0.2.0"]

and add an option to the root key :repl-options in project.clj

:repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}

To start a Rhino ClojureScript in Emacs after doing cider-jack-in (which starts the nREPL) enter this into the repl:

(cemerick.piggieback/cljs-repl (cljs.repl.rhino/repl-env))

This should get you a cljs prompt.

If instead you enter the following into the Emacs nREPL:

(require 'cljs.repl.browser)
(cemerick.piggieback/cljs-repl (cljs.repl.browser/repl-env :port 8090))

and you refresh or open a browser with the same index.html as above that loads the same ClojureScript code as above you will again be connected to the browser's JavaScript runtime, but now from an nREPL session. Make sure to access index.html through a server, not through the file system.

0.8.2Austin

Ignore the suggestion in the piggieback docs to use Austin instead. It does build on piggieback and adds features but also ties in a Clojure server. It's easier to just start with plain piggieback first. Austin hopelessly confused me at first.

Austin starts two servers within a Clojure nREPL session. One to serve the index.html file and one to (reverse-) serve the ClojureScript repl for the browser to connect to. Because they share the same Clojure repl environment the html server can insert the same port and session number into the index.html file as the port and session that the repl server is configured with. They just share an atom. These port and session numbers are randomly chosen. This way you can of course start multiple sessions and repls and they won't clash. You also don't have to manually setup and choose the port number of the repl server and then insert it into the ClojureScript loaded in the index.html file.

0.8.3Weasel

The third option for enabling a ClojureScript repl that connects to a browser within an nREPL session is to use weasel. Weasel does not use long polling (like piggieback and Austin) to connect from the browser to the ClojureScript repl server but websockets. It still needs piggieback though so leave the set up described for piggieback in place but add one more dependency to project.clj:
[weasel "0.6.0"]

Start a ClojureScript repl by entering the following into a Clojure nREPL session (such as created for example by cider-jack-in in Emacs):

  (require 'weasel.repl.websocket)
  (cemerick.piggieback/cljs-repl (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 8092))

This will not work because the ClojureScript loaded by the index.html file needs to be a bit modified:

(ns my.cljs.core
  (:require [weasel.repl :as ws-repl]))

(ws-repl/connect "ws://localhost:8092")

Alternatively:

(ns my.cljs.core
  (:require [weasel.repl :as ws-repl]))

(ws-repl/connect "ws://localhost:8092"
   :verbose true
   :print #{:repl :console}
   :on-error #(print "Error! " %))

Explanation of the options:

verbose ; boolean, defaults to true
:print ; :repl to print only to the repl,
       ; :console to print only to the console
       ; #{:repl :console} to print to both
       ; or any variadic function to handle printing differently.
       ; defaults to :repl
:on-open, :on-error, :on-close ; fns for handling websocket lifecycle events.
                               ; default for all is nil

Again, try:

(. js/console (log "Hello!!!"))

0.9Figwheel

For more info see figwheel.Add dependency to project.clj
[figwheel "0.3.1"] 

Add following to the plugins key:

[lein-figwheel "0.3.1"]

Add the figwheel configuration to project.clj:

:figwheel {
   :http-server-root "public" ;; this will be in resources/
   :server-port 3449          ;; default

   ;; CSS reloading (optional)
   ;; :css-dirs has no default value 
   ;; if :css-dirs is set figwheel will detect css file changes and
   ;; send them to the browser
   :css-dirs ["resources/public/css"]

   ;; Server Ring Handler (optional)
   ;; if you want to embed a ring handler into the figwheel http-kit
   ;; server
   ;; :ring-handler example.server/handler 
   ;; To be able to open files in your editor from the heads up display
   ;; you will need to put a script on your path.
   ;; that script will have to take a file path and a line number
   ;; ie. in  ~/bin/myfile-opener
   ;; #! /bin/sh
   ;; emacsclient -n +$2 $1
   ;;
   :open-file-command "figwheel-opener"
   ;; if you want to disable the REPL
   ;; :repl false

   ;; to configure a different figwheel logfile path
   ;; :server-logfile "tmp/logs/figwheel-logfile.log" 
} 

Start figwheel in terminal:

rlwrap lein figwheel 

Open http://localhost:3449/index.html

0.10Paredit

When editing Clojure in Emacs use paredit. Some shortcuts:
M-(	paredit-wrap-round, surround expression after point in parentheses
C-→	Slurp; move closing parenthesis to the right to include next expression
C-←	Barf; move closing parenthesis to the left to exclude last expression
C-M-f, C-M-b	Move to the opening/closing parenthesis

0.11Links

0.11.1Books

Two books I've found useful: There's also a book on ClojureScript:

0.11.2General web tutorials and docs on Clojure:

0.11.3Leiningen

0.11.4Emacs

0.11.5Repl

0.11.6Other

0.11.7React

0.11.8Starter projects:

comments powered by Disqus