CLI tool
squint-cljs/cherry avatar
squint-cljs/cherry

Cherry compiles ClojureScript to plain ES modules

Experimental ClojureScript to ES6 module compiler

663 stars25 forksClojureLicense varies

At a glance

What is it?
Cherry is an experimental ClojureScript to ES6 module compiler that emits readable JavaScript with source maps, no Google Closure dependency and a shared npm standard library, plus a watcher, an nREPL server and a browser REPL. Its own README says it has many bugs and will undergo many breaking changes.
Who is it for?
Cherry suits a developer experimenting with writing ClojureScript that compiles to plain JavaScript with no Closure step, especially inside a Vite or React project where the output has to be readable and debuggable. It is the wrong tool for production, because the README states plainly that it is an experiment, is not recommended to be used in production, has many bugs and will undergo many breaking changes.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 50 days ago.
What is it written in?
Mainly Clojure, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An experiment with a stated warning

Cherry is described as an experimental ClojureScript to ES6 module compiler, and its purpose is stated in one line: reducing friction between ClojureScript and JS tooling.

The README carries a warning that is worth quoting in full, because it is unusual to see it this prominently: this project is an experiment and not recommended to be used in production, it currently has many bugs and will undergo many breaking changes.

That framing shapes everything else. The project started out as a fork of Scriptjure and is currently being reworked to meet the stated goals, so it is a work in progress rather than a finished tool. The goals themselves are conditional too: depending on interest both from people working on this and the broader community, the goals may or may not be pursued.

There is a companion project worth knowing about, because the distinction matters. The README points to Squint, which is a CLJS syntax to JS compiler. Cherry is the fuller attempt: it compiles .cljs files into ES6-compatible .mjs files, with a shared standard library, macro support, REPL support and async/await. Squint is the smaller, more syntax-level tool.

The npm package is cherry-cljs at version 0.6.38, with the recent tags v0.6.38 from 2026-08-10 and v0.6.37 and v0.6.36 from 2026-08-08. The last push was on 2026-09-03, the repository is not archived, and the homepage is squint-cljs.github.io/cherry.

The quickstart, end to end

The quickstart is short and it produces a running program, which is the fastest way to judge the project.

shell
$ mkdir cherry-test && cd cherry-test
$ npm init -y
$ npm install cherry-cljs@latest

Then you write a .cljs file, and the example in the README uses Node's fs and url modules directly through a require of the ES module:

clojure
(ns example
  (:require ["fs" :as fs]
            ["url" :refer [fileURLToPath]]))

(prn (fs/existsSync (fileURLToPath js/import.meta.url)))

(defn foo [{:keys [a b c]}]
  (+ a b c))

(js/console.log (foo {:a 1 :b 2 :c 3}))

The compile and run are a single command, where run does both:

code
$ npx cherry run example.cljs
true
6

Two things in that example are the design statement of the project. The require uses a vector of the module specifier and the binding, which is the ClojureScript style rather than the CLJS-style quoted symbols, and it reaches into Node's own modules by name. The output is a plain Node program: the file is checked for existence, a Clojure function is defined, and it is called from JavaScript.

For the options, npx cherry --help lists everything, and the compiled output is ES modules with source map support for debugging and compiled JS files that are fairly readable.

The goals, and the one that constrains everything else

The Project goals section is a list of design intentions, and reading it tells you what the author is optimising for.

The first goal is to compile .cljs files on the fly into ES6-compatible .mjs files. The second is that the compiler will be available on NPM and can be used from JS tooling, but is not part of the compiled output unless explicitly used. The third is readable output with source map support. The last is lightweight and fast, with heavy lifting such as optimizations expected to be done by JS tooling, and no dependency on Google Closure, since the project will use it for bootstrapping itself by using the ClojureScript compiler, but users of this project will not use it for compilation.

That last point is the significant one. The entire ClojureScript toolchain today ends in a Closure compile step that minifies and renames. Cherry removes it, and the price is that the optimisation has to come from the JavaScript toolchain instead.

The compatibility goal is unusual enough to be worth its own attention. Compiled JS files are linked to one shared NPM module called cherry-cljs which contains cljs.core.js, cljs.string and similar, so that libraries written in cherry can be compiled and hosted on NPM while all sharing the same standard library and data structures. And output linked to older versions of cherry will work with newer versions of cherry, that is binary compatibility.

That last one is a design commitment most compilers do not make. It means a library published to npm keeps working when the consumer upgrades their Cherry version, which is what makes publishing cherry libraries to npm viable at all.

