shadow-cljs

chromalchemy 2025-06-08T14:52:21.279959Z

Does the :esm target support calling js from clojure? It obviously supports calling cljs from js.. right? I have a webpack project that i want to have lightweight minimum size cljs integrated with. Ideally cljs could use jquery and other npm libs and js functions from the project. And the project js could call a cljs fn to initialize the cljs funtionality. I have had some success with :npm-module target before (doing the 2-way calling). But i struggled with bundle size issues, so i switched to ussing scittle. Now i want to try full cljs again. I was on an old version of Web pack before so that might’ve been part of the issue. Does :esm supersede :npm-module if the js build supports esm modules? Or are there specific functionalities of :npm-library that can’t be replicated?

niwinz 2025-06-09T09:06:58.348549Z

The warning can be hidden with jvm-opts:

niwinz 2025-06-09T09:08:32.165719Z

Some internal dependencies still uses unsafe which triggers warning on the latest jdk version

thheller 2025-06-09T09:10:24.439039Z

yeah, but that option sounds like shadow-cljs is gonna do something nasty in some way. I haven't seen the guava one before though, so not sure whats going on there

niwinz 2025-06-09T09:11:42.218859Z

yeah, I know that is has nothing with shadow-cljs, but this is the option we using internally for avoid see that warning every time we start a repl or shadow-cljs

thheller 2025-06-09T09:13:37.639309Z

yeah I have plans to switch to using jetty instead of undertow. that should get rid of them I hope

niwinz 2025-06-09T09:16:17.014019Z

I don't think it is undertow, we are using undertow (our own binding) and the warning we see on starting backend is on netty:

thheller 2025-06-09T09:16:48.180709Z

that is so weird. how is everyone seeing different messages 😛

thheller 2025-06-09T09:17:51.324849Z

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.jboss.threads.JBossExecutors (file:/Users/thheller/.m2/repository/org/jboss/threads/jboss-threads/3.5.0.Final/jboss-threads-3.5.0.Final.jar)
WARNING: Please consider reporting this to the maintainers of class org.jboss.threads.JBossExecutors
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release

thheller 2025-06-09T09:18:01.772589Z

WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by com.sun.jna.Native in an unnamed module (file:/Users/thheller/.m2/repository/net/java/dev/jna/jna/5.16.0/jna-5.16.0.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

niwinz 2025-06-09T09:18:14.567959Z

It all depends on the dependencies. on starting shadow-cljs i see the same warning that has been shown in this chat thread.

thheller 2025-06-09T09:18:27.300339Z

is what I get on java24

niwinz 2025-06-09T09:19:04.977999Z

on starting penpot backend which uses undertow as http server, the warning is on Netty (is a dependency of AWS sdk, that has nothing in common with undertow)

thheller 2025-06-09T09:21:48.591589Z

still I'd rather not have these at all since they'll just confuse users and are completely meaningless, at least for now 😛

niwinz 2025-06-09T09:22:27.671469Z

agree

thheller 2025-06-08T14:53:25.618669Z

> lightweight minimum size cljs

thheller 2025-06-08T14:53:50.666039Z

what do you expect here? I'd say about 90kb is the minimum, ~30kb gzip. that is CLJS only, goes up the more CLJS side stuff you add of course.

thheller 2025-06-08T14:54:06.803809Z

and yes it can do both ways

thheller 2025-06-08T14:56:03.415069Z

build size wise it won't get much smaller than :npm-module, a little but no more than 5% I'd guess

chromalchemy 2025-06-08T14:56:20.105269Z

Thats fine, i think i was getting megabytes before!

thheller 2025-06-08T14:56:46.313389Z

probably didn't do a release (i.e. :advanced optimized) build then

thheller 2025-06-08T14:57:08.855009Z

watch/compile will always be several megabytes no matter what target

chromalchemy 2025-06-08T15:00:51.116049Z

I thinks :advanced was breaking for me, so I turned optimizations down. But I’m really not bringing in any crazy libraries to cljs. It should be pretty straightforward, but it’s stumped me multiple times for multiple hours. Again, older Web pack and node versions in the mix were hard to rule out, but that’s been updated now.

thheller 2025-06-08T15:01:38.353849Z

webpack will have almost zero impact on this, so doesn't really matter which version

thheller 2025-06-08T15:01:57.763649Z

turning even to :simple will still be at least 2mb min I think. has been a while since I checked that.

chromalchemy 2025-06-08T15:02:18.966259Z

Btw, the js build runs on node 20. That should be OK right? Since the shadow CLJS build runs in its own process on a newer version of nude.

thheller 2025-06-08T15:02:25.560639Z

also irrelevant. the build process runs on the JVM

thheller 2025-06-08T15:04:05.756779Z

if you had :npm-module working then thats about the baseline you can expect. just need a release build with full :advanced to get anything reasonable size wise

thheller 2025-06-08T15:08:00.527469Z

to answer the last part of your question :esm or :npm-module don't really matter if they run through webpack again anyway. the result is more of less identical. slightly better for :esm but very minimal and pretty much the only difference is how the interop looks

thheller 2025-06-08T15:09:53.504519Z

there is also an undocumented :esm-files target, which is pretty much :npm-module but instead of using commonjs require/exports uses ESM import/export ... but this is all really pretty much irrelevant with regards to build size

thheller 2025-06-08T15:10:37.218729Z

kinda hard to talk about in an abstract way, would help to have some actual code/examples 😛

chromalchemy 2025-06-08T15:12:35.399239Z

Thanks. I’ll post a minimal repo. In a bit.

chromalchemy 2025-06-08T15:15:10.847889Z

So with :esm-files i wouldn’t need to lint my fns with :export? And could use the :modules in shadow edn? If that worked for me, what would be the downside of just using the :esm target instead?

chromalchemy 2025-06-08T15:16:46.021849Z

Also in this context of 2-way calling, i would need to set :js-provider right? To :external ?

thheller 2025-06-08T15:25:55.564319Z

:js-provider :import and I'd recommend setting up an alias on the webpack side. so that you can (:require ["@my-project/whatever.js" ...]) on the CLJS side and have webpack resolve that to src/whatever.js or so

thheller 2025-06-08T15:27:50.350389Z

can then also setup an alias to have import ... from "@cljs/foo.js" or so

thheller 2025-06-08T15:28:24.159719Z

:esm is a bit more verbose on the config side via :modules, leading to minimally smaller output (less boilerplate per ns)

thheller 2025-06-08T15:28:40.086239Z

:esm-files is same as :npm-module and requires individual :export tags yes, :esm does not since its all from the build config

chromalchemy 2025-06-08T18:56:15.771729Z

With this current state shadow-cljs is failing to even compile. Giving this error:

thheller 2025-06-08T18:56:54.367549Z

:entries and :init-fn is not needed. only :exports, unless you actually want the :init-fn to be called when the script is loaded

thheller 2025-06-08T18:57:29.033589Z

:init-fn expects a singular symbol, so the error above is because of the extra vector

thheller 2025-06-08T18:57:49.717489Z

:init-fn gbo.core/my_cljs_fn, but again, seems misplaced here

chromalchemy 2025-06-08T18:57:49.923899Z

Right now I using that fn double to test calling directly, and to load by default with module.

thheller 2025-06-08T18:58:03.912629Z

ah

thheller 2025-06-08T18:58:26.451729Z

:entries [gbo.core] is redundant because it is inferred from :exports and :init-fn

thheller 2025-06-08T18:58:51.447669Z

:dev {:compiler-options {:optimizations :simple}} does absolutely nothing because :dev builds do not apply any optimizations whatsoever (to CLJS code)

chromalchemy 2025-06-08T18:59:32.152009Z

so if i have either of those, i don’t need :entries

thheller 2025-06-08T18:59:42.592929Z

correct

thheller 2025-06-08T19:01:14.195069Z

import cljs, { keyword } from "goog:cljs.core";
import { my_cljs_fn } from "/assets/js/cljs-output/core.js";

my_cljs_fn("cljs fn called explicitly from exports");

// module initialization should also run my_cljs_fn with default message

// call cljs fn explicitly from js
cljs.my_cljs_fn("cljs fn called with cljs keyword");

thheller 2025-06-08T19:01:17.112459Z

this is basically all wrong

thheller 2025-06-08T19:01:49.644749Z

import cljs, { keyword } from "goog:cljs.core"; webpack has no clue what this is and it doesn't work

chromalchemy 2025-06-08T19:02:22.342689Z

Ok cool. Shadow compiles now. But i do get these warnings

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/Users/ryan/.m2/repository/com/google/guava/guava/32.1.2-jre/guava-32.1.2-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release

thheller 2025-06-08T19:02:24.048539Z

import { my_cljs_fn } from "/assets/js/cljs-output/core.js"; the :module was named :main, thus generating only assets/js/cljs-output/main.js

thheller 2025-06-08T19:04:06.154679Z

the warnings are kinda annoying, but not related to anything shadow-cljs does

thheller 2025-06-08T19:04:26.939479Z

java is getting a bit "loud" with their deprecation warnings

chromalchemy 2025-06-08T19:12:26.692509Z

Ok. I tried running it and got this error from the project runner. Any insight? I havent customized any webpack config for this minimial test. There are 4 webpack config. files at the bottom of the repo. I’m not sure where to start. (ps I haven’t read up on the aliases thing yet..)

thheller 2025-06-08T19:13:09.482759Z

I already told you that this is not supported and will not work in webpack. just remove that import, it has no substitute

thheller 2025-06-08T19:13:20.502689Z

if you want to create keywords from js add it to your exports

thheller 2025-06-08T19:13:37.572129Z

import { keyword, my_cljs_fn } from "/assets/js/cljs-output/main.js";

thheller 2025-06-08T19:13:58.339809Z

:exports {my_cljs_fn gbo.core/my_cljs_fn keyword cljs.core/keyword}

thheller 2025-06-08T19:15:44.407349Z

FWIW :release {:compiler-options {:optimizations :advanced}} in the build config also does absolutely nothing because its already the default

chromalchemy 2025-06-08T19:24:24.580499Z

Its running!! I got the fn confirmations in the browser console! 🎉

👍 1
chromalchemy 2025-06-08T19:26:31.856139Z

Just for reference, the dev build stats from webpack. Gonna try release now.

chromalchemy 2025-06-08T19:28:32.499419Z

Do i need to manually clear the output directory when changing builds (from dev to release?)

thheller 2025-06-08T19:29:17.596279Z

not really no

chromalchemy 2025-06-08T19:32:37.338269Z

Ok. Browser test worked with release built too 🙌 The webpack stats from release:

thheller 2025-06-08T19:33:23.065439Z

looks about right. shadow-cljs can also generate build reports btw. https://shadow-cljs.github.io/docs/UsersGuide.html#build-report

chromalchemy 2025-06-08T19:35:35.676589Z

> I already told you that this is not supported and will not work in webpack. Were you referring to trying to load cljs as a symbol in the js file?

chromalchemy 2025-06-08T19:36:10.665089Z

(which i think is what i did with :npm-module)

thheller 2025-06-08T19:36:13.652669Z

import cljs, { keyword } from "goog:cljs.core"; this line. all of it. every single thing 🙂

thheller 2025-06-08T19:36:35.916559Z

the only thing that you can use reliably is what is in your :exports basically

chromalchemy 2025-06-08T19:38:14.358949Z

Ok, thanks for the clarification.

thheller 2025-06-09T06:31:19.688459Z

I already answered that as well. see Can't resolve 'product' in '/Users/ryan/dev/gbo/theme/assets/js/cljs/cljs-runtime' resolve 'product' ...

thheller 2025-06-09T06:31:42.288479Z

["product" :as product-page] as the require is technically correct

thheller 2025-06-09T06:32:02.489369Z

but webpack will see it as import * as X from "product" and must resolve that somehow

thheller 2025-06-09T06:32:09.701099Z

since its not a npm package is can't find it, thus the error above

thheller 2025-06-09T06:33:30.923049Z

and the JS code is exporting a default class. so not a function you will directly call, but instead a new object you'd need to instantiate first

thheller 2025-06-09T06:47:21.382069Z

"/assets/js/theme/product.js" as the require means shadow will try to find it on the classpath. I'd recommend going with a webpack alias as linked above, but you can write (:require ["esm:/assets/js/theme/product.js$default" :as Product])

thheller 2025-06-09T06:48:19.384779Z

then (.my_js_product_method (Product. i-dont-know-what-context-is))

thheller 2025-06-09T06:48:47.191829Z

or (:require ["esm:/assets/js/theme/product.js" :as product]) and (product/my_js_top_level_fn)