Skip to content

Commit

Permalink
feat: add primer-miso, a re-implementation of Primer's UI in Haskell
Browse files Browse the repository at this point in the history
WIP.

Signed-off-by: Drew Hess <[email protected]>
  • Loading branch information
dhess committed Dec 12, 2024
1 parent 652ae24 commit c68cce8
Show file tree
Hide file tree
Showing 17 changed files with 1,817 additions and 3 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package-targets = update-tests
$(package-targets):
$(MAKE) -C primer $@
$(MAKE) -C primer-api $@
$(MAKE) -C primer-miso $@
$(MAKE) -C primer-selda $@
$(MAKE) -C primer-service $@
$(MAKE) -C primer-benchmark $@
Expand Down
10 changes: 10 additions & 0 deletions Makefile.wasm32
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ $(test-targets):
update:
$(CABAL) update

frontend:
$(MAKE) -C primer-miso frontend

frontend-prod:
$(MAKE) -C primer-miso frontend-prod

serve-frontend:
$(MAKE) -C primer-miso serve-frontend

clean:
$(CABAL) clean
$(MAKE) -C primer-miso clean

.PHONY: build configure $(test-targets) update clean
10 changes: 10 additions & 0 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ if arch(wasm32)
packages:
primer
primer-api
primer-miso
else
packages:
primer
primer-api
primer-miso
primer-selda
primer-service
primer-benchmark
Expand Down Expand Up @@ -69,6 +71,14 @@ source-repository-package
subdir: selda selda-sqlite
--sha256: 0fw336sb03sc54pzmkz6jn989zvbnwnzypb9n0ackprymnvh8mym

-- Until a new Hackage release is made which includes
-- https://github.com/dmjio/miso/pull/752
source-repository-package
type: git
location: https://github.com/dmjio/miso
tag: 2b548d48bffb0e8ae28a6cfb886e4afb0d8be37a
--sha256: sha256-fzDEa8vKXgxPPB+8NDLmhn2Jw1UDZso7e/klwidCfhM=

