Home

Awesome

Miso Isomorphic Example using Stack

💡 NOTE: Since stack has dropped support for GHCJS, I would recommend checking out the Nix-based isomorphic Miso example for an updated example.

This is an expansion of the SPA isomorphic example, which is a minimal example of Miso's isomorphic features.

Get Started

$ ./stack-build.sh # or ./stack-build-docker.sh
$ (cd result && bin/server)

That's all you need to do. The rest of this README is for a deeper dive into the development setup, code structure and more. If you don't want to spend time building GHCJS, you can use ./stack-build-docker.sh instead.

<a href="https://user-images.githubusercontent.com/1189998/38898963-c7f68ecc-4296-11e8-86d7-e1cf740fe101.png" target="_blank"> <img width="1792" alt="The backend code (left), shared code (middle) and frontend code (right)" title="The backend code (left), shared code (middle) and frontend code (right)" src="https://user-images.githubusercontent.com/1189998/38898963-c7f68ecc-4296-11e8-86d7-e1cf740fe101.png"> </a> <!-- TOC --> <!-- /TOC -->

Motivation

This example focuses on:

Additionally it sets up a nice development environment for VSCode (although it works fine without):

For more in-depth information, I recommend checking out the Miso isomorphic example, which links to some great resources.

Running the example

Using stack,

$ ./stack-build.sh
$ (cd result && bin/server)

NOTE: Same as the original isomorphic example, the it expects you start the server standing in result/, so it knows where to find the static files, in result/static/.

If using VSCode and you want to use the phoityne debugger,

$ (cd backend && stack build phoityne-vscode) \
  && (cd common && stack build phoityne-vscode)

Additionally, you might want fswatch, for the runner.sh/rebuild.sh scripts,

$ brew install fswatch # alternatively, sudo apt-get install fswatch

Code Structure

Each folder has a stack.yaml, which tells stack how to build the code. For convenience, you can build the whole project by using the stack-build.sh script, which also copies the files into the result directory.

As mentioned, the executable and JavaScript goes into the result/ directory, with the JS in result/static/all.js and the exec in result/bin/server.

Frontend

The JavaScript gets generated from the GHCJS frontend:

FilenameResponsibilityBuild Type
src/Main.hsThe Miso frontend application, which initialises and runs the appExecutable

Backend

The Servant backend application, which takes care of routing and serving resources, is located in the backend:

FilenameResponsibilityBuild Type
bin/Main.hsSets up the WAI/Warp server that runs the servant backendExecutable
src/App.hsThe Servant backend application, which takes care of routing and serving resourcesLibrary
test/Main.hsConfigures the hspec test environemntTest
test/Spec.hsAutomatically finds all test files that end on Spec.hsTest
test/Backend/AppSpec.hsTests for src/App.hs (the default setup just checks that the endpoints return status code 200)Test

Common

Finally, the main bulk of the application is found in common/src/Common/. In this module we have:

FilenameResponsibilityBuild Type
src/Common/Model.hsContains the Model, initialModel and update ActionsLibrary
src/Common/Routes.hsSets up the links and servant routing treesLibrary
src/Common/View.hsContains all the views, and a view router (viewModel), which takes care of displaying the correct view, or a page404ViewLibrary

Project

The project folder is simply a way to get the root files into the workspace, without putting the whole root folder in the workspace. You can cd project and then ln -s ../originalFile lnFile to get your files in here.

Development Environment

To open the project, open the Miso-Project.code-workspace, which is a VSCode Workspace. This will open the three folders, backend, common and frontend as folders in the workspace, and their individual settings will then take effect (this uses VSCodes multi-root workspaces).

NOTE: Currently the focus has been on getting a nice development environment set up for VSCode out-the-box, but there is nothing stopping you from using this with other editors, since it simply uses HIE and stack for everything.

Backend and Common

The backend, along common, can be developed with HIE, by placing a stack.yaml in their respective folders. The backend needs this, but for common we have only put one there to make HIE work nicely with it.

Nothing needs to be done to setup these, as long as you opened the project via Miso-Project.code-workspace and installed the recommend extensions it lists.

Frontend

For the frontend (GHCJS) I haven't find anything really satisfying yet, so I'm settling for running stack build on file changes, with the following,

$ stack --stack-yaml=frontend/stack.yaml build --fast --file-watch

Or run the VSCode task Watch Test Frontend (press F6). You can set this up in an external terminal, or just in VSCode's integrated terminal.

Tasks

Instead of remembering how to build and run and how to build/watch the frontend, some default tasks are set up.

You often just want to run Rebuild/Copy/Launch Everything!, which will start three separate tasks, so you don't have to do anything.

