I’m reworking the “Launcher” for shadow-cljs
and would be interested to hear what people think about my plans. I’m undecided on which way to go since there are several options each with their own pros/cons.
The launcher is used to download maven :dependencies
and starting the JVM with the proper classpath. shadow-cljs
basically consists of 3 separate parts:
- Clojure Library doing all the work
- The Standalone Launcher
- The
npm
package providing theshadow-cljs
CLI command
shadow-cljs
supports 3 different launchers currently: lein
via :lein+project.clj
, tools.deps
clojure
via :deps+deps.edn
and its standalone version. lein
and tools.deps
are not affected by this work and will stay exactly the same as they are now. You can even skip 2,3 entirely and just use the Clojure Library directly.
Issue #1
Previously the launcher was distributed via the shadow-cljs-jar npm package which the shadow-cljs
npm package just depended on. This meant that the standalone launcher was always downloaded even when actually using the other launchers.
Issue #2
Due to the way npm
operates the launcher would be downloaded for each project separately although it rarely changes and each project could use a shared version instead. 5 projects currently means 5 copies which is not the end of the world but could be improved.
Issue #3
The launcher was very limited: download dependencies and build a classpath. This is exactly the same way tools.deps
and lein
operate. A standalone “uberjar” is used to complete that job and then a secondary JVM is launched to run the actual application. This works well but means that sometimes 2 JVMs are started. I also want to get to the point where :dependencies
can be added dynamically without restart which would require duplicated work in the Clojure Library + Launcher.
Plan A
Keep everything the way it is.
Plan B
Use the shadow-cljs
JS script to download the launcher independently from npm
. The launcher could be distributed via Github or so and shared between projects. It would only be downloaded when required, meaning lein
or tools.deps
users wouldn’t. This is also how tools.deps
and lein
work basically.
Plan C
Plan B + make the launcher a bit smarter to enable dynamic classpath additions and such.
Plan D
Actually bundle shadow-cljs
with the launcher. Saves the first initial download step, can do more optimizations for startup. Could investigate GraalVM native-image for nearly instant startup. Downside is that shadow-cljs
has far more frequent releases than the launcher. So “upgrading” would mean a larger download.
Bonus Level
Actually create a completely standalone executable. One that you could double-click to launch and even get an actual GUI application at some point. Terminal is so 20 years ago. I think we can do better and I personally want that. CLI version will stay for CI and such but developer experience would be much improved if you could click a thing instead of running 3 separate terminals or so. IMHO,YMMV. For those that remember there used to be cuttle for CLJS and the Vue world has a pretty badass UI too. There is also guppy which is fairly new but looks interesting too.
The final Question
I’m currently undecided whether to go with C or D and want the Bonus Level
Which would you prefer: A more frequent larger download (~40MB) or a less frequent ~20MB download plus a separate more frequent ~20MB download? Am I overthinking this?
If you use lein
to deps
nothing changes for you but you’ll miss out on some of the AOT optimizations (4sec startup vs 9sec startup). shadow-cljs
will stay a Clojure Library primarily and this is just about finding the most optimal way to launch it.
- I use
lein
and don’t care - I use
tools.deps
and don’t care - Plan A - screw lein/tools.deps users
- Plan B - do what is known to work
- Plan C - optimize my bandwidth
- Plan D - I got the bandwidth
- Gimme the GUI already!
0 voters