ClojureScript foreign library support

137 views
Skip to first unread message

Luke VanderHart

unread,
Sep 28, 2011, 1:33:32 PM9/28/11
to Clojure Dev
I have done some work to make it easy to use "foreign" libraries in
ClojureScript - by "foreign", I mean libraries that are neither
ClojureScript nor GClosure-aware. (The term "external" is already
overloaded in GClosure, so I tried to avoid that).

All you have to do to include a foreign library is add a :foreign-libs
key to your compiler options. The following code, for example,
includes the excellent Raphael.js library.

(closure/build "src/cljs" {:optimizations :simple
:foreign-libs [{:file "https://
raw.github.com/DmitryBaranovskiy/raphael/master/raphael-min.js"
:provides
["raphael"]}]
:output-dir "working"
:output-to "public/js/main.js"})

The :file key can refer directly to a URL, or to a classpath or
working-dir relative path.

Then, in my cljs code, all I have to do to is :require ['raphael'],
and reference its constructor method with js/Raphael. (e.g, (js/
Raphael. 10 10 50 50))

It works for both :simple and :advanced modes, correctly bundling the
foreign library's source with your compiled app.

Of course, the external library still has to be compatible with Google
Closure compilation. For example, Raphael is not 100% compatible with
advanced mode. It compiles without errors, but then generates SVG
markup with munged attribute names which obviously causes problems.

The code is on my github repo at https://github.com/levand/clojurescript.
Please tell me what you think: personally, I find this very useful and
would like to see it rolled into master.

Brenton Ashworth

unread,
Sep 28, 2011, 3:04:06 PM9/28/11
to Clojure Dev
I think this is a great addition. We certainly to need this to round
out all of the possible library usage scenarios.

How hard will it be to get Raphael working in advanced mode?
> The code is on my github repo athttps://github.com/levand/clojurescript.

Luke VanderHart

unread,
Sep 28, 2011, 7:15:56 PM9/28/11
to Clojure Dev
I haven't had the time to look into it in detail yet. I suspect it
will require some modifications to the way Raphael sets SVG
attributes. I'm planning on looking into it some more tomorrow
evening, perhaps forking Raphael to fix whatever issues there are.

Unfortunate, as Raphael is a great library. Still, it works great in
simple compilation, and the same approach will work for other
libraries as well - it all depends on how well they support advanced
mode compilation.

Luke VanderHart

unread,
Sep 30, 2011, 11:05:21 AM9/30/11
to Clojure Dev
I looked into creating an externs file for Raphael, but no luck - the
problem isn't due to unavailable vars, which makes sense, since it's
being compiled together with the client code, so any munging would be
consistent anyway.

The problem is that it's enumerating map keys as strings, like this:

attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};

and then later on:

for (var key in attr) {
if (attr.hasOwnProperty(key)) {
el.setAttribute(key, Str(attr[key]));
}
}

Which results in a bunch of munged attributes.

Any solution, I think, would have to modify Raphael itself.

Luke VanderHart

unread,
Sep 30, 2011, 11:42:47 AM9/30/11
to Clojure Dev
So, in leiu of getting Raphael to work in advanced mode, my
"preferred" method of utilizing an external library while still
leveraging advanced mode for my own code is as follows:

1. Reference the library in a script tag in my HTML, as I would when
writing vanilla javascript.
2. Include the library itself as an extern file in my compilation
options.
3. Use the library using the js/ namespace and standard Javascript
interop, no need for any 'requires'.

This will throw off hundreds of warnings about "accessing <var> in
externs has no effect", but it will also guarantee that references to
any part of the Raphael library won't be munged when my own code is
advanced-compiled.

Using this technique with Raphael seems to work fine: I get advanced
mode compilation for my own code, and I can use Raphael freely. The
only downside is that I don't benefit from dead code elimination
within Raphael, and I have to live with all the warning messages from
using a normal js file as an extern file.



On Sep 30, 11:05 am, Luke VanderHart <luke.vanderh...@gmail.com>

Chas Emerick

unread,
Sep 30, 2011, 11:43:20 AM9/30/11
to cloju...@googlegroups.com
The author of Raphael was interviewed (or just blogged about it, I don't remember) on his distaste for Google Closure. I think patches along the lines of making the library safe for advanced compilation are probably a non-starter.

For my purposes, being able to bundle in libraries like Raphael is just fine; pity about the limited optimization opportunities, but until someone tackles a maximally-compatible rendering library in ClojureScript, it's not a bad tradeoff IMO.

- Chas

> --
> You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
> To post to this group, send email to cloju...@googlegroups.com.
> To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.
>

Reply all
Reply to author
Forward
0 new messages