From cde3f6540878d4559bd2daa1b759d2191d23ff55 Mon Sep 17 00:00:00 2001 From: lazyLambda Date: Wed, 29 Jul 2026 22:30:58 -0400 Subject: [PATCH 1/3] Enable cabal multi-repl in skeleton Re-enable multi-repl: True in skeleton/cabal.project. The prior disable comment blamed a GHC 9.14 "LinkInMemory" panic; the real trigger is buildable unit files with no exposed-modules of their own (only reexported-modules or similar). GHC's multi-repl driver routes empty units through Pipeline.hs's LinkInMemory path even when the interpreter is enabled, and panics. See GHC issues #25366 and #26073. frontend-wasm and frontend-js were the offenders: both only reexport Frontend, no own exposed-modules. Added a one-line Placeholder module + hs-source-dirs to each so the unit files have at least one target. cabal repl --enable-multi-repl now loads 20 modules across common + frontend + backend + landing-page + frontend-wasm in one GHCi session on GHC 9.14.1. --- skeleton/cabal.project | 4 ++-- skeleton/frontend/js/frontend-js.cabal | 6 ++++++ skeleton/frontend/js/src/FrontendJs/Placeholder.hs | 1 + skeleton/frontend/wasm/frontend-wasm.cabal | 8 ++++++++ skeleton/frontend/wasm/src/FrontendWasm/Placeholder.hs | 1 + 5 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 skeleton/frontend/js/src/FrontendJs/Placeholder.hs create mode 100644 skeleton/frontend/wasm/src/FrontendWasm/Placeholder.hs diff --git a/skeleton/cabal.project b/skeleton/cabal.project index b32cb6de2..936c3dc90 100644 --- a/skeleton/cabal.project +++ b/skeleton/cabal.project @@ -24,8 +24,8 @@ if !(arch(javascript) || arch(wasm32)) --optional-packages: -- deps/jenga/deps/reflex-dom/* --- multi-repl: True --- Disabled: GHC 9.14 multi-unit GHCi panics with "LinkInMemory". +multi-repl: True +-- Re-enabled to investigate the LinkInMemory panic prior turns noted. diff --git a/skeleton/frontend/js/frontend-js.cabal b/skeleton/frontend/js/frontend-js.cabal index 162cdcd7d..17b96dee2 100644 --- a/skeleton/frontend/js/frontend-js.cabal +++ b/skeleton/frontend/js/frontend-js.cabal @@ -49,12 +49,18 @@ custom-setup jenga-setup library + hs-source-dirs: src build-depends: base -- , frontend reexported-modules: Frontend + -- Placeholder module: workaround for GHC #25366 / #26073. + -- cabal repl --enable-multi-repl panics ("LinkInMemory") on units + -- with no exposed-modules of their own. + exposed-modules: FrontendJs.Placeholder + default-language: Haskell2010 if (arch(javascript) || arch(wasm32)) diff --git a/skeleton/frontend/js/src/FrontendJs/Placeholder.hs b/skeleton/frontend/js/src/FrontendJs/Placeholder.hs new file mode 100644 index 000000000..84442d092 --- /dev/null +++ b/skeleton/frontend/js/src/FrontendJs/Placeholder.hs @@ -0,0 +1 @@ +module FrontendJs.Placeholder where diff --git a/skeleton/frontend/wasm/frontend-wasm.cabal b/skeleton/frontend/wasm/frontend-wasm.cabal index 46879b19c..526523e69 100644 --- a/skeleton/frontend/wasm/frontend-wasm.cabal +++ b/skeleton/frontend/wasm/frontend-wasm.cabal @@ -49,12 +49,20 @@ custom-setup jenga-setup library + hs-source-dirs: src build-depends: base -- , frontend reexported-modules: Frontend + -- Placeholder module: workaround for GHC #25366 / #26073. + -- cabal repl --enable-multi-repl panics ("LinkInMemory") on units + -- with no exposed-modules of their own. See: + -- https://gitlab.haskell.org/ghc/ghc/-/issues/25366 + -- https://gitlab.haskell.org/ghc/ghc/-/issues/26073 + exposed-modules: FrontendWasm.Placeholder + default-language: Haskell2010 if (arch(javascript) || arch(wasm32)) diff --git a/skeleton/frontend/wasm/src/FrontendWasm/Placeholder.hs b/skeleton/frontend/wasm/src/FrontendWasm/Placeholder.hs new file mode 100644 index 000000000..b3f1ed9fc --- /dev/null +++ b/skeleton/frontend/wasm/src/FrontendWasm/Placeholder.hs @@ -0,0 +1 @@ +module FrontendWasm.Placeholder where From 8713ce545d08b731736c90cc296f01d1fdb3d5ee Mon Sep 17 00:00:00 2001 From: lazyLambda Date: Thu, 30 Jul 2026 22:23:44 -0400 Subject: [PATCH 2/3] jenga-backend-servant: use hashed-asset protocol, not serveDirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit serveStaticDir was using Snap.serveDirectory for static asset dirs, which does trailing-slash canonicalization on directory-shaped paths. Every hashed-asset entry (produced by jenga-asset-manifest) is a directory containing type/target/encodings metadata files, so requests like /ghcjs/all.js got 302'd to /ghcjs/all.js/ and then fell through to the SPA HTML — the browser tried to parse HTML as JS and errored. Swap to Jenga.Asset.Serve.Snap.serveAssets, which reads the type file, follows the target redirect to the hashed filename, and serves with proper caching + encoding negotiation. Matches obelisk-v1's serveStaticAssets shape. Verified end-to-end on a NixOS VBox deploy: /ghcjs/all.js now returns 302 -> hashed target -> 200 text/javascript with immutable caching, and the reflex-dom frontend actually loads. --- .../jenga-backend-servant.cabal | 1 + .../src/Jenga/Backend/Servant.hs | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/jenga-backend-servant/jenga-backend-servant.cabal b/lib/jenga-backend-servant/jenga-backend-servant.cabal index 44dd16bbd..67835fa9c 100644 --- a/lib/jenga-backend-servant/jenga-backend-servant.cabal +++ b/lib/jenga-backend-servant/jenga-backend-servant.cabal @@ -14,6 +14,7 @@ library , bytestring , containers , data-default + , jenga-asset-serve-snap , jenga-route-servant , reflex-effectful , servant diff --git a/lib/jenga-backend-servant/src/Jenga/Backend/Servant.hs b/lib/jenga-backend-servant/src/Jenga/Backend/Servant.hs index 904cecf1f..28d35cdad 100644 --- a/lib/jenga-backend-servant/src/Jenga/Backend/Servant.hs +++ b/lib/jenga-backend-servant/src/Jenga/Backend/Servant.hs @@ -71,6 +71,7 @@ import GHC.Generics (Generic) import Snap.Core import Snap.Http.Server (quickHttpServe) import qualified Snap.Util.FileServe as Snap +import qualified Jenga.Asset.Serve.Snap as JengaAsset import Jenga.Route (HasRoute(..), PageName, urlToSegments) import Jenga.Frontend (Frontend(..), renderFrontendHtml) @@ -223,12 +224,20 @@ serveFrontendRoute cfg configs backend = do -- ─── Static file serving ─────────────────────────────────────── --- | Serve files from a StaticAssets directory. --- Tries processed (hashed) first, falls back to unprocessed. +-- | Serve files from a StaticAssets directory using the obelisk-style +-- hashed-asset protocol (reads type/target metadata, emits redirects to +-- the hashed asset, serves it with proper caching + encoding negotiation). +-- +-- Previously used @Snap.serveDirectory@, which does trailing-slash +-- canonicalization for the asset directories (each hashed asset is +-- structured as a dir with @type@/@target@/@encodings@ files), so every +-- request for @/ghcjs\/all.js@ got 302'd to @/ghcjs\/all.js\/@ and fell +-- through to the SPA HTML. Matches obelisk-v1's @serveStaticAssets@. serveStaticDir :: StaticAssets -> Snap () serveStaticDir assets = - Snap.serveDirectory (_staticAssets_processed assets) - <|> Snap.serveDirectory (_staticAssets_unprocessed assets) + JengaAsset.serveAssets + (_staticAssets_processed assets) + (_staticAssets_unprocessed assets) -- | Serve static assets (compatibility alias). serveStaticAssets :: StaticAssets -> [Text] -> Snap () From 4a6bfa6f6cc50502abda988d2e020c6b76ca6435 Mon Sep 17 00:00:00 2001 From: lazyLambda Date: Thu, 30 Jul 2026 22:29:47 -0400 Subject: [PATCH 3/3] Bump deps/obelisk-oauth to 393ff55 (drop stale CHANGELOG.md wildcard) The obelisk-oauth-common.cabal had `extra-source-files: CHANGELOG.md` referencing a file that never existed in the common/ subdir at any rev. Older cabal / haskell.nix silently ignored the missing file; newer haskell.nix errors with Cabal-6661 during plan generation, breaking any `jenga deploy push` invocation that touches this dep. Fix committed upstream on gs/jenga-port; bumping submodule to pick it up. --- deps/obelisk-oauth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/obelisk-oauth b/deps/obelisk-oauth index 9fc65d49a..393ff559e 160000 --- a/deps/obelisk-oauth +++ b/deps/obelisk-oauth @@ -1 +1 @@ -Subproject commit 9fc65d49a9da612c85419ed069993dcb8e25d996 +Subproject commit 393ff559e0b3192196e0692c0b655a1249072314