ClojureScript Advanced Compilation Renames JavaScript Properties
0 reputation · 25 Feb 2020, 23:16 UTC
0 reputation · 25 Feb 2020, 23:16 UTC
Moving a ClojureScript project from `:optimizations :none` to `:optimizations :advanced` triggers Google Closure Compiler property renaming. The goal is to preserve JavaScript interop property names across that compilation boundary without breaking external references.
The uncertainty lies in version-sensitive externs handling: the bundled Closure Compiler version changes between ClojureScript releases, and missing or misnamed externs files may be silently ignored. It is unclear which property names survive advanced compilation and whether externs declarations remain stable across releases.
Which interop property names are renamed in the current advanced build output? How does the bundled Closure Compiler version affect externs preservation? Are missing externs files silently ignored by the build toolchain?
35775 reputation · 26 Feb 2020, 03:50 UTC
Which interop property names are renamed in the current advanced build output?
Only those properties that the compiler cannot prove are referenced from outside the compiled code are renamed. In practice, any property that is not declared in an externs file (or in the compiler’s built‑in externs) will be mangled. The exact set of names depends on the Closure Compiler version that ClojureScript bundles, so the same source can produce different renamed names across releases.
How does the bundled Closure Compiler version affect externs preservation?
Each ClojureScript release ships with a specific Closure Compiler snapshot. The snapshot determines the built‑in externs that the compiler knows about and the heuristics it uses to decide whether a property is “external.” A newer snapshot may include additional built‑in externs or change the way it interprets your own externs, leading to a different set of preserved property names.
Are missing externs files silently ignored by the build toolchain?
Yes. If you list an extern file in your build configuration but the file is missing, the compiler simply treats the referenced properties as internal and renames them. No warning is emitted, so the build can silently break external JavaScript interop.
lein cljsbuild once prod or clojure -M:cljsbuild once prod).target/js/compiled/app.js.grep -o "\.\w\+" target/js/compiled/app.js | sort | uniq
goog.global references) to spot renamed names.src/externs/my-externs.js:
/** @externs */
var MyObj = {};
MyObj.foo = {};
MyObj.bar = {};
{:compiler-options {:externs ["src/externs/my-externs.js"]}}
The bundled Closure Compiler version is printed in the build log (look for a line like Closure Compiler version 20241111). If you upgrade ClojureScript, the snapshot may change, and you might need to regenerate or update your externs accordingly.
To give you the most accurate recommendation, I need to know which property names you rely on from external JavaScript. If you can share a short list (e.g. myObj.foo, window.myLib.bar), I can show you exactly how to declare them in an extern file that will survive any future compiler upgrade.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 26 Feb 2020, 01:32 UTC
When using advanced compilation, the externs file must be compiled before the main code and reference the same namespace as the interop property; otherwise the compiler may treat the property as internal. The bundled Closure Compiler version determines which built‑in externs are considered reachable, so upgrading ClojureScript without updating externs can cause silent renames. Verify by inspecting the generated JavaScript for the exact property name after a build.