Dependencies

Every non-trivial ClojureScript application will eventually need to
consume code created by others. ClojureScript developers can of course
take advantage of code authored using ClojureScript. However,
ClojureScript developers can also consume arbitrary JavaScript code,
whether or not it was written with ClojureScript in mind.

This guide assumes you’ve worked through the Quick Start
guide, and are equipped with the dependencies introduced there.

Consuming JavaScript Code

While you can consume any JavaScript code, the optimal mechanism for
including that code is not always the same. The following sections
explore the various options for utilizing third party JavaScript code.

Closure Library

The easiest JavaScript code to consume is that of Google’s
Closure Library (GCL), which
is automatically bundled with ClojureScript. GCL is a massive collection
of JavaScript code organized into namespaces much like ClojureScript
code itself. Thus, you can require a namespace from GCL in the same
fashion as a ClojureScript namespace. The following example demonstrates
basic usage:

See
this
blog post on the difference between :import and :require for
closure libs.

The gist of it is: use :import for Closure classes and enums, and use
:require for everything else.

External JavaScript Libraries

In cases where GCL doesn’t contain the functionality you want, or you’d
otherwise like to take advantage of a third party JavaScript library,
you can use the code directly.

Let’s consider the case where we want to use a fancy JavaScript library
called yayQuery. To utilize a JavaScript library, simply reference the
JavaScript as normal. Whether the file is loaded externally or inline
makes no difference, both will be applied in the same fashion at
runtime. To make things simple, we’ll define this library inline:

To use this library from ClojureScript, we can simply refer to the
symbols directly. If you build the following code using
{:optimizations :none}, everything will work fine and you will see a
message in your JavaScript console.

While this works fine with unoptimized code, it will fail when we use
advanced optimizations. Try compiling the same code with
{:optimizations :advanced} and reload your browser. You will receive
an error message similar to the following (it may not be exactly as
below):

Uncaught TypeError: sa.B is not a function

Why did this happen? When using advanced optimizations, the Google
Closure Compiler will rename symbols. In most cases, this is not a
problem, as all instances of the same symbol will be renamed
consistently. However, in this case the external symbol (the name in the
JavaScript code) is separate from our compilation unit, so the names
no longer match. Fortunately, we have options for resolving this issue
without losing all of the benefits of advanced compilation.

Using Externs

To fix compilation without modifying your source code at all, you can
add an externs file. An externs file defines the symbol names in a given
library, and is used by Google Closure Compiler to determine which
symbols must not be renamed. Here’s a minimal externs file for our
yayQuery library:

It is important to understand that all paths referenced in the
:externs vector must be on the classpath. For example you might
have placed the above externs file under a resources directory. Then
when using the standalone ClojureScript JAR you must launch your build
script with the following:

java -cp cljs.jar:resources:src clojure.main build.clj

Recompile with the externs file referenced, and your code should work
again without any modifications. Note that for many popular JavaScript
libraries, you may be able to find externs files which have already been
created by the library authors or the broader community. These files are
useful for any developer taking advantage of Google Closure Compiler,
even those not using ClojureScript.

Using String Names

For simple cases where you only reference a small number of JavaScript
symbols, you can also change your source code to reference code by
string name. Google Closure Compiler will never rename strings, so this
style will work without needing to create an externs file. The code
below will work in advanced compilation mode even without externs:

Careful readers may notice above that we are referencing js/window
just as we did js/yayQuery in the failing example. It works in this
case because Google Closure Compiler ships out of the box with a number
of externs for browser APIs. These are enabled by default.

Bundling JavaScript Code

To maximize efficiency of content delivery, you can bundle JavaScript
code along with your compiled ClojureScript code.

Google Closure Compiler Compatible Code

If your external JavaScript code has been written to be compatible with
Google Closure Compiler, and exposes its namespaces using
goog.provide, the most efficient way to include it is to bundle it
using :libs. This bundling mechanism takes full advantage of advanced
mode compilation, renaming symbols in the external JavaScript library
and eliminating dead code. Let’s adapt our yayQuery library from
previous examples, as below:

Because this code is compatible with advanced compilation, there is no
need to create externs. If you look at the compiled output, you’ll see
that the functions have been renamed and the unreferenced debugMessage
has been completely eliminated by Google Closure Compiler.

