Figwheel Main
Figwheel Main builds your ClojureScript code and hot loads it as you are coding!
Get a quick idea of what Figwheel does by watching the 6 minute flappy bird demo of figwheel.
Learn even more by watching a 45 minute talk on Figwheel given at ClojureWest 2015.
Read the introductory blog post.
Support Work on Figwheel and other Clojure tools
I contribute a significant amount of time writing tools and libraries for Clojure and ClojureScript. If you enjoy using figwheel, rebel-readline, spell-spec, cljs-test-display or piggieback please consider making a contribution.
Features
Live code reloading
If you write reloadable code figwheel can facilitate automated live interactive programming.
Supports Node.js
Static file server
The inclusion of a static file server allows you to get a decent
ClojureScript development environment up and running quickly. For
convenience there is a :ring-handler
option so you can embed a ring
handler into the figwheel server.
Live CSS reloading
Figwheel will reload your CSS live as well.
Live JavaScript reloading
Figwheel can live reload your JavaScript source files.
Heads up display
Figwheel has a non-intrusive heads up display that gives you feedback on how well your project is compiling. By writing a shell script you can click on errors in the heads up display and they will open in your editor!
Descriptive Errors with Code Context
Figwheel provides descriptive compiler errors that point to where the error is in your code. These errors appear in the REPL as well as the heads up display.
Excellent Configuration Error Reporting
It can be quite daunting, when you are configuring a tool for the first time. Figwheel currently offers excellent configuration error reporting that will help you if you happen to misconfigure something.
ClojureScript REPL
When you launch Figwheel it not only starts a live building/reloading
process but it also optionally launches a CLJS REPL into your running
application. This REPL shares compilation information with the
ClojureScript compiler, allowing the REPL to be aware of the code
changes as well. The REPL also has some special built-in control functions
that allow you to control the auto-building process and execute
various build tasks without having to stop and rerun figwheel.main
.
Robust connection
Figwheel's connection is fairly robust. I have experienced figwheel sessions that have lasted for days through multiple OS sleeps.
Message broadcast
Figwheel broadcasts changes to all connected clients. This means you can see code and CSS changes take place in real time on your phone and in your laptop browser simultaneously.
Respects dependencies
Figwheel will not load a file that has not been required. It will also respond well to new requirements and dependency tree changes.
Won't load code that is generating warnings
If your ClojureScript code is generating compiler warnings Figwheel won't load it. This, again, is very helpful in keeping the client environment stable. This behavior is optional and can be turned off.
Try Figwheel with Flappy Bird
Via Leiningen
Make sure you have the latest version of leiningen installed.
Clone this repo:
$ git clone https://github.com/bhauman/flappy-bird-demo-new.git
Change into the flappy-bird-demo-new directory and run:
$ lein fig:build
Via Clojure Tools
First we will want to install the clj
and clojure
command line
tools.
Clone this repo:
$ git clone https://github.com/bhauman/flappy-bird-demo-new.git
Change into the flappy-bird-demo-new directory and run:
$ clj -A:build
Get started quickly with the template!
You can get a quick greenfield project with the Figwheel Template
Learning ClojureScript
If you are brand new to ClojureScript it is highly recommended that you do the ClojureScript Quick Start first. If you skip this you will probably suffer.
There is a lot to learn when you are first learning ClojureScript, I recommend that you bite off very small pieces at first. Smaller bites than you would take when learning other languages like JavaScript and Ruby.
Please don't invest too much time trying to set up a sweet development environment, there is a diverse set of tools that is constantly in flux and it's very difficult to suss out which ones will actually help you. If you spend a lot of time evaluating all these options it can become very frustrating. If you wait a while, and use simple tools you will have much more fun actually using the language itself.
Read the Tutorial
There is an extensive getting started tutorial I highly reccomend reading it if you are new to Clojure, ClojureScript and or the new Clojure CLI tools.
Documentation
The documentation a currently a good resource but is still a work in progress.
Getting Help
You can get help at both the ClojureScript Google Group and on the #clojurescript, #figwheel-main and #beginners Clojurians Slack Channels
Quick Usage
This is abbreviated usage documentation intended for experienced Clojure/Script developers. I highly reccomend the tutorial if you are new to Figwheel and ClojureScript.
Clojure CLI Tools
First, make sure you have the Clojure CLI Tools installed.
On Mac OSX with brew:
brew install clojure
Now launch a ClojureScript REPL with:
clj -Sdeps "{:deps {com.bhauman/figwheel-main {:mvn/version \"0.2.18\"}}}" -M -m figwheel.main
This will first compile browser REPL code to a temp directory, and
then a browser will open and a cljs.user=>
prompt will appear.
From here you can do REPL driven development of ClojureScript.
With Leiningen
You can also use leiningen
by adding it to :dependencies
in your
project.clj
and launching it like so:
lein run -m figwheel.main
With Rebel Readline for much better REPL experience
Figwheel main will automatically use rebel-readline-cljs
if it is
available. So, you can get Rebel Readline behavior by simply adding it
to your dependencies.
clojure -Sdeps "{:deps {com.bhauman/figwheel-main {:mvn/version \"0.2.18\"} com.bhauman/rebel-readline-cljs {:mvn/version \"0.1.4\"}}}}" -M -m figwheel.main
As of right now using Rebel readline does create some startup overhead (hoping to correct this in the near future), so you may want to choose use it only when you are going to interact at the REPL.
Setting up a build with Tools CLI
Set up a build which will allow you to start a watch/build/reload process on a set of local ClojureScript source files.
If the following doesn't work for you please see the tutorial.
The following assumes the current working directory is the root directory of your project.
Ensure your deps.edn
file has the figwheel.main
dependencies:
{:deps {com.bhauman/figwheel-main {:mvn/version "0.2.18"}
com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}}
;; setup some development paths
:paths ["src" "target" "resources"]
;; setup a helpful alias to start the build
:aliases {:build-dev {:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}}}
Create a dev.cljs.edn
build file:
{:main example.core}
In src/example/core.cljs
, place the following ClojureScript code:
(ns example.core)
(enable-console-print!)
(prn "hello world!")
Then run the command:
clojure -M -m figwheel.main -b dev -r
This will launch a REPL and start autobuilding and reloading the src
directory so that any files you add or change in that directory will
automatically be hot reloaded into the browser.
The -b dev
or --build dev
flag option is indicating that Figwheel
should read dev.cljs.edn
for build configuration.
The -r
or --repl
flag indicates that a REPL should be launched.
Interesting to note that the above command is equivalent to:
clojure -M -m figwheel.main -co dev.cljs.edn -c -r
You can also start your build running with the build-dev
alias we
defined in the deps.edn
to save some typing:
clojure -A:build-dev
Setting up a build with Leiningen
Set up a build which will allow you to start a watch/build/reload process on a set of local ClojureScript source files.
If the following doesn't work for you please see the tutorial.
The following assumes the current working directory is the root directory of your project.
Ensure your project.clj
file has figwheel.main
dependencies:
:dependencies [[com.bhauman/figwheel-main "0.2.18"]
[com.bhauman/rebel-readline-cljs "0.1.4"]]
;; setup target as a resource path
:resource-paths ["target" "resources"]
;; set up an alias to invoke your figwheel build
:aliases {"fig" ["trampoline" "run" "-m" "figwheel.main"]
"build-dev" ["trampoline" "run" "-m" "figwheel.main" "-b" "dev" "-r"]}
Create a dev.cljs.edn
build file:
{:main example.core}
In src/example/core.cljs
, place the following ClojureScript code:
(ns example.core)
(enable-console-print!)
(prn "hello world!")
Then run the command:
lein trampoline run -m figwheel.main -- -b dev -r
This will launch a REPL and start autobuilding and reloading the src
directory so that any files you add or change in that directory will
automatically be hot reloaded into the browser.
The -b dev
or --build dev
flag option is indicating that Figwheel
should read dev.cljs.edn
for build configuration.
The -r
or --repl
flag indicates that a REPL should be launched.
using the aliases
We are probably better off using the helpful aliases that we created
in the project.clj
You can invoke the above command using the fig
alias like so:
lein fig -- -b dev -r
You can also just use the build-dev
alias to get the same result:
lein build-dev
Why use an alias here? Why not use a lein plugin? The first reason I'm not using a plugin here is that Leiningen boots a lot faster when it doesn't have to dynamically load/compile plugin code. Another reason is that
figwheel.main
's command line options are much more expressive thanlein-figwheel
's and lein aliases are better positioned to leverage that expressiveness.
Using your own HTML to host your app
If would prefer to use your own HTML page to host your application
instead of the default page served by figwheel.main
, you will first
need to ensure that you have added resources
to the :paths
key in
deps.edn
, as demonstrated above. After that, you can place the
index.html
in resources/public/index.html
so that it will mask the
one served by the figwheel.main
helper application.
The following is some example HTML to help you get started. The
trickly part is the path to the ClojureScript bootstrap file. The
default output path is available at cljs-out/[build-id]-main.js
. So
in this case it will be: cljs-out/dev-main.js
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app"></div>
<script src="cljs-out/dev-main.js"></script>
</body>
</html>
You can place CSS and other static assets in the resources/public
directory.
Configuring Figwheel Main
If you need to configure figwheel.main
, you will use a
figwheel-main.edn
file in the root of your project directory.
For example let's explicitly set our watch directory.
Create a figwheel-main.edn
file in the root of your project
folder with these contents:
{:watch-dirs ["cljs-src"]
:css-dirs ["resources/public/css"]}
:watch-dirs
instructs figwheel.main
to watch and compile the
sources in the cljs-src
directory.
:css-dirs
instructs figwheel.main
to watch and reload the CSS
files in the resources/public/css
directory.
If you need to override some of the figwheel configuration options for a particular build, simply add those options as metadata on the build edn.
For example if you want to have :watch-dirs
that are specific to the
dev build then in your dev.cljs.edn
file:
^{:watch-dirs ["cljs-src" "dev"]}
{:main example.core}
All the available configuration options are documented here: https://github.com/bhauman/figwheel-main/blob/master/doc/figwheel-main-options.md
All the available configuration options specs are here: https://github.com/bhauman/figwheel-main/blob/master/src/figwheel/main/schema/config.clj
Classpaths, Classpaths, Classpaths
Understanding of the Java Classpath can be very helpful when working with ClojureScript.
ClojureScript searches for source files on the Classpath. When you add
a re-frame
dependency like so:
{:deps {com.bhauman/figwheel-main {:mvn/version "0.2.18"}
com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
;; adding re-frame
re-frame {:mvn/version "1.10.5"}}
:paths ["src" "target" "resources"]}
The source files in re-frame
are on the Classpath and the
ClojureScript compiler can find re-frame.core
when you require it.
Your sources will need to be on the Classpath so that the Compiler can
find them. For example, if you have a file
cljs-src/example/core.cljs
you should add cljs-src
to the :paths
key so that the ClojureScript compiler can find your example.core
namespace. It is important to note that the src
directory is on your
Classpath by default.
In Figwheel, the embedded HTTP server serves its files from the Java Classpath.
It actually serves any file it finds on the Classpath in a public
sub-directory. This is why we added target
and resources
to the
:paths
key in the deps.edn
file above. With target
and
resources
both on the Classpath the server will be able to serve
anyfile in target/public
and resources/public
.
The compiler by default compiles artifacts to target
for easy cleaning.
It is custmary to put your index.html
, CSS files, and other
web artifacts in the resources/public
directory.
Live CSS Reloading
As mentioned above you configure figwheel.main
to live reload your
CSS files as you edit them.
Just add a :css-dirs
key that lists the CSS directories to watch in
your figwheel configuration.
For example in your figwheel-main.edn
file:
{:css-dirs ["resources/public"]}
Using CSS Precompilers
Using SASS or LESS and still want to have the benefits of live CSS reloading?
Simply run your sass or less watcher/compiler on the command line and
make sure the final output CSS files land in one of the directories
that you have listed in your :css-dirs
configuration option (mentioned
above).
Working with Node.js
Unlike cljs.main
, with figwheel.main
you will not specify a
--repl-env node
because the figwheel.repl
handles Node.js REPL
connections in addition to others.
You can launch a Node REPL like so:
clojure -M -m figwheel.main -t node -r
You can quickly get a hot reloading CLJS node build up an running using the
deps.edn
, example.core
and dev.cljs.edn
above. Simply add a --target node
or -t node
to the compile command.
clojure -M -m figwheel.main -t node -b dev -r
This will launch a CLJS Node REPL initialized with example.core
you
can now edit example/core.cljs
and it will be hot reloaded.
Of course if you add :target :nodejs
to dev.cljs.edn
like so:
{:main example.core
:target :nodejs}
You be able to run the build more simply:
clojure -M -m figwheel.main -b dev -r
Reload hooks
It is common to want to provide callbacks to do some housekeeping before or after a hot reload has occurred.
You can conveniently configure hot reload callbacks at runtime with metadata. You can see and example of providing callbacks below:
;; first notify figwheel that this ns has callback defined in it
(ns ^:figwheel-hooks example.core)
;; mark the hook functions with ^:before-load and ^:after-load
;; metadata
(defn ^:before-load my-before-reload-callback []
(println "BEFORE reload!!!"))
(defn ^:after-load my-after-reload-callback []
(println "AFTER reload!!!"))
The reload hooks will be called before and after every hot code reload.
Quick way to understand the command line options
If you supply a -pc
or --print-config
flag to figwheel.main
, it
will not execute the command. It will instead print out the computed
configuration.
For example:
clojure -M -m figwheel.main -pc -b dev -r
Will output:
---------------------- Figwheel options ----------------------
{:ring-server-options {:port 9550},
:client-print-to [:repl :console],
:pprint-config true,
:watch-dirs ("src"),
:mode :repl}
---------------------- Compiler options ----------------------
{:main exproj.core,
:preloads [figwheel.core figwheel.main figwheel.repl.preload],
:output-to "target/public/cljs-out/dev-main.js",
:output-dir "target/public/cljs-out/dev",
:asset-path "cljs-out/dev",
:aot-cache false,
:closure-defines
#:figwheel.repl{connect-url
"ws://localhost:9550/figwheel-connect?fwprocess=c8712b&fwbuild=dev",
print-output "repl,console"}}
Using figwheel.main from a script
See the figwheel.main/start
function and the figwheel.main/start-join
functions.
Contributing to the Helper App
Figwheel main comes with a helper Ring app that is served when there is no other html page to host the REPL JavaScript env.
If you are interested in contributing to this app:
First hit me up in the #figwheel-main channel on the clojurians Slack so that we can co-ordinate a bit.
To work on the helper app:
Checkout this repository and change directory to the figwheel-main
directory where this README is located.
Then the command
clj -M -m figwheel.main -b helper -r
should launch a live development workflow for the Helper application.
tweaking the CSS
The CSS files for the helper app are located at
helper-resources/public/com/bhauman/figwheel/helper/css
and you
should be able to edit them live.
working on the app itself
Both the server-side and client side code are located in
src/figwheel/main/helper.cljc
and you should be able to work on them
live.
If you change the behavior of the CLJS in
src/figwheel/main/helper.cljc
it will not be reflected in the actual
helper app until you compile with make helper
editing helper content
The helper app content is generated from the Markdown files in the
helper-content
directory. You must compile the markdown with make helper-docs
this currently requires ruby
and kramdown (gem install kramdown
)
keep it simple
The helper app is intended to be very simple in structure. We do not want to
add more dependencies and slow the startup time of figwheel.main
,
and we also do not want to do anything that will interfere with the
users running code.
License
Copyright Β© 2018 Bruce Hauman
Distributed under the Eclipse Public License either version 1.0 or any later version.