on
Zero to Om - Act 5
In this part we will have a closer look at the app’s build configuration and discover what it can do for us. The source code can be found on GitHub.
Note: I strongly recommend reading the previous post first if you haven’t done so already.
The Build Config
The project’s build configuration is defined in project.clj
. It is written in Clojure and looks like this:
;; project.clj, part 1
(defproject todomvc "0.1.0-SNAPSHOT"
:jvm-opts ^:replace ["-Xms4g" "-Xmx4g" "-server"]
:dependencies [[org.clojure/clojure "1.6.0"]
[org.clojure/clojurescript "0.0-2280"]
[org.clojure/core.async "0.1.267.0-0d7780-alpha"]
[secretary "1.2.0"]
[om "0.7.1"]]
:plugins [[lein-cljsbuild "1.0.4-SNAPSHOT"]]
Basically, it’s just one big map of vectors. The dependencies
are automatically loaded from Leiningen. cljsbuild
is a plugin that allows to compile ClojureScript to JavaScript - its configuration is:
;; project.clj, part 2
:source-paths ["src"]
:cljsbuild {
:builds [{:id "dev"
:source-paths ["src"]
:compiler {
:output-to "app.js"
:output-dir "out"
:optimizations :none
:source-map true}}]})
Earlier we’ve learned that to build the application we can use lein cljsbuild once dev
. The command starts Leiningen and executes the dev
build profile of the cljsbuild
exactly once. If we use auto
instead, it watches the source paths for any changes and triggers a build. Very handy during development.
If you look into the out
folder in the project’s root directory after a build, you’ll see that there is a folder for every dependency we’ve specified. Inside you’ll find the library’s source files, in ClojureScript and JavaScript. Additionally, the Google Closure library files can be found in the goog
directory.
Note: The source-map
option tells the plugin to generate a <file-name>.js.map
file for each compiled file. This allows the browser to show you the corresponding ClojureScript source code instead of an incomprehensible jumble of JavaScript code when there is a runtime error.
Publishing
There will come a time when you want to publish your application and share it with the world. The out
folder contains roughly 2 MB of JavaScript. Not to mention that they are scattered throughout 88 separate files. Sounds like the requirements for a browser benchmark, not like a usable web application!
So we need to reduce the number of files and the total file size. That’s where the Google Closure Compiler can help us. Below you’ll find a new build profile called release
which will solve all of our problems:
:cljsbuild {
:builds [{:id "dev"
...}
{:id "release"
:source-paths ["src"]
:compiler {
:output-to "out/app.min.js"
:optimizations :advanced
:elide-asserts true
:pretty-print false
:source-map "app.js.map"
:externs ["src/react-externs.js"]}}]}
In contrast to the dev
profile it outputs a single file, uses advanced optimizations, removes assertions and does not pretty print the JavaScript code. To run this build profile we use lein cljsbuild once release
. It takes noticeable longer than the development profile but we’ll only run this when we actually want to publish the app anyway.
Note: The Closure Compiler is the bulldog among minifiers: very aggressive, stopping for no one. That’s why you need to tell it what code it should NOT remove by using ‘externs files’, such as react-extern.js in our case.
The following table gives you a feeling for the influence that the different options have on the final result. It shows the file sizes for the different ‘modes’ the Closure Compiler offers:
The advanced mode reduces the file size by roughly 78% compared to the simple mode. The Closure Compiler achieves this minification by making a dead code analysis: removing JavaScript code that won’t be used by the app.
So the best result we can expect is 47 KB gzipped. Sounds reasonable. For comparison, without the option :elide-asserts true
the result for advanced mode grows to 195 KB (48 KB gzipped). Without setting :pretty-print false
it even grows to 275 KB (53 KB gzipped).
Of course there is one thing we’ve left out: the footprint of the React library. The minified version is 123 KB (34 KB gzipped). That makes 81 KB gzipped in total. I’ll let you decide for yourself whether this would be an acceptable value for your app :)
Tip of the Day: Leiningen allows you to specify an alias for single or multiple commands. This way, we can just create a publish
command that runs a build using the release profile - and does a cleanup beforehand:
:aliases {
"publish" ["do" ["cljsbuild" "clean"] ["cljsbuild" "once" "release"]]
}
Outlook
There is a project called shadow-build that allows you to split the final JavaScript file into multiple files, eg putting the common code (like the standard library) into one file and the rest into separate files. Then you could have separate ‘areas’ in your application. For example, a user who doesn’t go to the admin section won’t need to download the source code for it. Currently, getting this functionality into ClojureScript directly is being discussed.
This concludes our journey through the build facilities. Finally, I recommend glancing over the cljsbuild sample once. It documents all the possible options of the ClojureScript plugin.
In Act 6 we’ll have a look at libraries that help us create better Om applications!
comments powered by Disqus