While an extremely efficient way to bundle external JavaScript, most
popular libraries are not compatible with this approach.

Bundling "Foreign" JavaScript Code

If the code you wish to bundle has not been authored with Google Closure
Compiler compatibility in mind, you can include it as a foreign library.
Foreign libraries are included in your final output, but are not passed
through advanced compilation. Let’s consider a version of yayQuery which
does not include a goog.provide:

Notice the presence of :require in the ns declaration. This
references a "namespace" called yq, but there is no corresponding
goog.provide in the yayQuery file. In the case of foreign libraries,
the "namespace" is provided in the build configuration. As long as the
name in the :provides key matches what you :require and is unique
across referenced libraries, you can name it anything you please:

Note that we have re-introduced our externs file here. Though the
foreign library is bundled, it must otherwise be referenced exactly as
if the script had been included externally.

CLJSJS

The previous sections have discussed the various ways of integrating
with any external JavaScript code. Finding the best way to integrate a
library can be tricky, especially if you have to procure externs.
Fortunately, for many of the most common JavaScript libraries, there is
an easier way. The CLJSJS project
automatically packages up external JavaScript libraries in a way that’s
directly supported by the ClojureScript compiler. It will automatically
package the best version of a library in a given context (including
minified libraries when using advanced optimizations, for example), and
automatically includes the appropriate externs.

Let’s say we’ve outgrown our beloved yayQuery library, and want to use
jQuery instead. This is one of the many popular libraries which has been
pre-packaged. We can fetch a copy as below:

Compile the code as below (note the addition of the JAR in our class
path), and you should see the message display when you load your
browser:

java -cp cljs.jar:jquery-1.9.0-0.jar:src clojure.main build.clj

Replacing a (transitive) CLJSJS dependency with another build of the library

Sometimes you have a transitive dependency on a CLJSJS library but want
to include the dependency manually or use a custom build of it. In that
case you need to do two things: (1) exclude the dependency with
:exclusions and (2) create an empty namespace with the cljsjs name so
that the build does not break.

For example om depends on cljsjs/react. To include a custom build
you need:

Consuming ClojureScript Code

The ability to consume any JavaScript library makes ClojureScript an
incredibly flexible and powerful language for writing JavaScript
applications. Of course, ClojureScript developers can also easily
include ClojureScript libraries authored by others.

Using Libraries Directly

Let’s make use of Schema, a
ClojureScript library which enables us to validate complex data types.
First, we need to procure a copy of the library:

As with CLJSJS libraries, everything is packaged in a JAR file which we
will reference in our class path when compiling. Unlike CLJSJS
libraries, though, ClojureScript library JARs contain no externs or
deps.cljs mappings.

Using the library is simple. Note that ClojureScript code and Clojure
macros are packaged in the same library:

Load up your browser, and you’ll see a helpful validation error from
Schema in your JavaScript console. Change the :c key to an integer
value and rebuild if you’d like to see this error go away.

Using Leiningen

In practice, it’s somewhat rare to use ClojureScript libraries directly
as in the previous section. Most popular ClojureScript libraries contain
other dependencies, and it can be quite a challenge to chase down all of
a library’s dependencies by hand and reference them in your build
command. Fortunately, this task can be handled automatically by
Leiningen, a popular build tool for Clojure and
ClojureScript projects.

Let’s use Leiningen to build a project which uses
cljs-ajax, a ClojureScript
library which provides convenience functions for sending remote "AJAX"
requests (ignore for a moment the fact that only the "A"s are relevant
in our case). To start, we need to create a project.clj file:

This project file is read by Leiningen, which then uses the information
within it to fetch all of the relevant dependencies automatically. In
addition, it includes configuration for a cljsbuild plugin. We’ve
created a single profile under the :min key. The compiler options
should look quite familiar by now! Fortunately everything we’ve learned
already translates perfectly to our new Leiningen based workflow.

After running this command, you will likely see Leiningen spend a few
moments downloading the required dependencies. Without Leiningen, we’d
have to track all of these down ourselves! After some crunching, you
should eventually see some green text announcing that the build was
successful! Because we chose to use the auto option, Leiningen will
continue running in the background looking for changes. You can confirm
this by making a trivial edit to core.cljs, notice that the change is
automatically recognized and a new build is produced!

Load index.html in your web browser with your JavaScript console open,
and you should see a successful "XHR" request!