Skip to content
Hyunwoo Nam edited this page Jun 7, 2018 · 18 revisions

Why do we need externs?

When a ClojureScript project uses external JS libraries, the external references to those libraries will be munged when compiling in Advanced Optimizations mode. This is the result of the Google Closure compiler aggressively renaming all unexported symbols. details here

(CLJS code) -----------> (Closure-safe JS Code) ------------> (Optimized JS)
                 |                ^                   |
                CLJS              |         Google Closure Compiler
              Compiler            |            (Advanced Mode)
                                  | 
                                  |
                 Any external library symbols used here
                will be munged in the optimized JS unless
                  they are defined in an externs file.

Since the majority of Javascript libraries do not follow Google Closure's coding standards for advanced optimization, they cannot simply be fed into the Google Closure compiler. Thus, the compiler allows for an externs file describing which external symbols are not to be touched during optimization.

What are externs exactly?

An externs file is a normal Javascript file that only contains functions and variable declarations. Declared functions have empty bodies. Declared variables can be var statements without assignment, or can be an object containing other variables and functions.

function hello() {}
var world;
var nested = {
   foo: {},
   bar: function() {}
};

The function and variable declarations can contain optional annotations in the form of JSDoc comment tags. This is mainly useful for us to tell Google Closure which functions are constructors for defining types, and if a variable is of a specific type.

/** @interface */
function Foo() {}
Foo.prototype.bar = function() {};
/** @type {!Foo} */
var foo;

Document the extern creation process

It is useful to document the method you use to create the externs so that others will be able to update the extern if necessary. A good place for the documentation is the extern file itself.

Here are some examples:

Examples

Extra Resources

Extern Generators

Tricks

If generated externs are empty:

var Plyr = function () {};

It is possible that it is possible to generate externs from the object instance, instead of the class. Instantiate object in console and try generating externs for that instead. Open browser dev console, select sandbox frame and evaluate e.g. PlyrFoo = new Plyr("#player");. Then you might be able to generate externs:

var PlyrFoo = {
  "captions": {
    "active": {},
    "currentTrack": {}
  },
  ...
};

Open Questions

  • How are some of the cljsjs package externs generated with so many annotations?
  • Is there a way we can automate the testing externs to some extent?