ClojureScript :advanced builds break JS interop after a compiler version bump
0 reputation · 14 Dec 2023, 05:40 UTC
0 reputation · 14 Dec 2023, 05:40 UTC
I am upgrading a ClojureScript project's compiler and build tool to a newer release, and I want the existing :advanced optimization build to keep working against the third-party JavaScript libraries the app depends on. The :simple build is not a concern; the unresolved part is the aggressive renaming and dead-code elimination that :advanced applies through the Google Closure Compiler.
The codebase calls into external JS libraries that are loaded at runtime rather than bundled, so property and function names must survive compilation as literal strings. Some entry points are invoked from plain JavaScript outside the ClojureScript artifact. I am unsure which of these call sites need to be guarded with goog.exportSymbol/^:export metadata versus an externs file, and whether the newer compiler/GCC pairing changed how strictly missing externs are treated. I also want to know if the js> reader macro is a safe escape hatch for the few low-level calls, or if it is equally subject to renaming of the surrounding symbols.
^:export metadata, or both?js> embedded code behave differently under :advanced than under :simple?31275 reputation · 14 Dec 2023, 06:26 UTC
:optimizations :advanced
:pretty-print true
$.ajax, L.map). If the name appears as a mangled identifier like a.bc instead of the original, the Closure Compiler renamed or removed it.WARNING: No externs file for .... New warnings after the upgrade indicate missing externs that could lead to renaming.Likely explanation: A newer ClojureScript compiler bundles a newer Google Closure Compiler that enables stricter externs checking. When an external property is not declared in an externs file, the compiler may rename or drop it, breaking JS interop that worked before.
Confirmed facts:
var $ = {};
$.ajax = function() {};
var L = {};
L.map = function() {};
The js> form outputs its contents verbatim. Under :advanced it does not prevent renaming of surrounding ClojureScript symbols, but the code inside the block is not renamed because the Closure Compiler treats it as opaque JavaScript. The block can still be eliminated if its result is never used; to prevent that, either use the result or annotate the surrounding form with ^:export.
If you are accessing an external property via bracket notation with a string literal (e.g., $('#elem')['animate']), the name is safe from renaming regardless of externs. Let me know whether your calls use dot notation or bracket‑string notation so I can tell you if an externs file is strictly required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.