Shadow-cljs release build: how to troubleshoot issues with Google Closure advanced optimizations?

I’m developing a simple Reagent SPA with a Firebase Firestore backend. The app shows a list of quotes (for now they are just fakes I created with Faker.js) and features infinite scrolling.

Basically there are 2 steps involeved when fetching data from Firestore:

  1. The app fetches a single batch of quotes from Firestore as soon as it mounts in the DOM. It’s not a Firestore subscription, it’s a Firestore one-time query, so no Firestore subscription is involved.
  2. When the user scrolls past a certain threshold, the callback of a IntersectionObserver kicks in, and the app fetches the next batch of quotes.

Both step 1 and 2 work as expected when I run shadow-cljs watch app, or when I create a release build with :compiler-options {:optimizations :simple}. However, only step 1 works fine when I create a release build with :compiler-options {:optimizations :advanced} (the default option for shadow-cljs).

To put it in other terms, the Firestore query (step 1) still works, but infinite scrolling (step 2) breaks when I compile the app with Google Closure’s advanced optimizations.

My understanding is that Google Closure advanced optimizations strip away some symbols and break step 2.

My question is: how can I debug what broke step 2? Can you recommend any resources to understand what’s going on with Google Closure advanced optimizations?

I found this handbook, but before committing to read it I’d like to know what other options I might have.


The most likely cause of your problems are missing Externs.

Definitely turn on :infer-externs :auto and you should be getting warnings as described in the guide. This is enabled by default in newer versions (2.10.15+).

You don’t need to read the closure compiler handbook because it is not relevant to shadow-cljs.

1 Like

Thanks, I completely skipped that part of the documentation.
I have just tried :auto and I get no warnings. With :all I get plenty of warnings though (from reagent, core.async, accountant, clerk).
Tomorrow I’ll try to understand what’s going on.

Kudos for shadow-cljs. It’s an amazing tool and the documentation is top-notch!

Don’t use :all.

You can try shadow-cljs release app --pseudo-names. If that fixes things then it is likely that some of the generated code clashes with something else that is already on your page. Do you have any other JS code loaded on the page?

1 Like

Now everything works fine, either if I use --pseudo-names or not. I’m not sure what fixed the issue.

In my index.html I download Firebase and several Firebase SDKs with a <script defer />. I am using defer for the app’s bundle too (I am not doing any code splitting). I didn’t change anything here.

The last changes I did on the app were: I removed stacktrace.js, added the React integration for Sentry, added glogi for logging.
I was using StackTrace.js because I was sending the stackframes to shadow-cljs Inspect with tap>. Now I just send them to Sentry.

By the way, I saw that --pseudo-names calls .setGeneratePseudoNames for the Closure compiler options, but I couldn’t find any documentation, only this javadoc with no explanation.

“pseudo-names” still renames everything it would usually rename, just in a way that lets you recognize what the original name was. So instead of renaming cljs.core.assoc to something short like xF it renames to $cljs$core$assoc$. This is meant to help spot rename problems where xF is accidentally clashing with some other javascript on the page. If you have including other scripts on the page that tends to happen sometimes. Much less likely to clash with the longer name. It is just a debugging aid and makes the code rather large so it should not be used in production.