scittle

2025-06-20T09:19:07.087369Z

Some food for thought here. So I convinced myself that Scittle is the future for writing Cljs apps (at least an important part) and will compete with other no build process tools like htmx etc. Now after porting replicant-kanban to Scittle yesterday I am thinking how to overcome a particular challenge. Clojurescript has the ability to execute optimization steps during compile time https://github.com/cjohansen/phosphor-clj/blob/main/src/phosphor/icons.clj#L34. Or creating a specific tailwind css file. With Scittle you would have to either do this manually or yet introduce.. a server side build process kind of thing. Also just ordering the namespaces the right way is something you will not want to do manually.. you need a server side construct for this. So will Scittle still depend on some kind of build process? And is that bad? Maybe the dependency is smaller. Is it still an improvement over Cljs build tools? Curious for your opinions!

1
2025-06-20T09:24:36.704689Z

So one main advantage of Scittle, with or without build process, I see is executable version control. You check everything that you need into source control, something you will never do with a Cljs app since the diff will be too big. With Scittle they should be little, but git tree will give you the exact version without having to run an (expensive) build process. Also a build process can fail over time so maybe you lose that particular version all together. With Scittle you can easily host two or tens of versions on one single server, on demand, without any costs (just some git magic)

2025-06-20T09:30:01.120339Z

So in the case of these icons, maybe at developer time you generate an optimized part of the icons into source control. This is ok since the data is small and probably will not change over time. Especially if you have some smart printing of the edn data like alphabetically sorted keys to avoid unnecessary big git diffs. So maybe some of "compile-time" dependencies like the full icons set will never be checked in and are only part of the "compile-time" setup. The run-time version is always working and checked into git

2025-06-20T09:30:31.009509Z

Still an open question how this compile time setup would look exactly

borkdude 2025-06-20T09:30:43.888259Z

Just throwing this in here but @chaos has made some kind of bundler for scittle: https://ikappaki.github.io/scittlets/ see npx scittlets pack

🙏 1
borkdude 2025-06-20T09:31:51.703519Z

I think for ordering the namespaces I was thinking about something like an import-map but for scittle to describe which namespaces corresponds to which URL

borkdude 2025-06-20T09:32:16.151309Z

but to do this I'll have to move things to async evaluation (which is entirely possible) and then we can also use ESM library in require

2025-06-20T09:33:43.363009Z

Yeah actually that is one optimization I wanted to suggest. Right now the script tags determine the load order and also block any parallel loading. I think the loading could be seperated from the evaluation to allow for parallel loading of assets (automatically via http3 I believe)

2025-06-20T09:34:16.153039Z

And also Sci could figure out the order of the namespace that way by doing the loading of the namespace itself via :load-fn

2025-06-20T09:34:36.068479Z

Then you should still add all the necessary namespaces still. But that is at least one thing

borkdude 2025-06-20T09:34:38.696269Z

yes, that's what I meant but it has to be :async-load-fn

2025-06-20T09:34:45.789779Z

ah ok cool

borkdude 2025-06-20T09:34:59.209079Z

https://github.com/babashka/sci/blob/master/doc/async.md

👌 1
2025-06-20T09:35:11.331559Z

I created some adhoc code to parse the namespace server side to create a dependency tree to put the script tags in the right order

borkdude 2025-06-20T09:35:46.461539Z

that's one way to do it of course, but I want scittle to also work without a server

➕ 2
2025-06-20T09:35:55.884429Z

Yeah exactly

2025-06-20T09:36:16.800229Z

I can imagine Scittle will also have some code that can be used server side for these kind of things

2025-06-20T09:36:53.433769Z

For instance the kanban project is not too small so it easy to miss a namespace. Or if you make changes it is nice if everything just works

2025-06-20T09:38:08.716839Z

Of course doesn't have to be part of the Scittle project itself, but a specific project for that purpose

borkdude 2025-06-20T09:38:59.810039Z

reloading is already possible by calling scittle.core.eval_script-tags() from the browser console btw

2025-06-20T09:39:55.360109Z

yeah, what i am missing is the support of the lifecycle hooks and custom error handling

2025-06-20T09:40:40.937389Z

