diff --git a/README.md b/README.md index 18f64fd..ebf8392 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ pre-announce, - CLI UX - [x] opts - [ ] logging -- [ ] [deal with errors](https://github.com/srid/memoir/issues/1) +- [x] [deal with errors](https://github.com/srid/memoir/issues/1) - [ ] How to serve non-generated files (css, img, etc.) - [ ] Publish Data.LVar to Hackage - [ ] documentation ([guide](https://documentation.divio.com/)) diff --git a/docs/concepts/hot-reload.md b/docs/concepts/hot-reload.md index 9382d9c..41ac955 100644 --- a/docs/concepts/hot-reload.md +++ b/docs/concepts/hot-reload.md @@ -13,5 +13,5 @@ TODO - data reload (filesystem.md) - HTML templates implications - link to tailwind (for shim trick) -- exceptions +- exception handling in browser view - can run in production? \ No newline at end of file diff --git a/src/Ema/Server.hs b/src/Ema/Server.hs index a2cee2a..5534117 100644 --- a/src/Ema/Server.hs +++ b/src/Ema/Server.hs @@ -5,11 +5,12 @@ module Ema.Server where import Control.Concurrent.Async (race) -import Control.Exception (try) +import Control.Exception (catch, try) import Data.LVar (LVar) import qualified Data.LVar as LVar import qualified Data.Text as T import Ema.Class (Ema (decodeRoute)) +import GHC.IO.Unsafe (unsafePerformIO) import NeatInterpolation (text) import qualified Network.HTTP.Types as H import qualified Network.Wai as Wai @@ -83,14 +84,21 @@ runServerWithWebSocketHotReload port model render = do pure (H.status404, "No route") Just r -> do val <- LVar.get model - pure (H.status200, renderWithEmaShims val r) + let html = renderCatchingErrors val r + pure (H.status200, html <> emaStatusHtml <> wsClientShim) f $ Wai.responseLBS status [(H.hContentType, "text/html")] v - renderWithEmaShims m r = - render m r <> emaStatusHtml <> wsClientShim renderWithEmaHtmlShims m r = - render m r <> emaStatusHtml + renderCatchingErrors m r <> emaStatusHtml + renderCatchingErrors m r = + unsafeCatch (render m r) $ \(err :: SomeException) -> + encodeUtf8 $ + "
" + <> show @Text err + <> "
Once you fix your code this page will automatically update." routeFromPathInfo = decodeRoute @model . fmap (fromString . toString) + unsafeCatch :: Exception e => a -> (e -> a) -> a + unsafeCatch x f = unsafePerformIO $ catch (seq x $ pure x) (pure . f) -- | Return the equivalent of WAI's @pathInfo@, from the raw path string -- (`document.location.pathname`) the browser sends us.