Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.language.clojure.shadow-repl

shadow-cljs as the ClojureScript REPL RUNTIME: attaching to the nREPL a shadow-cljs watch already runs, SELECTING the build whose JS runtime an eval must land in, and keeping that selection true for every later eval.

Five facts about that server decide everything here, and every one of them was learned from a live shadow-cljs, not from its wire protocol:

  1. A shadow-cljs nREPL is INDISTINGUISHABLE from a plain JVM one over describe: it advertises no cljs/shadow op and reports only a :clojure version, so dialect detection from describe metadata answers :clj for it. The only honest question is a read-only eval that RESOLVES shadow.cljs.devtools.api inside the server.
  2. The build is selected PER nREPL SESSION — (shadow…api/nrepl-select :app) — and ONE session serves EVERY build of that server, so a selection belongs to the CONNECTION and never to an attachment: selecting :worker makes the next eval that believes it targets :app answer from the worker's runtime, silently and with no error. Every eval therefore re-checks both the session and the build selected in it, never a boolean.
  3. Selecting from a session ALREADY sitting in a build FAILS — the select form is compiled as ClojureScript and dies on No such namespace: shadow.cljs.devtools.api. :cljs/quit first is what makes selection total: it returns such a session to CLJ, and in a session that never left CLJ it is an ordinary keyword evaluating to itself.
  4. nrepl-select answers the SAME watch for build not running whether the build id does not exist or is merely not being watched. The two are told apart BEFORE selecting — by the server's own build config and worker-running? — because they need opposite fixes.
  5. A selected build with NO connected JS runtime neither errors nor hangs: every eval answers No available JS runtime. on :err with a done status. Nothing but a started runtime fixes it, so that answer is turned into the instruction that starts one.
shadow-cljs as the ClojureScript REPL RUNTIME: attaching to the nREPL a
`shadow-cljs watch` already runs, SELECTING the build whose JS runtime an eval
must land in, and keeping that selection true for every later eval.

Five facts about that server decide everything here, and every one of them was
learned from a live shadow-cljs, not from its wire protocol:

1. A shadow-cljs nREPL is INDISTINGUISHABLE from a plain JVM one over
   `describe`: it advertises no cljs/shadow op and reports only a `:clojure`
   version, so dialect detection from describe metadata answers `:clj` for it.
   The only honest question is a read-only eval that RESOLVES
   `shadow.cljs.devtools.api` inside the server.
2. The build is selected PER nREPL SESSION — `(shadow…api/nrepl-select :app)` —
   and ONE session serves EVERY build of that server, so a selection belongs
   to the CONNECTION and never to an attachment: selecting `:worker` makes
   the next eval that believes it targets `:app` answer from the worker's
   runtime, silently and with no error. Every eval therefore re-checks both
   the session and the build selected in it, never a boolean.
3. Selecting from a session ALREADY sitting in a build FAILS — the select form
   is compiled as ClojureScript and dies on `No such namespace:
   shadow.cljs.devtools.api`. `:cljs/quit` first is what makes selection
   total: it returns such a session to CLJ, and in a session that never left
   CLJ it is an ordinary keyword evaluating to itself.
4. `nrepl-select` answers the SAME `watch for build not running` whether the
   build id does not exist or is merely not being watched. The two are told
   apart BEFORE selecting — by the server's own build config and
   `worker-running?` — because they need opposite fixes.
5. A selected build with NO connected JS runtime neither errors nor hangs:
   every eval answers `No available JS runtime.` on `:err` with a `done`
   status. Nothing but a started runtime fixes it, so that answer is turned
   into the instruction that starts one.
raw docstring

eval!clj

(eval! {:keys [host port build target]} eval-opts)

Evaluate in build's JS runtime over the shadow-cljs nREPL at host:port.

attachment is {:host :port :build :target}. The build is (re)SELECTED whenever selected? does not already report it for that connection: a replaced session (an evicted socket, a restarted watch) would otherwise silently answer as JVM Clojure, and a SIBLING build selected on the same server would answer from the wrong runtime without erroring at all. When it is already selected the hot path stays ONE round trip.

Answers {:selected? true :result <nrepl-client/eval! map>}, plus :message when the build has no runtime; a build that can no longer be selected answers {:selected? false :message …} and evaluates NOTHING.

Evaluate in `build`'s JS runtime over the shadow-cljs nREPL at `host:port`.

`attachment` is `{:host :port :build :target}`. The build is (re)SELECTED
whenever `selected?` does not already report it for that connection: a
replaced session (an evicted socket, a restarted watch) would otherwise
silently answer as JVM Clojure, and a SIBLING build selected on the same
server would answer from the wrong runtime without erroring at all. When it
is already selected the hot path stays ONE round trip.

Answers `{:selected? true :result <nrepl-client/eval! map>}`, plus `:message`
when the build has no runtime; a build that can no longer be selected answers
`{:selected? false :message …}` and evaluates NOTHING.
sourceraw docstring

no-runtime-markerclj

The exact stderr shadow-cljs emits for EVERY eval on a build whose runtime is not connected. Its own text, matched as a substring — never re-worded here.

The exact stderr shadow-cljs emits for EVERY eval on a build whose runtime is
not connected. Its own text, matched as a substring — never re-worded here.
sourceraw docstring

no-runtime?clj

(no-runtime? result)

True when this eval result is shadow-cljs saying the build has no JS runtime attached — a done reply whose stderr carries shadow's own marker, never an exception and never a timeout.

True when this eval result is shadow-cljs saying the build has no JS runtime
attached — a `done` reply whose stderr carries shadow's own marker, never an
exception and never a timeout.
sourceraw docstring

nrepl-portclj

(nrepl-port dir)

The nREPL port a shadow-cljs watch running in dir published, or nil.

ONE deterministic file, written by the user's OWN server, under the directory the caller explicitly named — this is not port discovery: nothing is scanned, no other server can answer, and with no watch running there is no file and the caller is told to start one.

The nREPL port a `shadow-cljs watch` running in `dir` published, or nil.

ONE deterministic file, written by the user's OWN server, under the directory
the caller explicitly named — this is not port discovery: nothing is scanned,
no other server can answer, and with no watch running there is no file and the
caller is told to start one.
sourceraw docstring

port-file-pathclj

Where shadow-cljs watch publishes its nREPL port, relative to the project.

Where `shadow-cljs watch` publishes its nREPL port, relative to the project.
sourceraw docstring

probe!clj

(probe! {:keys [host port build timeout-ms]})

What the nREPL at host:port IS, in one read-only eval:

{:shadow? true :builds ["npm" "app"] :worker? true :target :node-script}

:builds are the ids the SERVER loaded (its own config is authoritative — the caller's cwd may not be where it runs); :worker? is whether build has a live watch; :target is that build's shadow target. A plain JVM nREPL answers :shadow? false, and an unreachable one adds :error.

What the nREPL at `host:port` IS, in one read-only eval:

  {:shadow? true :builds ["npm" "app"] :worker? true :target :node-script}

`:builds` are the ids the SERVER loaded (its own config is authoritative — the
caller's `cwd` may not be where it runs); `:worker?` is whether `build` has a
live `watch`; `:target` is that build's shadow target. A plain JVM nREPL
answers `:shadow? false`, and an unreachable one adds `:error`.
sourceraw docstring

runtime-hintclj

(runtime-hint build target)

What actually starts the missing runtime, phrased for THIS build's target. The watch stays running either way — the runtime is the second process.

What actually starts the missing runtime, phrased for THIS build's `target`.
The watch stays running either way — the runtime is the second process.
sourceraw docstring

select!clj

(select! {:keys [host port build timeout-ms]})

Select build in the nREPL session nrepl-client/eval! reuses for host:port, by evaluating shadow's own nrepl-select IN that session — so every later eval through the same session lands in that build's JS runtime.

TOTAL whatever that session currently is: :cljs/quit goes first, returning a session that already sits in a build to CLJ, and evaluating to itself in one that never left it. Without it a re-select is compiled AS ClojureScript and dies on No such namespace: shadow.cljs.devtools.api.

Answers {:selected? true} — recording the selection for selected? — or {:selected? false :message …} with the server's own reason.

Select `build` in the nREPL session `nrepl-client/eval!` reuses for `host:port`,
by evaluating shadow's own `nrepl-select` IN that session — so every later eval
through the same session lands in that build's JS runtime.

TOTAL whatever that session currently is: `:cljs/quit` goes first, returning a
session that already sits in a build to CLJ, and evaluating to itself in one
that never left it. Without it a re-select is compiled AS ClojureScript and
dies on `No such namespace: shadow.cljs.devtools.api`.

Answers `{:selected? true}` — recording the selection for `selected?` — or
`{:selected? false :message …}` with the server's own reason.
sourceraw docstring

selected?clj

(selected? {:keys [host port build]})

True when build is the build this process has selected in the session nrepl-client/eval! will reuse for host:port — the SAME session AND the SAME build. A live session token alone is not enough: every build on one shadow-cljs server shares that session, so a sibling build's selection owns this one's next eval.

True when `build` is the build this process has selected in the session
`nrepl-client/eval!` will reuse for `host:port` — the SAME session AND the
SAME build. A live session token alone is not enough: every build on one
shadow-cljs server shares that session, so a sibling build's selection owns
this one's next eval.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close