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!
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)
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
Still an open question how this compile time setup would look exactly
Just throwing this in here but @chaos has made some kind of bundler for scittle:
https://ikappaki.github.io/scittlets/
see npx scittlets pack
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
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
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)
And also Sci could figure out the order of the namespace that way by doing the loading of the namespace itself via :load-fn
Then you should still add all the necessary namespaces still. But that is at least one thing
yes, that's what I meant but it has to be :async-load-fn
ah ok cool
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
that's one way to do it of course, but I want scittle to also work without a server
Yeah exactly
I can imagine Scittle will also have some code that can be used server side for these kind of things
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
Of course doesn't have to be part of the Scittle project itself, but a specific project for that purpose
reloading is already possible by calling scittle.core.eval_script-tags() from the browser console btw
yeah, what i am missing is the support of the lifecycle hooks and custom error handling
I started out with eval_script_tags from outside of Scittle, but i think it needs to be more integrated
Because everything is async it is hard to do things at the right time otherwise. Or eval_script-tags need to support certain callbacks
"Because everything is async it is hard" welcome to JS!
I think moving to async evaluation has the most priority, after that we can see what other things we bolt on
maybe async has to become scittle2 or so, because I'm pretty sure it won't be backwards compatible
yeah sounds good. I'm also still calibrating on what I really need. Things are still fresh
scittle has been around for a few years, did you just discover it?
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
No i didn't just discover it, but I saw it more as a toy before
I had commits in early 🙂
oh yes, I now see it 😅
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)
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
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
the size would be the same as SCI
the standard library still comes along
well maybe somewhat smaller
perhaps 500kb or so
not sure
What is your main motivation for Cherry? The removal of the goog dependency?
here is how you can embed cherry in an existing CLJS build: https://github.com/squint-cljs/cherry/blob/main/doc/embed.md
similar to the use case of SCI
I gave a talk about cherry and squint at the Clojure Days 2022
it's on youtube ;)
yeah I remember. Let me check that again
Sci has so many applications that I find it hard to apply it to the reasoning for cherry. So looking at the slides now
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
there might be more use cases I've forgotten about
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
wrong link: https://github.com/nextjournal/clojure-mode
Yeah ok makes total sense
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 🙂
one more important one: squint and cherry assume ESM code by default, in existing ClojureSCript/shadow-cljs those are a bit of a hack
not that I'm fond of ESM, but that's what the JS ecosystem has moved towards
one limitation that ESM brings is that module members are immutable so something like (set! some.other/var ...) doesn't work in cherry
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
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
the code surely is easier to read and debug
Yeah so that allows us to publish npm libraries with clojure code that js people will be happy to use 🙂
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
Yeah makes sense. What would be the benefit of going with Squint otherwise? Just the bundle size?
you mean in general? easier JS interop, smaller build
so the interop is also better in Squint then in Cherry? I assumed that was equal
the interop is easier since everything in squint is just JS datastructures, no converting between JS and CLJS
ah no clj->js etc required. Ok got it
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?
similar I think
I think I would just do the cherry port, the build isn't already small anyway
btw here is pinball using scittle: https://thegeez.net/static/pinball/pinball.html and here it the exact same code using squint: https://squint-cljs.github.io/squint/?src=https://gist.githubusercontent.com/borkdude/ca3af924dc2526f00361f28dcf5d0bfb/raw/09cd9e17bf0d6fa3655d0e7cbf2c878e19cb894f/pinball.cljs
Yeah that's amazing
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 🙂
Of course you have been working hard for it to get here
Do you think the cherry port of Scittle is far away?
I will not hold it against you or anything, just curious what you think
not really I think... just need to do it (like so many things)
ok fair enough
Interesting possibility anyway
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
but... keeping existing things running also takes time
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 🙂
I am happy with Scittle anyway
scittle is very little code. you can also build your own thing very easily
especially with sci.configs, just plug the stuff in
scittle.core is around 100 lines. scittle.impl.error is mostly code you wrote I think
the rest is just about integrating other libraries like reagent
maybe that just makes it more amazing. But it helps in maintaining it
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
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
So many layers to think about 🤯
yes
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.
hmm, but integrating the other CLJS libraries would probably still have to happen in a custom build
I think this would probably be a good starting point: https://github.com/squint-cljs/cherry/blob/main/doc/embed.md
Maybe by that time other cljs libraries can use squint or cherry to create js artifacts 💪
@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).
@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
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 8000Main diff is caused by the static assets I had to add
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.
maybe we need to add a new function set-config to the replicant.env namespace 🤔
What does it do during dev or prod?
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
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