shadow-cljs

borkdude 2025-10-03T12:25:46.526669Z

If I run npx shadow-cljs run shadow.cljs.build-report client report.html in the reagent project, do I get a build report as produced with advanced CLJS? 🧡

βœ… 1
thheller 2025-10-06T07:37:49.542479Z

hey. completely missed this thread. seems like you sorted out the sizes? as for using lite mode you can just use deps.edn local root or git dep for cljs. that'll override the shadow-cljs preferred version. since there were no breaking changes that should be just fine. you should also be able to just set :compiler-options {:lite-mode true :elide-to-string true} for it to function.

thheller 2025-10-06T07:38:01.611039Z

I have not actually tested that, but see no reason why that wouldn't work.

borkdude 2025-10-06T07:39:08.689269Z

I did try this and it failed with some mysterious errors. If I'm back at the kbd today I can try again

thheller 2025-10-06T07:40:18.384039Z

I mean a dev build will almost certainly not work, but release builds should be fine

thheller 2025-10-06T07:41:30.550289Z

I honestly don't quite see the point of lite-mode though. react alone is like 5 times of what is saved, so seems like a drop of water in a very big bucket πŸ˜‰

thheller 2025-10-06T07:42:26.014609Z

still curious about it though. will try to test myself when I find the time.

borkdude 2025-10-06T07:43:40.565719Z

deps.edn:

{:paths ["src"]
 :aliases
 {:dev {:extra-paths ["test" "examples/todomvc/src" "examples/simple/src" "examples/geometry/src" "demo"]
        :extra-deps {org.clojure/clojurescript {:git/sha "f9a6856d91e45377391406fc34a581bc4043615e"
                                                :git/url ""}
                     doo/doo {:mvn/version "0.1.11"}
                     funcool/promesa {:mvn/version "11.0.678"}
                     thheller/shadow-cljs {:mvn/version "3.2.1"}}}}}

$ cat shadow-cljs.edn
{:source-paths ["src" "test" "examples/todomvc/src" "examples/simple/src" "examples/geometry/src" "demo"]
 :dev-http {8090 {:roots ["site/public" "target/shadow-cljs/client/public" "classpath:public"]}}
 :builds
 {:client {:target :browser
           :output-dir "target/shadow-cljs/client/public/js"
           :asset-path "/js"
           :modules {:main {:entries [reagentdemo.prod]}}
           :devtools {:ignore-warnings true}
           :compiler-options {:warnings {:fn-deprecated false}
                              }}
  :test {:target :karma
         :output-to "target/shadow-cljs/resources/public/js/karma.js"
         :output-dir "target/shadow-cljs/resources/public/js"
         :asset-path "js"
         :source-map true
         :compiler-options {:infer-externs :auto}
         :ns-regexp "(reagenttest\\.test.*|reagent\\..*-test)"}}
 :deps {:aliases [:dev]}
 #_#_:dependencies [[doo "0.1.11"]
                [funcool/promesa "11.0.678"]]}
npx shadow-cljs run shadow.cljs.build-report client report.html
https://gist.github.com/borkdude/ce742cb399d5a1d8afd7e98af810268c

borkdude 2025-10-06T07:44:18.298749Z

seems I have to fix the source paths in deps.edn

borkdude 2025-10-06T07:44:54.316989Z

hmm, those paths already exist in the alias so should be ok

thheller 2025-10-06T07:55:30.646849Z

hmm yeah weird. seems like that :jsdoc is incorrect

thheller 2025-10-06T07:56:48.904559Z

I have never bothered with type annotations for the closure compiler, so not sure what this means

thheller 2025-10-06T07:57:22.778019Z

shadow-cljs uses a newer closure compiler version that regular cljs, so maybe thats why its passing regular tests?

thheller 2025-10-06T07:58:01.485809Z

not sure I set anything in shadow-cljs that would affect this otherwise. I even opt out of some jsdoc related warnings

thheller 2025-10-06T07:59:36.109969Z

ah, goog-define already infers the type, so it just doubled. dunno why that extra jsdoc is there then https://github.com/clojure/clojurescript/blob/df04ee2c41ce7683b52c5793d0154ca88771f0f7/src/main/clojure/cljs/core.cljc#L758-L762

