After including these libraries in my local assets folder, the
fade-logo-on-scroll function worked as
expected in test and development. However, once deployed to staging, the
fade-logo-on-scroll function didn’t
work, and an “Uncaught TypeError: Undefined is not a function” error message appeared in the browser console.
What function is this error message referring to and why the heck is it missing? And why is it missing in
staging but not in development? Just above the TypeError, you may have observed that the “zf” function name
actually refers to the
waypoint function in my ClojureScript function. After compilation and minification, the
In fact, it appears as though the compiler couldn’t find a reference to the .waypoint function in the waypoints
higher compilation level for production than for test and development. ClojureScript uses
Google Closure optimizations so that the
:whitespace (removes comments
:advanced. If not specified,
the optimization level defaults to simple.
Advanced compilation is more aggressive than the other types of compilation in that it renames symbols to gain performance improvements. Unfortunately, this means that the compilation process breaks references to functions defined outside of the compiled code, such as those in external jQuery libraries, since the renamed function reference doesn’t match the name of the defined function.
To fix this problem, I had to add an extern for the external library method. Google Closure allows for the use of externs. Externs are files that declare the names of external libraries functions that should not be renamed. Google Closure provides extern files for some common libraries—in fact, the project that I was working on already included the jQuery extern, which is why I didn’t see any errors for the jQuery functions.
If you’re using a library that doesn’t have a pre-existing extern file (like the waypoints library in this example),
you need to make your own. Thankfully, creating an extern file is easy. In your project’s externs folder, create a
renamed. This function’s body should be empty. For example, I added a file called
externs/waypoints.js and included
the following declaration:
Next, in the
project.clj file, specify this extern file for the
:compiler associated with the build using
:advanced optimizations. In the following example example,
:externs is a vector containing the path to each
extern file. By convention, these extern files reside in the project’s externs folder.
To avoid surprises due to differences between compilation optimizations in various environments (i.e., development
vs. production), it’s always a good idea to test the production compiler before deploying to staging or production.
For example, I used the
lein cljsbuild auto prod command to locally build the project using the prod environment’s
advanced optimization setting. This helps to avoid some potentially embarrassing ClojureScript blunders.