Programmatically accessing ClojureScripts Externs Inference

TLDR: As of release 1.9.456 ClojureScript can infer externs preventing the clobbering of third party JavaScript libraries. We can retrieve the inferred externs programmatically which allows us to create our own custom externs file.

What are externs?

Lets say you want to use third party Foo library like so:

(defnfoobar[arg](let[my-foo(Foo.arg)](.barmy-foo)))

As long as you don't use advanced compilation this will work just fine. But as soon as you build with advanced compilation the above code will fail due to the Google Closure Compiler munging bar into something like K. The Google Closure Compiler has no information about the Foo library so it aggressively munges the bar method.

To fix this you need to create an externs, basically a file which describes all the functions, methods, and classes contained in the third party library. So to use the Foo library you would need have a foo-externs.js containing

functionFoo(arg1){};Foo.prototype.bar=function(){};

See this blog post for more details about externs and the Google Closure Compiler.

ClojureScript externs inference

With the 1.9.456 release ClojureScript now has Externs inference. ClojureScript now has the ability to infer the needed externs for external libraries and automatically create an externs file for you.

get-externs-from-analysis takes in the compilation state and extracts the externs inference from it. This will work for any ClojureScript analysis like analyze-seq or analyze-file.

get-inferred-externs combines get-snippet-analysis and get-externs-from-analysis for an easy to use function extracting externs from ClojureScript code snippets.

Notice that all we needed to extract the externs was publicly exposed ClojureScript Analyzer API. cljs.util/map-merge is a convenience function that does a deep merge of two maps, transforming a vector of maps:

[{React{Component{prototype{render{}}}}},{Object{woz{}}}]

into one map

{React{Component{prototype{render{}}}},Object{woz{}}}

What benefits do we get from grabbing the externs from the ClojureScript analysis? We already get the inferred externs packaged up in the inferred_externs.js file. One use case is annotating the externs to give the Closure Compiler more information. Like adding the return type of React.Component.render for example.

Conclusion

Inferring externs removes much of the busy work that was previously needed to integrate third party JavaScript libraries. Everything that is needed to access them programmatically is contained in the ClojureScript Analyzer API.