thheller 2025-10-06T08:01:11.751879Z

ah no I just remembered that I do in fact override the goog-define macro

thheller 2025-10-06T08:03:47.281809Z

IIRC I only added this because I wanted to update the closure compiler but CLJS was using an older incompatible version, so had to override. I guess this isn't needed anymore.

juhoteperi 2025-10-06T10:30:55.134729Z

@borkdude Reagent shadow-cljs.edn is now updated so that the build from release command doesn't include the test suite, and matches the public site

πŸŽ‰ 1
thheller 2025-10-06T12:47:30.887289Z

small suggestions but since reagentdemo.main is only calling (core/init!) you might as well get rid of that one entirely and just use :modules {:main {:init-fn reagentdemo.core/init!}} in the build config

juhoteperi 2025-10-06T12:49:42.330009Z

I'm still keeping cljsbuild configs around and this seems work for those also

πŸ‘ 1
juhoteperi 2025-10-06T13:01:01.453289Z

There is also script to build the demo site with cljs.main + webpack: ./test-environments/bundle-adv/build.sh

❯ ls -l target/cljsbuild/prod-npm/public/js/main.js target/shadow-cljs-release/client/public/js/main.js target/bundle-adv/resources/public/js/main.js
-rw-rw-r-- 1 juho juho 551069 loka    6 15:57 target/bundle-adv/resources/public/js/main.js
-rw-rw-r-- 1 juho juho 547944 loka    6 15:58 target/cljsbuild/prod-npm/public/js/main.js
-rw-rw-r-- 1 juho juho 556878 loka    6 15:58 target/shadow-cljs-release/client/public/js/main.js

juhoteperi 2025-10-06T13:01:30.862939Z

tiny differences between the artifact sizes with shadow-cljs, cljs.main+webpack, cljsbuild+npm-deps

borkdude 2025-10-06T13:24:58.659619Z

And with lite?

juhoteperi 2025-10-06T13:28:19.152399Z

I guess I can add a new build for that, based on the cljs.main version

thheller 2025-10-06T13:28:29.672059Z

if you want to tweak the size a bit you can set :compiler-options {:shadow-keywords true}. shaves off a few pct with no downsides

thheller 2025-10-06T13:29:17.081819Z

probably not too much. build doesn't seem to contain that many keywords

borkdude 2025-10-06T13:30:07.101159Z

Hiccup does contain keywords though?

juhoteperi 2025-10-06T13:31:27.622989Z

556878 -> 549788

thheller 2025-10-06T13:32:19.181669Z

yeah about what I expected. many different keywords is what I meant to say. that option has the most impact when lots of namespaced keywords are involved. don't see many in the code for that build

πŸ‘ 1
juhoteperi 2025-10-06T13:42:08.716939Z

I'm getting pretty much the same artifact size for lite-mode + elide-to-string. Even a bit bigger output.

juhoteperi 2025-10-06T13:45:55.002009Z

Ah hm the options isn't working correctly for some reason, I see both cljs.core.Vector cljs.core.PersistentVector uses.

borkdude 2025-10-06T15:10:58.409389Z

Can you push a branch or something with lite mode? Perhaps we can show it to David to see if there is a bug in lite

borkdude 2025-10-03T12:25:56.279749Z

I'm seeing + react-dom @ npm: 19.1.0 337.58 KB 36.2 % + org.clojure/clojurescript @ mvn: 1.12.42 304.06 KB 32.6 % + demo 116.61 KB 12.5 % + src 36.26 KB 3.9 % which looks a bit much?

borkdude 2025-10-03T12:27:21.222909Z

is it possible to also see the gzipped sizes for individual packages? I guess not since zipping the whole is more efficient than the parts

p-himik 2025-10-03T12:51:06.198739Z

Builds reports are generated for release builds, yes. But what do you mean by "a bit much"? As compared to what?

borkdude 2025-10-03T12:53:02.135219Z

compared to numbers advertised elsewhere about react. I also noticed that the current reagent demo is 150kb gzipped. why has it grown with 100kb (gzipped)? not complaining, just trying to understand. context: https://mastodon.social/@borkdude/115310243716064865