I started out with eval_script_tags from outside of Scittle, but i think it needs to be more integrated

2025-06-20T09:41:30.002009Z

Because everything is async it is hard to do things at the right time otherwise. Or eval_script-tags need to support certain callbacks

borkdude 2025-06-20T09:43:16.731789Z

"Because everything is async it is hard" welcome to JS!

😂 2
borkdude 2025-06-20T09:43:47.663089Z

I think moving to async evaluation has the most priority, after that we can see what other things we bolt on

borkdude 2025-06-20T09:44:22.219539Z

maybe async has to become scittle2 or so, because I'm pretty sure it won't be backwards compatible

2025-06-20T09:44:28.521139Z

yeah sounds good. I'm also still calibrating on what I really need. Things are still fresh

borkdude 2025-06-20T09:44:51.163979Z

scittle has been around for a few years, did you just discover it?

2025-06-20T09:45:32.398179Z

I find it hard to imagine Scittle is already used in mission critical apps. I would just find it hard to believe a big app was created started with Scittle (at this moment). Porting is different

2025-06-20T09:45:46.646829Z

No i didn't just discover it, but I saw it more as a toy before

2025-06-20T09:45:58.578949Z

I had commits in early 🙂

borkdude 2025-06-20T09:46:17.765099Z

oh yes, I now see it 😅

borkdude 2025-06-20T09:47:08.193509Z

another idea I'm having is to port scittle to #cherry. then we have good performance as well (not that I have noticed any performance issues with SCI in normal apps yet)

2025-06-20T09:47:10.877659Z

I have been working on some tools to improvement backend and I was postponing the frontend part. I was looking at htmx and other things until I realized with some effort Scittle could do what we want Cljs to do

2025-06-20T09:48:07.706999Z

A cherry port would be amazing. I'm guessing that would affect the size of the scittle build as well? Right now it is 1mb i saw yesterday

borkdude 2025-06-20T09:48:21.472799Z

the size would be the same as SCI

borkdude 2025-06-20T09:48:37.970279Z

the standard library still comes along

borkdude 2025-06-20T09:49:04.501909Z

well maybe somewhat smaller

borkdude 2025-06-20T09:49:10.228339Z

perhaps 500kb or so

borkdude 2025-06-20T09:49:12.318079Z

not sure

2025-06-20T09:49:38.322069Z

What is your main motivation for Cherry? The removal of the goog dependency?

borkdude 2025-06-20T09:49:50.881119Z

here is how you can embed cherry in an existing CLJS build: https://github.com/squint-cljs/cherry/blob/main/doc/embed.md

borkdude 2025-06-20T09:49:57.805689Z

similar to the use case of SCI

borkdude 2025-06-20T09:50:35.879699Z

I gave a talk about cherry and squint at the Clojure Days 2022

borkdude 2025-06-20T09:50:47.518759Z

it's on youtube ;)

2025-06-20T09:50:58.229079Z

yeah I remember. Let me check that again

2025-06-20T09:52:30.430329Z

Sci has so many applications that I find it hard to apply it to the reasoning for cherry. So looking at the slides now

borkdude 2025-06-20T09:53:25.127209Z

I can give a bit of a summary. • It's not really possible to distribute compiled CLJS libs to NPM to be consumed from JS, unless if distribute the compiled standard library for each library and even then those compiled libraries can't talk to each other using the same stdlib. cherry and squint solve that problem by distributing the standard library as an npm package which they compile against. • squint and cherry have js-await so it's easier to work with async code • squint and cherry work in babashka and JVM Clojure and are a light-weight alternative to the ClojureScript compiler that you can use to compile snippets of JS on the fly in a backend

borkdude 2025-06-20T09:53:32.114939Z

there might be more use cases I've forgotten about

borkdude 2025-06-20T09:54:22.262679Z

one cool squint use case is that you can compile a ClojureScript library and make a light-weight version for JS developers to consume. This was done with the clojure-mode CodeMirror plugin that JS developers can now use without compiling CLJS: https://github.com/clojure-emacs/clojure-mode

borkdude 2025-06-20T09:54:46.440229Z

wrong link: https://github.com/nextjournal/clojure-mode

2025-06-20T09:54:55.732619Z

Yeah ok makes total sense