NameCommandDescriptionKeybinding
Build Project./stack-build.shBuilds the whole project⌘ ⇧ b, F7 or F6 task menu
Build Project and Launch Server./stack-build.sh && cd result && bin/serverBuilds the whole project and launches the serverF6 task menu
Relaunch Server on Change./project/runner.sh bin/serverRelaunches the server, every time the result/bin/server executable changesF6 task menu
Copy Build Files on Change./project/rebuild.shCopies the build files to result/.. every time the stack build artifacts changeF6 task menu
Relaunch Server & Copy Build Files on Change./project/rebuild.sh & ./project/runner.sh bin/serverA combination of the relauncher and rebuilder (copier)F6 task menu
Build Backendstack --stack-yaml=backend/stack.yaml build --fastBuilds the backendF6 task menu
Build Frontendstack --stack-yaml=frontend/stack.yaml build --fastBuilds the frontendF6 task menu
Watch Test Backendstack --stack-yaml=backend/stack.yaml test --fast --haddock-deps --file-watchRuns tests for the backend on file changesF6 task menu
Watch Test Frontendstack --stack-yaml=frontend/stack.yaml test frontend --fast --haddock-deps --file-watchRuns tests for the frontend on file changesF6 task menu
Watch Build Frontend with Problem Matcherstack --stack-yaml=frontend/stack.yaml build frontend --fast --file-watchBuilds the frontend on file changes, and reports the errors at the file location they were foundF6 task menu
Watch Test Commonstack --stack-yaml=common/stack.yaml test --fast --haddock-deps --file-watchRuns tests for common on file changesF6 task menu
Test Backendstack --stack-yaml=backend/stack.yaml test --fastRuns tests for the backendF8 or F6 task menu
Test Frontendstack --stack-yaml=frontend/stack.yaml test frontend --fastRuns tests for the frontendF6 task menu
Test Commonstack --stack-yaml=common/stack.yaml test --fastRuns tests for the frontendF6 task menu

They all run in the correct directory. You can configure these in backend/.vscode/tasks.json, common/.vscode/tasks.json and frontend/.vscode/tasks.json.

Using Docker

If you don't want to waste time building GHCJS, then you can use the Dockerfile to build the frontend.

First build the image,

$ docker build -t ghcjs:lts-9.21 .
Sending build context to Docker daemon    626MB
Step 1/4 : FROM tehnix/ghcjs-docker:lts-9.21
...
 ---> 6ef295b59aaf
Successfully built 6ef295b59aaf

Next we need to copy the stack global root folder out into the project directory, so that we don't loose our build progress after each docker run (alternatively we would have to commit the file every time),

$ docker run -v $(pwd):/src -it ghcjs:lts-9.21 cp -R /root/.stack /src/.stack-docker

Then, to build the project, run,

$ docker run -v $(pwd):/src -it ghcjs:lts-9.21 stack --stack-root /src/.stack-docker --stack-yaml=frontend/stack.yaml build

And your project should build the frontend. This is all done automatically, if you use ./stack-build-docker.sh.

Without VSCode

There are four main pieces of this that I would recommend running in your terminal (each in their own),

$ stack --stack-yaml=backend/stack.yaml build --fast --file-watch
$ stack --stack-yaml=frontend/stack.yaml build --fast --file-watch
$ ./project/rebuild.sh & ./project/runner.sh bin/server

That should let you edit your files, and automatically build the backend/frontend, copy the files over and relaunch the server.

Miscellaneuous

Setting up HIE

The recommended way to setup HIE is to clone it down and then run make build-copy-compiler-tool,

$ git clone https://github.com/haskell/haskell-ide-engine.git \
  && cd haskell-ide-engine \
  && make build-copy-compiler-tool

This will, at the time of writing, install HIE for GHC 8.0.2, 8.2.1 and 8.2.2.

You can then find the locations with stack exec -- which hie, which will make sure to pick the right hie executable for your project (GHC versions need to match).

Using the Debugger

The documentation on phoityne is a bit sparse, but the setup here should work. It works by running backend/test/Spec.hs, and then follows through the code paths you activate here.

You can for example try and set a breakpoint on inside a view in common/src/Common/View.hs (e.g. inside homeView) and then press F5 to initialize the debugger and then press continue (or F5 again). You should now be at the breakpoint you just set.

Clean up File Tree Clutter

You can filter out files in the file tree that you rarely, if ever, access. Go into Settings -> Workspace Settings, and uncomment the lines you want/add new lines to the "files.exclude" object.

Default Enabled Extensions

By default a slew of extensions are enabled. This is mainly inspired by Alexis King's writeup An opinionated guide to Haskell in 2018, which makes good arguments for enabling these.

TODO

There's still some things that would be nice to have included here.