-- Wasm workarounds.
--
-- We would prefer that these workarounds were not Wasm-dependent;
Expand Down
6 changes: 3 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,11 @@
wasm = pkgs.mkShell {
packages = with inputs.ghc-wasm.packages.${system};
[
wasm32-wasi-ghc-9_10
wasm32-wasi-cabal-9_10
wasmtime
all_9_10

pkgs.gnumake
pkgs.simple-http-server
pkgs.brotli

# We need to run native `tasty-discover` at compile
# time, because we can't do it via `wasmtime`.
Expand Down
2 changes: 2 additions & 0 deletions primer-miso/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
ghc_wasm_jsffi.js
661 changes: 661 additions & 0 deletions primer-miso/COPYING

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions primer-miso/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# NOTE:
#
# This Makefile assumes you're using the `nix develop` shell.

include ../makefiles/common.mk
26 changes: 26 additions & 0 deletions primer-miso/Makefile.wasm32
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# NOTE:
#
# This Makefile assumes you're using the `nix develop .#wasm` shell.

CABAL = wasm32-wasi-cabal

build: configure
$(CABAL) build

frontend: build
./build-frontend.sh

frontend-prod: build
./build-frontend.sh -Oz

serve-frontend: frontend
simple-http-server -i -p 8000 -- dist

configure:
$(CABAL) configure

clean:
$(CABAL) clean
rm -rf dist/

.PHONY: build configure clean
39 changes: 39 additions & 0 deletions primer-miso/build-frontend.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash

set -e

if [ $# -eq 0 ]; then
echo "Building for dev"
dev_mode=true
else
echo "Building for prod"
dev_mode=false
fi

rm -rf dist
mkdir dist
cp frontend/*.html dist/

hs_wasm_path=$(wasm32-wasi-cabal list-bin -v0 exe:primer-miso)

ghc_wasm_jsffi="dist/ghc_wasm_jsffi.js"

"$(wasm32-wasi-ghc --print-libdir)"/post-link.mjs \
--input "$hs_wasm_path" --output "$ghc_wasm_jsffi"

if ! [ -f "$ghc_wasm_jsffi" ] ; then
echo "post-link.mjs didn't produce a $ghc_wasm_jsffi file. Make sure you're in the Nix Wasm shell."
exit 1
fi

if $dev_mode; then
cp "$hs_wasm_path" dist/bin.wasm
else
wizer --allow-wasi --wasm-bulk-memory true --init-func _initialize -o dist/bin.wasm "$hs_wasm_path"
wasm-opt ${1+"$@"} dist/bin.wasm -o dist/bin.wasm
wasm-tools strip -o dist/bin.wasm dist/bin.wasm
brotli --rm --best dist/bin.wasm -o dist/bin.wasm.br
mv dist/bin.wasm.br dist/bin.wasm
fi

cp frontend/*.js dist
32 changes: 32 additions & 0 deletions primer-miso/exe/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{-# LANGUAGE CPP #-}

#ifdef wasi_HOST_OS

module MyMain (main) where

import Foreword

--import GHC.Wasm.Prim
import Language.Javascript.JSaddle.Wasm qualified as JSaddle.Wasm
import Primer.Miso (start)

foreign export javascript "hs_start" main :: IO ()

main :: IO ()
main = JSaddle.Wasm.run start

#else

module Main (main) where

import Foreword

import Language.Javascript.JSaddle.Warp
import Primer.Miso (start)

-- Note that `debug` works with `cabal repl` but not `cabal run`.
-- The best workflow is to run `ghcid -c "cabal repl primer-miso" -W -T ':main'`.
main :: IO ()
main = debug 8000 start

#endif
14 changes: 14 additions & 0 deletions primer-miso/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Primer</title>
</head>

<body>
<script src="index.js" type="module"></script>
</body>

</html>
22 changes: 22 additions & 0 deletions primer-miso/frontend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { WASI, OpenFile, File, ConsoleStdout } from "https://cdn.jsdelivr.net/npm/@bjorn3/[email protected]/dist/index.js";
import ghc_wasm_jsffi from "./ghc_wasm_jsffi.js";

const args = [];
const env = [];
const fds = [
new OpenFile(new File([])), // stdin
ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)),
ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)),
];
const options = { debug: false };
const wasi = new WASI(args, env, fds, options);

const instance_exports = {};
const { instance } = await WebAssembly.instantiateStreaming(fetch("bin.wasm"), {
wasi_snapshot_preview1: wasi.wasiImport,
ghc_wasm_jsffi: ghc_wasm_jsffi(instance_exports),
});
Object.assign(instance_exports, instance.exports);

wasi.initialize(instance);
await instance.exports.hs_start();
107 changes: 107 additions & 0 deletions primer-miso/primer-miso.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
cabal-version: 3.0
name: primer-miso
version: 0.8.0.0
license: AGPL-3.0-or-later
license-file: COPYING
copyright: (c) 2024 Hackworth Ltd
maintainer: [email protected]
author: Hackworth Ltd <[email protected]>
stability: experimental
synopsis:
A web frontend for building Primer programs, using the Miso framework

category: UI

library
exposed-modules:
Primer.Miso
Primer.Miso.Colors
Primer.Miso.Layout
Primer.Miso.Util

hs-source-dirs: src
default-language: GHC2021
default-extensions:
DataKinds
DeriveAnyClass
DerivingStrategies
DerivingVia
LambdaCase
NoImplicitPrelude
OverloadedStrings

ghc-options:
-Wall -Wincomplete-uni-patterns -Wincomplete-record-updates
-Wcompat -Widentities -Wredundant-constraints
-Wmissing-deriving-strategies -fhide-source-paths

build-depends:
, aeson >=2.0 && <2.3
, base >=4.12 && <4.21
, bytestring >=0.10.8.2 && <0.13
, containers >=0.6.0.1 && <0.7.0
, data-default ^>=0.8.0.0
, deriving-aeson >=0.2 && <0.3.0
, extra >=1.7.10 && <1.8.0
, jsaddle ^>=0.9.9.2
, linear ^>=1.23
, miso ^>=1.8.5.0
, mtl >=2.2.2 && <2.4.0
, optics >=0.4 && <0.5.0
, primer ^>=0.7.2
, text >=2.0 && <2.2
, uniplate >=1.6 && <1.7.0

if arch(wasm32)
build-depends: jsaddle-wasm ^>=0.0.1.0

else
build-depends:
, jsaddle-warp ^>=0.9.9.2
, warp >=3.3 && <3.5
, websockets ^>=0.13.0.0

executable primer-miso
main-is: Main.hs
hs-source-dirs: exe
default-language: GHC2021
default-extensions:
DataKinds
DeriveAnyClass
DerivingStrategies
DerivingVia
LambdaCase
NoImplicitPrelude
OverloadedStrings

ghc-options:
-Wall -Wincomplete-uni-patterns -Wincomplete-record-updates
-Wcompat -Widentities -Wredundant-constraints
-Wmissing-deriving-strategies -fhide-source-paths

build-depends:
, base
, jsaddle
, primer
, primer-miso

if arch(wasm32)
build-depends:
, ghc-experimental ^>=0.1.0.0
, jsaddle-wasm

ghc-options:
-no-hs-main -optl-mexec-model=reactor "-optl-Wl,--export=hs_start"

else
build-depends:
, jsaddle-warp
, warp
, websockets

ghc-options: -threaded -rtsopts -with-rtsopts=-N

extra-source-files:
build-frontend.sh
frontend/**/*.html
frontend/**/*.js
Loading

0 comments on commit c68cce8

Please sign in to comment.