I have built an application using mostly Angular. Now I'd like to transition the project to Clojurescript. Clojurescript has very nice interop with JavaScript as we all know but is it feasible to do the other way around?
How can regular JavaScript /Angular code tap into the JavaScript generated from Clojurescript ?
The dream would be to write new features in clojurescript and have them work side by side with the legacy code.
Any tips or tricks regarding this are welcome.
Clojurescript vars, functions, and deftypes/records are normal JS vars, functions, and constructor functions/objects, so you can go cljs.core.abc()
in your javascript and invoke clojurescript without problems.
Caveats are:
- Name munging. Clojurescript names allow a wider range of characters than javascript, so many function names will be munged. E.g.
cljs.core/<
becomes cljs.core._LT_
.
- Macros. Macros only exist at clojurescript compile-time, so you can't use a CLJS macro from javascript.
- Advanced compilation. Clojurescript core is very large and it relies on the Google Closure compiler for dead-code elimination, which is only available in advanced compilation. Using advanced compilation safely is very easy in pure clojurescript code, but harder in javascript code. (The biggest danger is mixing string and symbol property access.)
You have a number of options:
- CLJS embeds legacy code as a library. Your program entry point is CLJS but uses your legacy code like an external library. Your legacy code provides an extern for where CLJS calls your legacy code. Legacy code only calls CLJS functions which are explicitly exported using
^:export
(if you define the name) or something like goog.exportSymbol
. CLJS and legacy code are deployed in separate JS files which are built separately.
- CLJS embeds legacy code directly. Program entry point is CLJS, but your legacy code is other js compiled into the same project. CLJS and legacy code are deployed in a single JS file built by the clojurescript compiler. To use advanced compilation, your legacy code must be safe to use with advanced compilation and structured for use with the Google Closure compiler (goog.provides/requires, type annotations, etc). Angular will probably have to remain separate, but there is an angular extern available.
- Legacy code calls CLJS code as a library. This is pretty much the same as "CLJS embeds legacy code as a library," except your entry point is legacy code and it calls exported cljs functions.
- Legacy code uses some CLJS features via library like mori. Here you are not using CLJS directly but a JS library that exposes some features of clojurescript as normal JS. It remains a separate js library, and you never write any clojurescript.
For Angular integration in ClojureScript, you can use the gyr extension.
For data conversion from Clojure types to Java Script types you can use clj->js.