ClojureScript

Advanced Compilation

If you are targeting traditional JavaScript clients (web browsers) it’s important to think about advanced compilation from the very beginning. Otherwise you will inevitably find yourself going through issues that could have been easily avoided with a little bit of up front preparation. In general do not wait to try an advanced build, you should always periodically generate a production build to catch issues sooner.

Avoid Foreign Libraries

It’s best to simply avoid foreign libraries if a solution exists either in Google Closure Library or in an existing ClojureScript library. Foreign libraries must supply externs for advanced compilation to work consistently.

Of course, in some cases foreign libraries cannot be avoided.

Using Foreign Libraries

If you must use a foreign library use a curated one like those provided by CLJSJS. These come packaged with externs so that you do not have to supply them yourself.

Occasionally you may find yourself needing a foreign library which has not been prepackaged.

Providing externs

When working with a foreign library which does not supply externs take the time to write an externs file for the API you intend to use. Externs files are surprisingly simple to provide. For example if the foreign library has some property Foo.bar that you wish to access your externs file should have the following entry:

Foo.bar;

If the foreign library has some method Foo.baz that you wish to invoke your externs file should have the following entry:

Foo.baz = function() {};

Sometimes there will not be a top level API but rather some method naming convention, that is, an ad-hoc Interface / Protocol. In these cases define your externs using Object:

Object.foo = function() {};
Object.bar = function() {};

Of course sometimes you will miss an extern entry and the the production file will produce a cryptic error. Thanks to a couple of Closure compiler options these issues are no longer difficult to debug.

Access from JavaScript

If you’d like to access ClojureScript code from JavaScript, then you will need to cope with the fact that advanced compilation will munge the JavaScript representation of your Var names. This can be easily addressed by adding :export metadata to Vars that should be consumable from JavaScript.

For example, if you have a square function, you can annotate it with ^:export as follows:

(ns my-math.core)

(defn ^:export square [x]
  (* x x))

With this, you can call your square function from JavaScript as follows:

my_math.core.square(3);

This works by including goog.exportSymbol calls in the emitted JavaScript wherever :export meta is associated with a Var.

For each exported Var, an additional un-renamed alias is established which points to the Closure-munged name. Munged names continue to be used internally within the optimized code.

The ^:export facility is for (and only for) providing external access to Vars via un-renamed aliases. If instead you’d like to debug optimized code which is using shortened names, consider :pseudo-names and :pretty-print, which are described in the following section.

You can individually export the Vars associated with protocol methods. In this example, bar and quux will be exported:

(defprotocol IFoo
  (^:export bar [this])
  (baz [this x])
  (^:export quux [this x y]))

Fixing Advanced Compilation Issues

Change your production build to use two additional options :pseudo-names true and :pretty-print true. Now your error will show a name that corresponds to the name in the original source. Add an externs entry for this missed case.

For more information about the specifics of :foreign-libs compiler option syntax consult dependencies.