Use esbuild from babashka and the JVM.
Bundle and transform JavaScript, TypeScript, JSX and CSS.
This library runs on babashka or on the JVM through babashka.ffi. It does not use or need Node.js.
Experimental. The API may change as we gather feedback.
Add the library to bb.edn or deps.edn:
{:deps {org.babashka/esbuild {:mvn/version "0.1.1"}}}That pulls in org.babashka/libesbuild, which carries esbuild for
macOS, Linux and Windows on x86_64 and aarch64. See Distribution.
babashka.ffi comes with it. Babashka includes it as well.
On the JVM, start with --enable-native-access=ALL-UNNAMED.
org.babashka/libesbuild is a jar of esbuild shared libraries, one per
platform, published to Clojars. Its version is the esbuild version it wraps
plus a shim number, so 0.28.2-2 holds esbuild v0.28.2. babashka.esbuild
depends on it, so a release of this library pins one esbuild.
The operating system loads a shared library from a file, so the first call
unpacks the one for your platform into
<xdg-cache>/babashka/esbuild/<libesbuild version>/. Later runs load it from
there. The version sits in the path, so an upgrade writes a new directory
instead of changing a file another process may be using.
(require '[babashka.esbuild :as esbuild])
(esbuild/transform "const x: number = 1" {:loader :ts})
;;=> {:code "const x = 1;\n"}
(esbuild/transform "let f = (x: number): number => x*2"
{:loader :ts :minify true :target :es2020})
;;=> {:code "let f=e=>e*2;\n"}Let's create some dummy source to play with:
(spit "foobar-main.tsx"
(str "const App = (): JSX.Element => <div>Hello World</div>;\n"
"\n"
"console.log(App());\n"))build bundles entry points and returns the output in memory:
(esbuild/build {:entry-points ["foobar-main.tsx"] :bundle true :format :esm :minify true})
;;=> {:outputs [{:path "<stdout>", :contents "var o=()=>React.createElement(\"div\",null,\"Hello World\");console.log(o());\n"}]}Pass :write true with :outfile or :outdir to write to disk instead.
Use kebab-case keywords for option names. Enum values can also be keywords.
:entry-points reaches esbuild as entryPoints.
Optionally specify :metafile true to generate :metafile metadata about build.
Generate a text report on this JSON string value via analyze-metafile:
(def build-result (esbuild/build {:entry-points ["foobar-main.tsx"] :bundle true :metafile true}))
(println (:report (esbuild/analyze-metafile (:metafile build-result) {:verbose true})))
foobar-main.js ───── 149b ── 100.0%
└ foobar-main.tsx ─ 113b ─── 75.8%Build and transform errors throw exceptions with error details in ex-data:
(esbuild/transform "let = ;")
;; Unexpected ";"
;; {:type :babashka.esbuild/error
;; :errors [{:text "Unexpected \";\"" :file "<stdin>" :line 1 :column 6}]}examples/squint_bundle.clj compiles ClojureScript with
squint and bundles it into one
JavaScript file:
bb examples/squint_bundle.clj
See doc/dev.md.
Copyright © 2026 Michiel Borkent
Distributed under the MIT License. See LICENSE.
esbuild is MIT licensed, Copyright (c) 2020 Evan Wallace. The libesbuild jar
carries its license.