Creating Externs - cljsjs/packages GitHub Wiki
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:
- react-grid-layout extern created using generator
- bcrypt extern from upstream project but modified
- moment extern is good example of auto generated and manually written parts
Examples
- chance externs
- csv externs
- d3 externs
- firebase externs
- hammer externs
- jquery 2 externs
- moment externs
- openlayers externs
- pikaday externs
- react externs
Extra Resources
- Using Javascript libraries in ClojureScript(A thorough guide to ClojureScript compilation, include externs)
- Advanced Compilation and Externs
- Annotating Javascript for the Closure Compiler
- Externs for Common Libraries
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?