juhoteperi 2025-10-03T12:56:04.189989Z

Does it include both the minimized and debug file?

borkdude 2025-10-03T12:56:35.893259Z

I don't know

borkdude 2025-10-03T12:56:44.095929Z

when I do a release build, the size is the same

juhoteperi 2025-10-03T12:56:52.555039Z

"+" on the row shows the invidual files

p-himik 2025-10-03T12:56:54.943349Z

compared to numbers advertised elsewhere about reactGotta know what's being used and done exactly to compare apples to apples. No actual idea but could be that the React is very amenable to tree shaking that GCC doesn't do. From the context: > React was 35K-40K gzipped Just tested - react-dom-client.production.js.gz from 19.2.0 is already 94 kB.

juhoteperi 2025-10-03T12:57:40.437679Z

Closure could optimize (tree-shake) JS files, but that breaks many JS libs, so Shadow-cljs doesn't use that for npm deps.

borkdude 2025-10-03T12:57:54.871319Z

hmm

juhoteperi 2025-10-03T12:57:58.970779Z

Shadow-cljs also allows using webpack or other tools for tree-shaking

borkdude 2025-10-03T12:58:07.474519Z

so if I would run this through esbuild with --minify it could get smaller

juhoteperi 2025-10-03T12:58:11.218459Z

yes

borkdude 2025-10-03T12:58:56.401829Z

minified by esbuild: $ ls -lat /tmp/min.js -rw-r--r-- 1 borkdude wheel 947936 3 okt. 14:58 /tmp/min.js same size basically

p-himik 2025-10-03T12:59:02.401349Z

No clue how React's react-dom is split into files, but files in v18 seem to be significantly smaller than in v19.

juhoteperi 2025-10-03T12:59:29.844579Z

I wrote about using ESBuild a white back: https://www.metosin.fi/blog/2024-09-05-using-shadow-cljs-with-esbuild Though the shadow-cljs integration with :shadow.build/stage there is unnecessarily complex and there should be a better way.

juhoteperi 2025-10-03T13:00:04.844459Z

I don't think you can just pass the shadow-cljs output to esbuild

juhoteperi 2025-10-03T13:00:20.414489Z

at least if it is the regular browser output

juhoteperi 2025-10-03T13:00:41.784589Z

there won't be the es6 module imports etc. there anymore for esbuild to process?

borkdude 2025-10-03T13:00:53.905089Z

true

juhoteperi 2025-10-03T13:01:36.509259Z

:js-provider :external works by finding the cljs ns require forms targetting npm libs -> shadow-cljs writes out a entry file for those libs and then you give just that entry file to esbuild

borkdude 2025-10-03T13:01:48.804339Z

worth a shot indeed

borkdude 2025-10-03T13:02:55.469879Z

FWIW, these are the files in the build report:

Source	Optimized
node_modules/react-dom/cjs/react-dom-client.production.js	167.43 KB
node_modules/react-dom/cjs/react-dom-server.browser.production.js	84.56 KB
node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js	81.51 KB
node_modules/react-dom/cjs/react-dom.production.js	3.3 KB
node_modules/react-dom/client.js	273
node_modules/react-dom/index.js	270
node_modules/react-dom/server.browser.js	245

juhoteperi 2025-10-03T13:03:58.718319Z

those server files being included isn't normal

borkdude 2025-10-03T13:04:05.382459Z

with js-provider: :main [JS: 581.51 KB] [GZIP: 139.43 KB]

borkdude 2025-10-03T13:04:46.878319Z

when I then minify and bundle the file with esbuild: same size:

esbuild --bundle --minify target/shadow-cljs/client/public/js/main.js > /tmp/min.js

borkdude 2025-10-03T13:04:52.968939Z

947767

juhoteperi 2025-10-03T13:05:16.708559Z

oh right you are looking at reagent demo site

juhoteperi 2025-10-03T13:05:25.025119Z

that isn't very typical project

borkdude 2025-10-03T13:05:36.802439Z

no wait I didn't release build yet, just the build report. wait a sec

borkdude 2025-10-03T13:06:14.528119Z

