cljs-dev

dnolen 2026-03-07T22:10:34.704929Z

@thheller is the ESM output of shadow-cljs relatively popular? there was some talk of switching to only that, is that going to happen? Reading over your notes I see that you made it work by just writing to globalThis in the CLJS output for vars?

dnolen 2026-03-08T13:56:00.551719Z

but did shadow flip to this as the default?

dnolen 2026-03-08T13:57:21.116539Z

re: "adds esm import/export after optimization" - this I can't parse. If it's optimized what is import for?

thheller 2026-03-08T17:10:29.936649Z

import for code splitting and in shadow-cljs there is the concept of :js-provider. default is :shadow as in shadow-cljs provides the npm dependencies. another is :import as in (:require ["react" ...]) becomes import ... from "react" in the final output so that other tools can process it

thheller 2026-03-08T17:11:09.964809Z

:modules
   {:main
    {:init-fn shadow.cljs.ui.main/init
     :preloads [shadow.grove.dev-support
                shadow.grove.preload]}

    ;; trying something to split out features not pages
    ;; maybe thats a better pattern overall, should be less config
    :code-editor
    {:entries [shadow.cljs.ui.components.code-editor]
     :depends-on #{:main}}}}

thheller 2026-03-08T17:11:35.674169Z

this is from the build config for the shadow-cljs ui. it lazy loads the code-editor (codemirror) when needed

thheller 2026-03-08T17:12:00.830169Z

or this is another dummy config

:modules
   {:shared {}
    :a
    {:exports
     {default demo.esm.a/foo
      foo demo.esm.a/foo
      bar demo.esm.a/bar}
     :init-fn demo.esm.a/init
     :depends-on #{:shared}}
    :b
    {:exports
     {default demo.esm.b/bar}
     :depends-on #{:shared}}
    :c
    {:exports
     {default demo.esm.c/foo}
     :depends-on #{:b}}}

thheller 2026-03-08T17:12:47.580669Z

so from js you can import X from "./a.js" and that is the default export, so it gets the value of demo.esm.a/foo

thheller 2026-03-08T17:13:35.289009Z

useful in environment that expect your files to have certain exports, like a default export. some tools expect that and don't work otherwise

thheller 2026-03-08T17:14:31.788469Z

so the way this is done is shadow-cljs adds "markers" to the code that :advanced will optimized but not eliminate. those markers are then later replaced with actual export ... statements

thheller 2026-03-08T17:15:23.255189Z

https://github.com/thheller/shadow-cljs/blob/344ce44739b0b524c438cf63bc0d6de58555d5dd/src/main/shadow/build/closure/ShadowESMExports.java not a whole lot of code, but only way I could find to make export actually work (in optimized builds)

thheller 2026-03-08T17:16:28.712459Z

basically its all just normal :advanced output, just post processed a bit

thheller 2026-03-08T17:18:13.666469Z

shadow-cljs doesn't have a default when it comes to build configs. people chose which :target to use. I'd say most still use :browser, but I recommend and have helped a few people to switch to :esm. mostly to make life with code splitting and dynamically loading stuff easier

thheller 2026-03-08T17:18:51.859929Z

also have stuff for actual "ESM", but doubt anyone ever actually used this

(ns demo.esm.a
  (:require
    ["" :as x]))

thheller 2026-03-08T17:19:31.555899Z

that just becomes a normal import ... from "". meaning shadow-cljs won't try to bundle it and stuff is loaded at runtime by the browser

thheller 2026-03-08T17:20:09.058389Z

little things, mostly to get to something that can actually make full use of ESM

thheller 2026-03-08T04:15:27.675649Z

for development it just fakes ESM. basically just exports everything to globalThis yes. for optimized builds it generates normal optimized output (with code splitting) and then adds esm import/export after the optimizations. can't have the closure compiler see those, since it will compile them away. optimized code is ESM compliant and does not export anything into globalThis. this is important so that other tools can actually work with it properly.

thheller 2026-03-08T04:15:46.216949Z

my recommendation is to only use ESM these days. it makes a lot of things much simpler and the interop is just better

thheller 2026-03-08T04:16:12.422489Z

not to mention you can just import() things dynamically without the need for the goog.module.ModulerLoader stuff

thheller 2026-03-08T04:18:35.070489Z

most of the work goes into working around the closure compiler being over eager and compiling everything away that we may want

thheller 2026-03-08T04:19:32.986349Z

also their output mode for ESM is kinda useless. can't influence what to export to the outside world at all, so not using that