In this post I'll give updates about open source I worked on during July and August 2025.
To see previous OSS updates, go here.
I'd like to thank all the sponsors and contributors that make this work possible. Without you the below projects would not be as mature or wouldn't exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.
Current top tier sponsors:
Open the details section for more info about sponsoring.
If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. Thank you!
Although summer hit Europe and I made a train trip to Switzerland for some hiking with my wife, OSS activity continued in the borkiverse. 20 projects saw updates. As usual, babashka, SCI and clj-kondo saw the most activity.
One of the big things I’m looking forward to is speaking at Clojure Conj 2025. At the risk of sounding a bit pretentious, the title of my talk is "Making Tools Developers Actually Use". Babashka started as a quirky interpreter "nobody had asked for" but now many Clojure developers don't want to live without it. Clj-kondo started out as a minimal proof-of-concept linter and now is widely used tool in Clojurian's every day toolset and available even in Cursive today. In the talk I want to reflect on what makes a tool something developers (like myself) actually want to use. I'm excited about this opportunity and about my first time visiting the Conj (don't ask me how I got the Clojure Conj cap on the photo above). Given the rest of the schedule, it's something I wouldn't want to miss.
For babashka, my main focus has been making it feel even more like regular Clojure. One example is the change in how non-daemon threads are handled. Previously, people had to sometimes add @(promise)
to keep an httpkit server alive. Now babashka behaves like clojure -X
in this regard: if you spawn non-daemon threads, the process waits for them. It’s looks like a small change, but it brings consistency with JVM Clojure, something I'm always aiming for more with babashka. If you want the old behavior, you can still use --force-exit
. While implementing this I hit an interesting bug with GraalVM and also found out that clojure -X
sometimes stalls when using agents. Maybe more on this next time.
Another change that was introduced is that when code is evaluated through load-string
or Compiler/load
(which is the same thing in bb), vars like *warn-on-reflection*
are bound. This fixes a problem with loading code in non-main threads. E.g. @(future (load-string "(set! *warn-on-reflection* true)"))
would fail in previous versions of babashka. You might wonder why you would ever want to do this. Well, a similar thing happens when you execute babashka tasks in parallel and that's where I ran into this problem.
SCI, the interpreter under the hood of babashka and several other projects, got some critical fixes as well. I detected one somewhat embarrasing bug when loading clojure+.hashp
in babashka. It had code that looked like:
(def config {})
(let [config {}
_ (alter-var-root #'config (constantly config))
]
...)
In the expression (alter-var-root #'config (constantly config))
the var #'config
was mistaken for the local config
since SCI's analyzer used a resolve
-like function that also resolves locals. This fails horribly. In 6 years of SCI it's the first time I encountered this bug though. After fixing this problem, I noticed that babashka's CI acted up. On every commit, babashka CI tests dozens of Clojure libraries by running their test suites. I noticed that specter's tests were failing. It turned out that one test actually worked prior to fixing the above bug exactly because the SCI analyzer's resolve
returned a node that evaluated to a local value. But there is no way I could just leave that bug in, so I had to make a pull request to specter as well to set this straight. A new specter version was released that works both with older version of babashka and the new version.
One other headscratcher in SCI was on the ClojureScript side of things and had to do with munging. In interop like (.-foo-bar #js {:foo-bar 1})
ClojureScript munges the field name in the interop form to foo_bar
but in the object it stays "foo-bar"
. The munging of this name wasn't applied in SCI as an oversight. So in SCI (and thus in nbb, joyride, scittle, etc.) the above expression would return 1
whereas in ClojureScript it would return nil
. In contrast, (.-foo-bar #js {:foo_bar 1})
would return nil
in SCI but 1
in CLJS. Although fixing this could mean a breaking change in SCI-based scripting environments I decided to align it with CLJS anyway, as switching between SCI and CLJS should not introduce these kinds of surprises.
Other improvements in SCI were made in the area of better using type hints on instance method interop.
And then there’s clj-kondo, the linter that is supposed to spark joy ✨, as far as a linter is able to do that in a developer's life. Two new linters were added, including one that catches suspicious uses of locking. This linter was inspired by a similar rule in splint. Lots of smaller improvements were made like sorting findings and imported files such that they are consistent across multiple runs that use the --parallel
option and across operating systems. And as usual bugfixes and preventing false positives.
One happy improvement to scittle is that referencing a library that was introduced by a <script>
tag now was made a lot easier. You can find the docs about that here. The tl;dr of this is that when a library registers itself as a global, you can just use that global in :require
now: (require '["JSConfetti" :as confetti])
.
Of course, none of this happens in isolation. I’m deeply grateful to the community and the sponsors who make this work sustainable: Clojurists Together, Roam Research, Nextjournal, Nubank, and many other companies and individuals. Every bit of support means I can keep refining these tools, fixing edge cases, and thinking about the long-term direction.
Here are updates about the projects/libraries I've worked on in the last two months in detail.
babashka: native, fast starting Clojure interpreter for scripting.
1.12.2
@(promise)
anymore when you spawn an httpkit server, for example. For futures and agents, bb uses a thread pool that spawns daemon threads, so that pool isn't preventing an exit. This behavior is similar to clojure -X
. You can get back the old behavior where bb always forced an exit and ignored running non-daemon threads with --force-exit
.clojure.test/*test-out*
to same print-writer as *out*
in nREPL serverCompiler/demunge
clojure.lang.TaggedLiteral/create
java.util.TimeZone/setDefault
println-str
(.getContextClassLoader (Thread/currentThread))
should be able to return results from babashka classpathdeps.clj
to 1.12.2.1565
*warn-on-reflection*
during load{string,reader}
(same as JVM Clojure) so can load code in other than than the main threadcheshire.generate/{add-encoder,encode-str}
6.8.0
1.3.0
1.21.2
fs
to 0.5.7
cheshire
to 6.1.0
SCI: Configurable Clojure/Script interpreter suitable for scripting
println-str
let
body:param-tags
on qualified instance method*suppress-read*
load-reader
*loaded-libs*
is now the single source of truth about loaded libs*warn-on-reflection*
and bind them during load-string
etc. such that set!
-ing then in a future
works.set!
syntax in CLJSmerge-opts
with :bindings
+ deprecate :bindings
(replaced by :namespaces {'user ...}
)clj-kondo: static analyzer and linter for Clojure code that sparks joy.
symbol
accepting var1.10.3
is the minimum clojure version:inline-def
with nested deftest
inline-configs
config.edn
in a git-diff-friendly way (@lread):locking-suspicious-lock
false positives:condition-always-true
false positives:locking-suspicious-lock
: report when locking is used on a single arg, interned value or local object:unresolved-protocol-method
. See docs (@emerson-matos)clojure.string/replace
and partial
as replacement fn:condition-always-true
check. (@NoahTheDuke)schema.core/defprotocol
(@emerson-matos)str
0.10.47
:deprecated-namespace
for .cljc
namespacesclerk: Moldable Live Programming for Clojure
squint: CLJS syntax to JS compiler
while
didn't compile correctlyclojure.string/includes?
ClassCastException
in statement function when passed Code records:with
option in require, e.g. :with {:type :json}
not=
as functionrandom-uuid
(@rafaeldelboni)[:$ ...]
tagscittle: Execute Clojure(Script) directly from browser script tags via SCI
globalThis
js deps (@chr15m). See docs.(.-foo-bar {})
now behaves as {}.foo_bar
, i.e. the property or method name is munged.cjohansen/dataspex
plugin (@jeroenvandijk)goog.string/format
(@jeroenvandijk)(set! #js {} -a 1)
CLJS syntax (by bumping SCI)dev
folder of the distribution + a dev/scitte.cljs-devtools.js
moduleedamame: configurable EDN and Clojure parser with location metadata and more
*suppress-read*
: :suppress-read
sci.configs: A collection of ready to be used SCI configs.
nbb: Scripting in Clojure on Node.js using SCI
:responses
key with raw responsesfs - File system utility library for Clojure
cherry: Experimental ClojureScript to ES6 module compiler
not=
is now a functionCLI: Turn Clojure functions into CLIs!
:repeated-opts
option to enforce repeating the option for accepting multiple values (e.g. --foo 1 --foo 2
rather than --foo 1 2
)deps.clj: A faithful port of the clojure CLI bash script to Clojure
CLJ_JVM_OPTS
for downloading tools jar.pod-babashka-fswatcher: babashka filewatcher pod
sci.nrepl: nREPL server for SCI projects that run in the browser
"session-closed"
to close op replypod-babashka-go-sqlite3: A babashka pod for interacting with sqlite3
http-server: serve static assets
:not-found
option for handling unfound files. The option is a function of the request and should return a map with :status
and :body
.Contributions to third party projects:
specter: Clojure(Script)'s missing piece
clojure-test-suite: Dialect-independent tests for clojure.core, and others, focused on characterizing how Clojure JVM behaves so that other dialects to reach parity.
These are (some of the) other projects I'm involved with but little to no activity happened in the past month.
Published: 2025-08-05
Tagged: clojure oss updates
In this post I'll give updates about open source I worked on during May and June 2025.
To see previous OSS updates, go here.
I'd like to thank all the sponsors and contributors that make this work possible. Without you the below projects would not be as mature or wouldn't exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.
Current top tier sponsors:
Open the details section for more info about sponsoring.
If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. Thank you!
Here are updates about the projects/libraries I've worked on in the last two months, 19 in total!
babashka: native, fast starting Clojure interpreter for scripting.
#^
metadata)satisfies?
for protocol extended to nil
1.2.50
taoensso/trove
work in bb by exposing another timbre
vartimbre
to 6.7.1
:protocol
metaprint-simple
1.21.1
with-redefs
+ intern
(see SCI issue #973clojure.lang.Var/intern
init
as task nameclojure.lang.Var/{get,clone,reset}ThreadBindingFrame
for JVM Clojure compatibilitytaoensso.timbre/spy
and include testtaoensso.timbre/set-ns-min-level!
and taoensso.timbre/set-ns-min-level
1.12.1
clojure.java.io/resource
implementationjava.text.BreakIterator
java.lang.Thread$Builder$OfPlatform
java.util.concurrent.ForkJoinPool
java.util.concurrent.ForkJoinPool$ForkJoinWorkerThreadFactory
java.util.concurrent.ForkJoinWorkerThread
java.util.concurrent.SynchronousQueue
taoensso.timbre/set-min-level!
taoensso.timbre/set-config!
fs
to 0.5.26
jsoup
to 1.20.1
edamame
to 1.4.30
taoensso.timbre
to 6.7.0
pods
: more graceful error handling when pod quits unexpectedlytype
should prioritize :type
metadatans-name
should work on symbols:clojure.core/eval-file
should affect *file*
during eval:init
in tasks only once:init
in tasks before task specific requiresresolve
when *ns*
is bound to symboldeps.clj
to 1.12.1.1550
http-client
to 0.4.23
SCI: Configurable Clojure/Script interpreter suitable for scripting
satisfies?
for protocol that is extended to nil
sci.async/eval-string+
should return promise with :val nil
for ns form rather than :val <Promise>
1.4.30
:type
key priority in type
implementationns-name
should work on symbols^:clojure.core/eval-file
metadata should affect binding of *file*
during evaluationsci.impl.Reflector
with changes in clojure.lang.Reflector
in clojure 1.12.1:static-methods
option for class with different name in hostwith-redefs
on core vars, e.g. intern
. The fix for this issue entailed quite a big refactor of internals which removes "magic" injection of ctx in core vars that need it.unchecked-set
and unchecked-get
for CLJS compatibilityclerk: Moldable Live Programming for Clojure
quickblog: light-weight static blog engine for Clojure and babashka
--date
to api/new. (@jmglov):preview
optionedamame: configurable EDN and Clojure parser with location metadata and more
:imports
to parse-ns-form
#^:foo
deprecated metadata reader macro (@NoahTheDuke)continue
value that indicates continue-ing parsing (@NoahTheDuke):auto-resolve-ns
affect syntax-quote:auto-resolve-ns
failing casesquint: CLJS syntax to JS compiler
random-uuid
(@rafaeldelboni)trampoline
(@rafaeldelboni)throw
in expression positionnil
clj-kondo: static analyzer and linter for Clojure code that sparks joy.
:locking-suspicious-lock
: report when locking is used on a single arg, interned value or local objectclojure.string/replace
and partial
as replacement fn:discouraged-java-method
. See docs:config-in-ns
on :missing-protocol-method
:redundant-ignore
on :missing-protocol-method
format
and whitespace flag after percent:missing-protocol-method
when using alias in method:redundant-ignore
aware of .cljc
:missing-protocol-method
linterReentrantLock
to coordinate writes to cache directory within same process:langs
option in :discouraged-var
to narrow to specific language:ns
to &env
in :macroexpand-hook
macros when executing in CLJS:name
data to :unresolved-namespace
finding for clojure-lspsci.configs: A collection of ready to be used SCI configs.
scittle: Execute Clojure(Script) directly from browser script tags via SCI
nbb: Scripting in Clojure on Node.js using SCI
import-meta-resolve
to fix deprecation warnings on Node 22+jsr:
and npm:
deps, including react in combination with reagentnode:
quickdoc: Quick and minimal API doc generation for Clojure
<sub>
deps.edn
2025.04.07
org.babashka/cli
dependencynative-image
friendlynextjournal.markdown.utils
:html-block
) and inline HTML (:html-inline
) (see #7):code
according to spec into <pre>
and <code>
block with language class (see #39)applied-science/js-interop
->hiccup
functionnextjournal.markdown.transform
through main nextjournal.markdown
namespace:responses
key with raw responseseven?
http-client: babashka's http-client
:request-method
in addition to :request
to align more with other clients:url
in addition to :uri
to align more with other clientsunused-deps: Find unused deps in a clojure project
fs - File system utility library for Clojure
cherry: Experimental ClojureScript to ES6 module compiler
cherry.embed
which is used by mallideps.clj: A faithful port of the clojure CLI bash script to Clojure
These are (some of the) other projects I'm involved with but little to no activity happened in the past month.
Published: 2025-07-01
Tagged: clojure oss updates
In this post I'll give updates about open source I worked on during March and April 2025.
To see previous OSS updates, go here.
I'd like to thank all the sponsors and contributors that make this work possible. Without you the below projects would not be as mature or wouldn't exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.
Current top tier sponsors:
Open the details section for more info about sponsoring.
If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. Thank you!
On to the projects that I've been working on!
I blogged about an important improvement in babashka regarding type hints here.
Also I did an interview with Jiri from Clojure Corner by Flexiana, viewable here.
Here are updates about the projects/libraries I've worked on in the last two months.
babashka: native, fast starting Clojure interpreter for scripting.
ThreadBuilder
interopjava.util.concurrent.ThreadLocalRandom
java.util.concurrent.locks.ReentrantLock
java.time.chrono.ChronoLocalDate
java.time.temporal.TemporalUnit
java.time.chrono.ChronoLocalDateTime
java.time.chrono.ChronoZonedDateTime
java.time.chrono.Chronology
cheshire.factory
namespace (@lread)24
0.9.45
1.4.28
java.util.regex.PatternSyntaxException
1.8.735
6.0.0
0.8.65
clerk: Moldable Live Programming for Clojure
squint: CLJS syntax to JS compiler
:context expr
in compile-string
:context expr
in set!
expressionreturn
:require
+ :rename
+ allow renamed value to be used in other :require clausenull
when written in else branch of if
#jsx
and #html
range
fixesrun!
defclass
: elide constructor when not provideddefclass
clj-kondo: static analyzer and linter for Clojure code that sparks joy.
:config-in-ns
on :missing-protocol-method
:redundant-ignore
on :missing-protocol-method
:missing-protocol-method
. See docs.
, e.g. py.
according to clojure analyzer--repro
flag to ignore home configurationdeftype
form results in NPE
(alias)
bug (@Noahtheduke)SCI: Configurable Clojure/Script interpreter suitable for scripting
sci.async/eval-string+
should return promise with :val nil
for ns form rather than :val <Promise>
volatile?
to core vars1.4.28
quickdoc: Quick and minimal API doc generation for Clojure
<sub>
deps.edn
2025.04.07
org.babashka/cli
dependencyCLI: Turn Clojure functions into CLIs!
process: Clojure library for shelling out / spawning sub-processes
html: Html generation library inspired by squint's html tag
cherry: Experimental ClojureScript to ES6 module compiler
cljs.pprint/pprint
add-tap
#html
id and class shortcuts + additional features and optimizations, such as an optimization for aset
nbb: Scripting in Clojure on Node.js using SCI
jsr:
dependency support, stay tuned.instaparse-bb: Use instaparse from babashka
edamame: Configurable EDN/Clojure parser with location metadata
fs - File system utility library for Clojure
fs/match
doesn't match when root dir contains glob or regex characters in pathfs/update-file
to support paths (@rfhayashi)sql pods: babashka pods for SQL databases
These are (some of the) other projects I'm involved with but little to no activity happened in the past month.
Published: 2025-05-02
Tagged: clojure oss updates