ClojureScript development as a hive IAddon: shadow-cljs build status, cljs-eval, Playwright e2e scenarios and build→e2e watching, driven by config in your project root.
One addon. One cljs subdomain on the code tool. Three ports.
Runtime assertions are pinned to the page the scenario drives, and a step that
could not be attempted is :incomplete — never a silent pass.
| Setting up a fresh project | nothing → a green scenario, with the traps that cost real time |
| Configuration reference | .hive-project.edn vs hive-cljs.edn, every key, every default |
| Step reference | the browser + runtime vocabulary, semantics, adding a kind |
| Mounting in a host | wiring into hive-mcp, why a subdomain, diagnosing a silent mount |
| Architecture | CPPB layers, the ports, extension points |
| Runnable example | a wired shadow-cljs + re-frame app you can cljs e2e run against |
A scenario asserts on the DOM and the live re-frame runtime in the same step vector:
[[:goto "/"]
[:click "#go"]
[:expect-text "#hi" "Hello, pedro"] ; browser → IBrowserDriver
[:expect-sub [:current-user] "some?"] ; runtime → ICljsEval
[:expect-db [:user] "some?"]]
:expect-sub / :expect-db / :dispatch / :eval-cljs are evaluated inside
the running application over shadow's nREPL. Everything else drives a real
browser.
That split is also a debugging instrument: :expect-text red while :expect-sub
green localises a bug to rendering rather than state.
For that reading to be trustworthy the runtime assertion has to be about the page the scenario is driving — so hive-cljs stamps the page it opens and pins evaluation to it. Otherwise any other connected runtime (a stray tab, a forgotten headless browser, the shadow UI) answers instead, and the scenario grades the wrong page while reporting green.
shadow-cljs.edn supplies the two ports:
{:deps true
:nrepl {:port 7889} ; runtime channel
:dev-http {8280 "public"} ; what the browser opens
:builds {:app {:target :browser ...}}}
Config — in your existing .hive-project.edn:
{:project-id "my-app"
:hive.cljs {:shadow {:nrepl-port 7889}
:builds {:app {:http-port 8280}}}}
…or in a standalone hive-cljs.edn. Both work; both together merge, with the
dedicated file winning. A single build id is the only required key.
Either file is found by walking up from wherever you invoked, so a
subdirectory works. A workspace can hold shared defaults, but a child inherits
them only by asking: :hive.cljs/inherit true. Values can come from the
environment with #hive/env PORT.
Then, with shadow-cljs watch app running:
code {command: "cljs doctor", directory: "/path/to/my-app"}
code {command: "cljs e2e run", directory: "/path/to/my-app", scenario: "login"}
code {command: "cljs watch start", directory: "/path/to/my-app"}
Full walkthrough: docs/setup.md. Working code to copy from: example/ — both config sources, both assertion channels, the input vocabulary and the watcher, in one small app.
| Subcommand | Does |
|---|---|
cljs doctor | validate config, report per-port connectivity and which runtimes are attached |
cljs staleness | is the cached config still current, and does the server serve our builds |
cljs status [build] | build verdict — one build or all |
cljs compile <build> | one compile cycle, returns the verdict |
cljs eval <build> <code> | evaluate cljs in the running runtime |
cljs e2e list \| run | run a scenario (scenario) or a tag set (tags) |
cljs watch start \| stop \| status | couple build success to e2e runs |
cljs help | subcommand index |
All accept directory (project root; defaults to cwd).
(require '[hive-cljs.test-api :as cljs-e2e])
(deftest login-works
(let [res (cljs-e2e/run-scenario! project-root :login)]
(is (cljs-e2e/passed? res) (cljs-e2e/explain res))))
Same execution path as the tool and the watcher. See setup.md.
One line in the host's local.deps.edn:
io.github.hive-agi/hive-cljs {:mvn/version "0.1.1"}
…or, when hacking on hive-cljs itself, #:local{:root "../hive-cljs"}.
Batteries included — the relay transport, nREPL client and browser driver all
ride in on hive-cljs's own :deps. The host declares nothing about our vendors.
Details and the reasoning: docs/hosting.md.
shadow-cljs watch <build>) for build status and e2e:nrepl-port in the config for the runtime channel, plus a browser with the app
open so a JS runtime is connected — a scenario's :goto handles that itselfclojure -M:test # 122 tests, 568 assertions — stubs only, no vendors needed
MIT.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |