nextjournal / clerk

⚡️ Moldable Live Programming for Clojure
https://clerk.vision
ISC License
1.74k stars 75 forks source link

Browser UI disappears when exception is thrown #638

Closed yuhan0 closed 2 months ago

yuhan0 commented 3 months ago

Minimal repro:

Start a deps.edn project:

{:paths ["src"] :deps {io.github.nextjournal/clerk {:mvn/version "0.15.957"}}} 

Start a repl and Clerk server, then show! the following src/repro.clj file

(ns repro
  (:require [nextjournal.clerk :as clerk]
            [nextjournal.clerk.viewer :as clerk.v]))

(clerk/with-viewer clerk.v/string-viewer "glub")

;; (throw (ex-info "whoops" {}))

Verify the page at http://localhost:7777/src/repro is displaying correctly, then uncomment the last line and re-evaluateclerk/show!

Expected outcome:

The error and stacktrace are displayed in red in the browser, which automatically hot-reloads once the exception-throwing expression is resolved (by commenting it out and evaluating clerk/show!)

Actual:

The browser page turns blank and has to be manually refreshed.

Additional details:

macOS 14.3.1 Clojure 1.11.2 Reproduced on both Firefox (version 123.0.1) and Chrome (Version 104.0.5061.0)

On Firefox, the console prints a series of stack traces from some sort of minified React code:

TypeError: b is null       react-dom.production.min.js:183:23
    oy Immutable
    tna component.cljs:423
    Hna template.cljs:128
    Ina template.cljs:131
    Qna template.cljs:234
    qg formatting.cljs:162
    $x clojure_mode.cljs:53
    pna component.cljs:317
    qna component.cljs:364
    rna component.cljs:394
    aw util.cljs:234
    Wla ratom.cljs:337
    rna component.cljs:392
    c component.cljs:410
    React 6
    l scheduler.production.min.js:12
    Y scheduler.production.min.js:14
    93 scheduler.production.min.js:12
    zl core.cljs:11933
    95 scheduler.production.min.js:20
    zl core.cljs:11933
    96 React
    zl core.cljs:11933
    98 React
    zl core.cljs:11933
    99 React
    zl core.cljs:11933
    Al core.cljs:12115
    <anonymous> parser.cljc:321

The issue appears to happen whenever the file contains a call to clerk/with-viewer / clerk/with-viewers, removing it causes the thrown exception to be displayed in the browser GUI.

Tested with eg. (clerk/with-viewers clerk/default-viewers 123)

mk commented 3 months ago

I believe we fixed this in 8d2929b99cfa2164f1fbaf54915b9dcaaa6fe46d. Can you try the latest main at cbb19fd8f1a9b3b01c9ccb0d43c6dbb4571f3829 and confirm this? Should cut a new release soon.

yuhan0 commented 3 months ago

I was able to reproduce on the latest main:

$ clj -Sdeps '{:deps {io.github.nextjournal/clerk {:git/url "https://github.com/nextjournal/clerk.git" :git/sha "cbb19fd8f1a9b3b01c9ccb0d43c6dbb4571f3829"}}}'
Clojure 1.11.2
user=> (require '[nextjournal.clerk :as clerk])
nil
user=> (clerk/serve! {})
Clerk webserver started on http://localhost:7777 ...
{}
user=> (clerk/show! "src/repro.clj")
Clerk evaluated 'src/repro.clj' in 82.061208ms.
nil

< uncomment the exception-throwing form >

user=> (clerk/show! "src/repro.clj")
Execution error (ExceptionInfo) at nextjournal.clerk.eval/eval+cache! (eval.clj:157).
Execution error (ExceptionInfo) at repro/eval18722 (repro.clj:8).
whoops

user=>

< browser page turns blank >

I made sure to clear the cache (by deleting the .clerk dir) and restarted the browser / opened the URL in a private window.

mk commented 3 months ago

Indeed I can reproduce the problem as well. Will look into it. Thanks for the detailed report!

mk commented 3 months ago

Turns out it was actually the commit I thought fixed it that broke it: 8d2929b99cfa2164f1fbaf54915b9dcaaa6fe46d. It will works with its parent 31326c1f2854e3f23f7cb15bb43cb3f561ad579e.

zampino commented 3 months ago

Looking at this and trying to go at the source of the problem. Apparently we're not able to display values produced by code like:

;; # 🐞Debug
(ns scratch.debug
  {:nextjournal.clerk/visibility {:code :hide}}
  (:require [nextjournal.clerk :as clerk]
            [nextjournal.clerk.viewer :as viewer]))

{:nextjournal/value (clerk/with-viewer viewer/string-viewer "hello")}

the above gives:

CleanShot 2024-03-25 at 15 34 27@2x

Such a map is present on the ex-data of exceptions thrown during clerk/show! (ex-data -> :doc -> :blob->result) but unlike the result viewer, the piece of ui that renders eval exceptions doesn't have an error-boundary upstream of the inspect-presented, and hence the blank screen

https://github.com/nextjournal/clerk/blob/c741fcd021c333d6d3fb2ba000cc04bb5d3e8251/src/nextjournal/clerk/render.cljs#L580-L586

Still need to understand why the map in the example is not renderable.