2025-06-20T09:55:52.337149Z

So basically cherry and squint will give us even more leverage with Clojure code. And are better suited to work in js environments. I like it 🙂

borkdude 2025-06-20T09:56:34.518089Z

one more important one: squint and cherry assume ESM code by default, in existing ClojureSCript/shadow-cljs those are a bit of a hack

borkdude 2025-06-20T09:56:58.032519Z

not that I'm fond of ESM, but that's what the JS ecosystem has moved towards

borkdude 2025-06-20T09:57:30.505819Z

one limitation that ESM brings is that module members are immutable so something like (set! some.other/var ...) doesn't work in cherry

2025-06-20T09:58:49.651719Z

I was also wondering before if I could already use cherry or squint for what I am trying with Scittle. Then i just sticked to Scittle as I saw cherry is not feature complete yet. But I can imagine Cherry and Squint will be easier to work with and debug in the browser console. The stacktraces with Scittle are sometimes a bit hard to interpreted if it is in the compiled cljs

borkdude 2025-06-20T09:59:10.380969Z

squint typically produces code around 1-2kb when mimimized with esbuild, something which isn't possible with CLJS. so it also focuses on very minimal builds

borkdude 2025-06-20T09:59:28.097119Z

the code surely is easier to read and debug

2025-06-20T09:59:46.749939Z

Yeah so that allows us to publish npm libraries with clojure code that js people will be happy to use 🙂

borkdude 2025-06-20T10:00:58.064339Z

I think for a scittle rebuild using one of squint or cherry I would go with cherry since when integrating other libraries like replicant, those libraries will assume CLJS data structures

2025-06-20T10:01:56.725449Z

Yeah makes sense. What would be the benefit of going with Squint otherwise? Just the bundle size?

borkdude 2025-06-20T10:02:25.364129Z

you mean in general? easier JS interop, smaller build

2025-06-20T10:02:58.415739Z

so the interop is also better in Squint then in Cherry? I assumed that was equal

borkdude 2025-06-20T10:03:22.624559Z

the interop is easier since everything in squint is just JS datastructures, no converting between JS and CLJS

2025-06-20T10:03:30.588079Z

ah no clj->js etc required. Ok got it

2025-06-20T10:04:22.540709Z

Yeah I think for Cherry I am too fond of immutable structures, but I can imagine you can have to versions of scittle? scittle-squint scittle-cherry or would the design be very different?

borkdude 2025-06-20T10:05:28.837329Z

similar I think

borkdude 2025-06-20T10:07:09.213819Z

I think I would just do the cherry port, the build isn't already small anyway

2025-06-20T10:14:52.035389Z

Yeah that's amazing

2025-06-20T10:16:07.273249Z

That's one of the main things USPs of Clojure that you can reuse code in so many places/ways. I believe you had a significant part in this 😃 I told you 5 years ago that Sci would be big 🙂

2025-06-20T10:16:34.882669Z

Of course you have been working hard for it to get here

1
2025-06-20T10:19:31.135609Z

Do you think the cherry port of Scittle is far away?

2025-06-20T10:19:52.866059Z

I will not hold it against you or anything, just curious what you think

borkdude 2025-06-20T10:19:59.160429Z

not really I think... just need to do it (like so many things)

2025-06-20T10:20:15.777969Z

ok fair enough

2025-06-20T10:20:25.116049Z

Interesting possibility anyway

borkdude 2025-06-20T10:21:40.423699Z

for now just use scittle, it's good enough for most things. scittle async is one thing that might be doable in less time though. since scittle is "good enough" I've not been motivated to move to cherry yet. if cherry was everything I had to evaluate CLJS in JS, I probably would have built nbb and scittle on top of that in the first place

borkdude 2025-06-20T10:21:51.084369Z

but... keeping existing things running also takes time

2025-06-20T10:23:21.711539Z

Yeah I can see that. I have no idea how you do keep up with everything that you have created. Huge admiration for that too 🙂

2025-06-20T10:23:45.837429Z

I am happy with Scittle anyway

borkdude 2025-06-20T10:24:10.322759Z

scittle is very little code. you can also build your own thing very easily

borkdude 2025-06-20T10:24:37.658789Z

especially with sci.configs, just plug the stuff in

borkdude 2025-06-20T10:25:43.850459Z

scittle.core is around 100 lines. scittle.impl.error is mostly code you wrote I think

borkdude 2025-06-20T10:26:07.933679Z

the rest is just about integrating other libraries like reagent

2025-06-20T10:26:56.671079Z

maybe that just makes it more amazing. But it helps in maintaining it

2025-06-20T10:27:42.486299Z

Yeah I think that error code comes back in several places. I was thinking I should put it in a library or something. I'm just not as well organized as you hehe

😆 1
2025-06-20T10:34:52.534949Z

ah i just realized that the big difference between Scittle with Sci and a possibly future Scittle with Cherry (or Squint) would be that the code would be transpiled instead of interpreted. And because you run the compiler in the browser you get the same benefits (of no external compiler). I was missing this for a moment

2025-06-20T10:35:20.862819Z

So many layers to think about 🤯

borkdude 2025-06-20T10:36:26.236139Z

yes

borkdude 2025-06-20T10:37:55.906659Z

Perhaps a thing like SCI but without sandboxing, basically compiler + eval + runtime in a JS environment would be enough as a library. Then you could just include this library into your browser or Node.js and build the rest on top as a thin layer.

borkdude 2025-06-20T10:39:06.601399Z

hmm, but integrating the other CLJS libraries would probably still have to happen in a custom build

borkdude 2025-06-20T10:39:48.242139Z

I think this would probably be a good starting point: https://github.com/squint-cljs/cherry/blob/main/doc/embed.md

2025-06-20T10:42:44.774479Z

Maybe by that time other cljs libraries can use squint or cherry to create js artifacts 💪

1
Chris McCormick 2025-06-20T11:51:55.039239Z

@jeroenvandijk for the specific problem of loading just the icons you want (similar to the solution you posted) I made this: https://github.com/chr15m/scittle-iconloader Maybe it's good enough (or maybe you're already doing this but you want something better).

2025-06-20T13:39:30.568949Z

@chris358 Thanks, that looks useful indeed. I think there can be different solutions to this problem. I just noticed while porting the kanban app that cljs gives you the option to do heavy stuff at compile time to have a light runtime. In the end what I did was remove all the edn data of the icons I didn't need which left me with less than 1% of the data. I guess going forward in Scittle setup you would probably writes apps slightly different than in cljs just to work around these differences. I'm guessing your approach is a good one

2025-06-20T16:44:27.002239Z

replicant-kanban in Scitte https://github.com/cjohansen/replicant-kanban/compare/main...jeroenvandijk:replicant-kanban:scittle Works with a simple http server e.g.

python -m http.server 8000

🔥 3
2025-06-20T16:47:30.474899Z

Main diff is caused by the static assets I had to add

2025-06-20T17:01:16.989139Z

I noticed that Replicant uses some compile time config to determine if it is in https://github.com/cjohansen/replicant/blob/4059a4aea00ad40698781a93fd16b78e7735a365/src/replicant/env.clj#L44-L47. I was wondering how we can control something like that from Scittle. In this case Replicant decided that I was in prod so the https://github.com/cjohansen/replicant/blob/4059a4aea00ad40698781a93fd16b78e7735a365/src/replicant/errors.cljc#L28 were being hidden from me. In dev I would have seen the problem right away. Maybe this has to be controlled through something sci.configs to get a similar flow.

2025-06-20T17:04:44.263149Z

maybe we need to add a new function set-config to the replicant.env namespace 🤔

borkdude 2025-06-20T17:15:33.925909Z

What does it do during dev or prod?

2025-06-20T17:18:22.726779Z

In prod it catches and logs exceptions if it can't find the config :replicant/dev? https://github.com/cjohansen/replicant/blob/4059a4aea00ad40698781a93fd16b78e7735a365/src/replicant/env.clj#L47C15-L47C30. In dev you just get an exceptions. In my case I am showing uncaught exceptions so I would have used the Sci stacktrace stepper to see what happened

2025-06-20T17:19:19.468869Z

This is probably very replicant specific. I'm guessing we'll find similar cases in the future when porting other apps. Maybe easier to decide after a couple of examples