yes, the demo site, since I was comparing with: https://chr15m.github.io/eucalypt/ which is of course not a fair comparison since eucalypt is just a cheap imitation of reagent

juhoteperi 2025-10-03T13:06:37.712679Z

the normal entrypoint for reagent demo site also contains full reagent test suite

juhoteperi 2025-10-03T13:06:45.512909Z

which also tests react-dom/server uses

juhoteperi 2025-10-03T13:06:51.492549Z

which a normal app wont do

juhoteperi 2025-10-03T13:07:33.012859Z

todomvc for example shows 170KB for react-dom

borkdude 2025-10-03T13:08:26.885789Z

ok, with js-provider external I get 587473. same with --bundle - it seems esbuild isn't picking up. ah ok. what is the non-normal entry point for the reagent site or is it always built like this?

juhoteperi 2025-10-03T13:08:54.788919Z

hmhm, it could be the site is always built like this

juhoteperi 2025-10-03T13:09:33.140139Z

you can switch :modules {:main {:entries [http://reagentdemo.dev]}} -> :modules {:main {:entries [reagentdemo.prod]}}

borkdude 2025-10-03T13:09:42.614059Z

nice! let me test

juhoteperi 2025-10-03T13:10:01.855149Z

Might be it should be included in the config for release builds... maybe I have lost it accidentally at some point

borkdude 2025-10-03T13:10:14.487989Z

much better: 361617

borkdude 2025-10-03T13:10:52.717699Z

o wait, this was still with external providr

borkdude 2025-10-03T13:11:50.422769Z

That's more like it:

borkdude 2025-10-03T13:11:59.474539Z

Perhaps you are using the merge option at site deploy time

borkdude 2025-10-03T13:12:07.454889Z

because this looks similar to what's currently deployed

juhoteperi 2025-10-03T13:12:33.776639Z

deployed site doens't use shadow-cljs in fact

juhoteperi 2025-10-03T13:12:52.025839Z

it is using the lein-cljsbuild version

juhoteperi 2025-10-03T13:13:22.380429Z

that is why shadow-cljs config doesn't use that prod entrypoint

borkdude 2025-10-03T13:13:59.012749Z

I see! I wanted to see the build report, this is why I looked at this

juhoteperi 2025-10-03T13:14:09.644239Z

and it using clojurescript compiler npm-deps... which hmm, probably passes React code through Closure Compiler also

juhoteperi 2025-10-03T13:14:27.471719Z

I have tested that React is one of JS libs that does seem to work after it goes through Closure Compiler

juhoteperi 2025-10-03T13:14:43.612189Z

So the deployed artifact might be different size vs shadow-cljs

borkdude 2025-10-03T13:14:56.036259Z

looks similar though, 150kb gzipped

borkdude 2025-10-03T13:15:04.086019Z

compared to current "production" site

Roman Liutikov 2025-10-03T13:15:40.685889Z

just ran a quick test locally bare bones uix app 303kb, 85kb gzipped same in reagent 335kb, 93kb gzipped

πŸ‘ 1
Roman Liutikov 2025-10-03T13:18:54.859939Z

> I have tested that React is one of JS libs that does seem to work after it goes through Closure Compiler React's production builds distributed via NPM are in fact built with Closure Compiler

1
juhoteperi 2025-10-03T13:20:39.062959Z

What Closure Compiler or other JS bundler could do, is to through out some exported functions that project doesn't use. Though that would also require that those functions are only users of some parts of React internals so those can be dropped. But Reagent anyone uses big part of the API so likely there isn't much that could be dropped.

πŸ‘ 1
borkdude 2025-10-03T13:24:02.440679Z

Probably they would have split it up in multiple modules if you could save a lot of space, like they did with react-dom/server

borkdude 2025-10-04T09:57:55.128489Z

I wonder if we can re-test this with CLJS master with lite mode and elide toString

borkdude 2025-10-04T09:58:26.458929Z

Not sure how to do this with shadow from CLJS master in reagent’s shadow config

juhoteperi 2025-10-07T09:16:51.016179Z

@borkdude https://github.com/reagent-project/reagent/pull/642

πŸ™ 1
borkdude 2025-10-07T09:47:19.931779Z

Cool, I posted the link in #cljs-dev