One of the greatest limitations of ClojureScript is how you can’t have two ClojureScript codebases, running on “development mode”, in the same runtime. In the browser, or in Node.JS, this is not a big problem, but it is one in some other situations – like browser extensions (you might be developing two extensions at the same time), node libraries (again, same case) and… well, editor plug-ins for Pulsar, or VSCode.
That… might not be a problem anymore.
If you use Shadow-CLJS (and you should) then you can just change your target to :npm-module
(or :esm
if you’re in the browser). That will create a lot of files but the important one is cljs_env.js
. What this file does, is bootstrap the Google Closure Compiler and make some assignments for Shadow and for ClojureScript namespaces.
In the beginning of the file, you’ll see this line:
var CLJS_GLOBAL = global; var $CLJS = CLJS_GLOBAL;
Just change it to:
global.some_unique_identifier = {} var CLJS_GLOBAL = global.some_unique_identifier; var $CLJS = CLJS_GLOBAL;
And that’s done.
But, of course, this is incredibly tedious: every time you change a file, you’ll need to make this change. So you can add a Shadow-CLJS hook to do it. On your shadow-cljs.edn file, inside your build target configuration, do this:
; .... :builds {:plugin {:target :npm-module :build-hooks [(some-env.hooks/hack-multiple-runtimes)] ; ....
Then create a Clojure file like some_env/hooks.clj
and add the following code:
(ns some-env.hooks (:require [clojure.string :as str])) (defn hack-multiple-runtimes {:shadow.build/stage :flush} [build-state] (let [built (slurp "<output-directory>/cljs_env.js") replaced (str "global.some_unique_identifier = {};\n" (str/replace-first built #"global" "global.some_unique_identifier"))] (spit "lib/cljs_env.js" replaced)) build-state)
And that’s it.
Now, in your shadow-cljs file, inside your build (maybe just below the hooks part?) you might want to add:
:dev {:compiler-options {:reader-features #{:cljs-dev}}}
And in your core namespace, you might also want to add:
(ns some-env/core (:require ...your requires... #?(:cljs-dev [shadow.cljs.devtools.client.npm-module]))
Or shadow.cljs.devtools.client.browser
, etc. Without these lines, you won’t have a REPL nor hot-reload code.