The remaining goals are macro support, REPL support, async/await support, native support for JS object destructuring with the syntax [^:js {:keys [a b]} #js {:a 1 :b 2}], and native JSX support through a #jsx reader tag.

The compatibility cost, stated by the author

The README has a paragraph that is unusually candid about what Cherry costs you, and it belongs in any evaluation.

Cherry may introduce new constructs such as js-await which will not be compatible with current CLJS. Also it might not support all features that CLJS offers. As such, using existing libraries from the CLJS ecosystem or compiling Cherry CLJS code with the CLJS compiler may become challenging.

That is a real constraint, and it is structural rather than a matter of missing features. The ClojureScript ecosystem is large, and a library written for CLJS is written against CLJS's semantics, including its own reader tags, its own macro system and its own treatment of keywords, protocols and records. If Cherry reads files with a different reader and compiles them with different rules, the two compilers are not interchangeable on the same source.

The counterweight in the same paragraph is worth noting too. Some results of this experiment may end up as improvements in the CLJS compiler if they turn out to be of value, and the README points at the slides of a presentation given at Dutch Clojure Days 2022 about cherry and squint for the longer argument.

The project is also explicit about its own ambition being conditional. The goals may or may not be pursued depending on interest, and the invitation is to submit issues for bug reports or share thoughts on GitHub Discussions. For an evaluator, that means the trajectory of the project is genuinely uncertain, which is unusual for a tool you might consider depending on.

cherry.edn, the watcher, and the REPLs

The configuration file is where the project-specific options live, and it is EDN rather than JSON or YAML.

In cherry.edn you can describe :paths, the source paths to search for files, where at the moment only .cljc and .cljs are supported. :extension is the preferred extension to output, which defaults to .mjs but can be set to .jsx for React-like projects. :copy-resources is a set of keywords representing file extensions that should be copied over from the source paths, for example :css and :json, and strings may also be used, where they represent regexes processed through re-find. :output-dir is where compiled files will be created, defaulting to the project root directory.

The one with a dependency is :deps, a map of dependencies from the Clojure ecosystem in the same format as deps.edn. Only :git/url with :git/sha and :local/root libraries are supported, no jars yet, and their source directories are resolved via the clojure CLI with -Spath and added to :paths. That requires clojure on the PATH. The example given is:

clojure
{:paths ["src"]
 :deps {io.github.cjohansen/replicant {:git/sha "..."}}}

On the toolchain side, npx cherry watch watches the source directories described in cherry.edn and recompiles them whenever they change. The nREPL implementation is described as currently immature and runs on Node.js with:

shell
npx cherry nrepl-server --port 1888

The browser REPL is a separate mechanism. The cherry-cljs/vite.js plugin hot-reloads compiled cherry in the page and runs an nREPL server that your editor connects to, evaluating forms in the live page, and the details are in doc/browser-repl.md.

For testing, Cherry ships a clojure.test-compatible testing library, requirable as cljs.test or clojure.test, so a test namespace written in the usual form runs under Cherry as well.

The repository, the packaging and the limits

The repository layout shows the breadth of the experiment. There is src/ for the compiler, bb/ and bb.edn for Babashka tasks, script/, corpus/ for test inputs, externs/, notes/, doc/, examples/, e2e/, test/ and test-resources/. There is a vite.js for the Vite plugin and a shadow-cljs.edn, which is how Cherry is embedded into a ClojureScript or shadow-cljs application as described in embed.md.

The npm package itself is small. The files array lists cljs.core.js, lib, node_cli.js, index.js and vite.js, and the bin entry maps the cherry command to node_cli.js. The runtime dependencies are chokidar for the file watching and squint-cljs at a pinned version, which is the sibling compiler. The devDependencies include esbuild, Babel with the React preset, Playwright, React, shadow-cljs and vercel/ncc.

The examples directory is the best evidence of what the compiler can currently do. There are examples for React, Vite, Vitest, wordle, aoc, deno, deno-fresh, jsx, replicant and browser-repl, and the README calls out a livecodes.io ClojureScript template, a playground, a wordle example and a React example, plus a browser-repl described as vite plus browser REPL with three rendering models, and a replicant tic-tac-toe using replicant hiccup.

The limits are these. It is explicitly an experiment with many bugs and many breaking changes. Interoperability with the existing ClojureScript ecosystem may become challenging, and the dependency resolution in cherry.edn supports only git and local roots, so a library published to clojars as a jar cannot be pulled in that way. The nREPL is described as currently immature. The goals themselves may not be pursued. And a project whose published version numbers sit at 0.6.x with a release history of a few patches in a week is going to keep moving under you.

For a team that wants ClojureScript syntax and JavaScript output without a Closure step, that is a compelling experiment to run. For anything you have to maintain, the README's own warning is the answer.

Editorial conclusion

Cherry suits a developer experimenting with writing ClojureScript that compiles to plain JavaScript with no Closure step, especially inside a Vite or React project where the output has to be readable and debuggable. It is the wrong tool for production, because the README states plainly that it is an experiment, is not recommended to be used in production, has many bugs and will undergo many breaking changes. Before trying it, run the quickstart in a scratch directory, and read the note that using existing libraries from the ClojureScript ecosystem may become challenging, since Cherry may introduce constructs such as js-await that current ClojureScript does not have.

Frequently asked questions

How do I try Cherry quickly?

Create a directory, run npm init -y, install with npm install cherry-cljs@latest, write a .cljs file, and compile and run it with npx cherry run example.cljs. The README's quickstart uses Node's fs and url modules in the require to show that the output runs as an ordinary ES module.

Is Cherry safe to use in production?

No, and the README says so directly: this project is an experiment and not recommended to be used in production, it currently has many bugs and will undergo many breaking changes. The goals may or may not be pursued depending on interest from contributors and the broader community.

How is Cherry different from Squint?

The README points to Squint as a separate project that is a CLJS syntax to JS compiler, while Cherry is the fuller attempt: it compiles .cljs files into ES6-compatible .mjs files, with a shared npm standard library, macro support, REPL support, async/await support and native JSX through a #jsx reader tag.

What can I configure in cherry.edn?

The :paths to search for source files, with only .cljc and .cljs supported at the moment, the :extension to output which defaults to .mjs and can be set to .jsx, :copy-resources for file extensions to copy over, :output-dir which defaults to the project root, and :deps for Clojure ecosystem dependencies in deps.edn format, where only :git/url with :git/sha and :local/root are supported and the clojure CLI is required on the PATH.

Does Cherry depend on Google Closure?

No. The goal is no dependency on Google Closure: the project will use it for bootstrapping itself by using the ClojureScript compiler, but users of this project will not use it for compilation. Heavy lifting such as optimizations is expected to be done by JS tooling instead.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. squint-cljs/cherry on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/squint-cljs-cherry.svg)](https://hysenlabs.com/projects/squint-cljs-cherry)

Was this useful?