Cljs dead code elimination for foreign libraries?

Hey,

I started having a look at the Cljs “Javascript modules” feature https://clojurescript.org/reference/javascript-module-support with the aim of seeing if I could use just part of an npm library’s code and not pull the entire lib into my build.

Examples of what I tried and results written up here, but tl;dr it hasn’t worked out so far
https://github.com/henryw374/cljs-jsmodules-test

It’d be good to hear if anyone has achieved this with some other npm lib and what compiler setup you have?

As far as I know the advanced Closure optimizations works well on code that is written in Clojurescript, but there is a couple of things that are possible in plain Javascript that make it break. And if you use external libraries, you never know what the Javascript looks like. You can try it of course. Also see https://shadow-cljs.github.io/docs/UsersGuide.html#closure-defines.

Thanks, but in this case I do know what the code looks like and so does Google Closure via :module-type :es6

The very short answer is that dead code elimination doesn’t work with code that isn’t written for it.

So several things that the “pretend to be ESM” js-joda file does are not compatible with DCE. Basically avoid all “dist” bundled files since in that process much of the actual ESM code is lost.

Thanks Thomas, I’d love to get a longer answer :wink: and/or pointers to some more reading. The fact that the Javascript Modules page mentions DCE gave me reason to believe that it must work for some code that isn’t written in the Closure style.

FWIW I did try a foreign-lib pointed to the js-joda src dir, but doing that I get compiler errors like ’ This language feature is only supported for ECMASCRIPT6 mode or better’ . I looked about for some flags to set or something that might fix this but didn’t find anything.

The long answer is unfortunately a year long or so. Don’t really have any pointers to give. I wrote about a bunch of stuff on my blog but thats mostly about shadow-cljs and why it does the stuff it does.

Overall I have no clue anymore what CLJS does with :foreign-libs or the Javascript Modules stuff in general so I can’t comment on that.

I did however port your example to shadow-cljs since I know exactly what that does.

To compare you can look at the build-reports

In short these are 3 separate builds. One using your minimal no-deps entry and 2 using js-joda. One variant just includes the “regular” npm package (similar to what you’d get from cljsjs) and one actually trying to run everything through :advanced compilation. As you can see it does make a difference but not as much as you’d probably expect.

Note that I didn’t actually try running the compiled code at all. So no clue if it even survived the :advanced compilation (most packages nowadays don’t).

Overall the Closure Compiler is fully capable of running strict ESM code through :advanced compilation with full DCE. However the js-joda.dist.esm.js is not strict ESM code. It is some kind of processed “mess” that basically just has export added at the end to “pretend to be ESM”. All “modern” language features (eg. class) were rewritten by babel. Those produce problems for the Closure Compiler and hinder DCE.

1 Like

much appreciated… I’ll get reading